diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 084ac806ce..288472971e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -45,7 +45,7 @@ (several vendors do use same interface chips for unrelated protocols). - [ ] For new USB devices, built and committed the changes for the - `scripts/upower/95-upower-hid.rules` file + `scripts/upower/95-upower-hid.hwdb` file - [ ] Proposed NUT data mapping is aligned with existing `docs/nut-names.txt` file. If the device exposes useful data points not listed in the file, the diff --git a/.gitignore b/.gitignore index ff67167786..660f49f9dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ .libs/ .inst/ /tmp/ +/obj/ /_install_pkgprotodir/ Makefile Makefile.in @@ -63,3 +64,5 @@ __pycache__/ # Debuggers and IDEs .gdb_history /nbproject +/.idea +/*.iml diff --git a/.lgtm.yml b/.lgtm.yml index 868e230c49..1f5663223c 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -2,7 +2,7 @@ path_classifiers: template: - exclude: "**/*.py.in" - - exclude: "**/NUT-Monitor.in" + - exclude: "**/NUT-Monitor*.in" queries: - exclude: cpp/fixme-comment @@ -17,4 +17,4 @@ extraction: index: filters: - include: "**/*.py.in" - - include: "**/NUT-Monitor.in" + - include: "**/NUT-Monitor*.in" diff --git a/INSTALL.nut b/INSTALL.nut index 4708b56684..cd8ce6b860 100644 --- a/INSTALL.nut +++ b/INSTALL.nut @@ -1,11 +1,13 @@ Installation instructions ========================= -This chapter describe the various methods for installing Network UPS Tools. +This chapter describes the various methods for installing Network UPS Tools. Whenever it is possible, prefer <>. Packagers have done an excellent and hard work at improving NUT integration into -their system. +their system. On the other hand, distributions and appliances tend to package +"official releases" of projects such as NUT, and so do not deliver latest and +greatest fixes, new drivers, bugs and other features. [[Installing_source]] Installing from source @@ -16,6 +18,13 @@ These are the essential steps for compiling and installing this software. The NUT linkdoc:packager-guide[Packager Guide], which presents the best practices for installing and integrating NUT, is also a good reading. +The link:config-prereqs.txt[Prerequisites for building NUT on different OSes] +document suggests prerequisite packages with tools and dependencies +available and needed to build and test as much as possible of NUT on +numerous platforms, written from perspective of CI testing (if you +are interested in getting updated drivers for a particular device, +you might select a sub-set of those suggestions). + [NOTE] .Keep in mind that... ================================================================================ @@ -52,7 +61,7 @@ long as you are consistent. The process for doing this varies from one system to the next, and explaining how to add users is beyond the scope of this document. -For the purposes of this document, the user name and group name +For the purposes of this document, the user name and group name will be 'ups' and 'nut' respectively. Be sure the new user is a member of the new group! If you forget to @@ -87,7 +96,7 @@ docs/configure.txt or './configure --help' for all the available options. If you alter paths with additional switches, be sure to use those -new paths while reading the rest of the steps. +new paths while reading the rest of the steps. Reference: <> from the User Manual. @@ -100,7 +109,7 @@ Build the programs This will build the NUT client and server programs and the selected drivers. It will also build any other features that were -selected during <> step above. +selected during <> step above. Installation @@ -175,7 +184,7 @@ permissions for the USB device, you may need to set up (operating system dependent) hotplugging scripts. Sample scripts and information are provided in the scripts/hotplug and scripts/udev directories. For most users, the hotplugging scripts -will be installed automatically by "make install". +will be installed automatically by "make install". (If you want to try if a driver works without setting up hotplugging, you can add the "-u root" option to upsd, upsmon, and @@ -299,7 +308,7 @@ To install NUT as a package execute: Port ^^^^ -The port is located under +sysutils/nut+. +The port is located under +sysutils/nut+. Use +make config+ to select configuration options, e.g. to build the optional CGI scripts. To install it, use: diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index f254cfd736..d9ae6fe415 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -32,6 +32,9 @@ import org.nut.dynamatrix.*; dynacfgPipeline.axisCombos_COMPILER_GCC = [~/COMPILER=GCC/] dynacfgPipeline.axisCombos_COMPILER_NOT_GCC = [~/COMPILER=(?!GCC)/] + // Avoid requiring success on GCC so old we can't manage warnings by CLI or pragmas: + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD = [~/COMPILER=GCC/, ~/GCCVER=([0123]\.|4\.[0123])/] + // Beside the flag here, the pre-defined C89/C90/ANSI scenarios // should only get considered in branches named ~/fightwarn.*89.*/ // or PRs to those (for non-GCC builds): @@ -58,6 +61,14 @@ import org.nut.dynamatrix.*; dynacfgPipeline.failFast = //true // false + // How long can a single "slow-build stage" run before we + // consider that the build agent is stuck or network dropped? + // The dynamatrix should try to re-schedule this scenario then. + dynacfgPipeline.dsbcStageTimeoutSettings = [ + time: 2, + unit: 'HOURS' + ] + // Note: this setting causes a lot of noise in build summary page and // parent job definition (PR, branch...) overview page on Jenkins, // by reporting dozens of lines for each analyzer ID ever published. @@ -419,7 +430,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -618,7 +630,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -673,13 +686,14 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration - ,[name: 'A build with all docs types on capable systems, and a distcheck (must pass)', + ,[name: 'An out-of-tree build with all docs types on capable systems, and a distcheck (must pass)', // TODO: This is a recipe (and target OS) test for ability to build // the docs without error; it should not iterate compilers (maybe // iterate docs tools though, if we were to support many backends?) @@ -703,6 +717,9 @@ set | sort -n """ ], dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', + // Build in a subdirectory to check that out-of-dir + // builds are healthy too + 'CI_BUILDDIR=obj', 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=minimal' ] ], @@ -711,7 +728,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -749,7 +767,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -776,7 +795,7 @@ set | sort -n """ dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', - 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=auto' + 'BUILD_WARNFATAL=no','BUILD_WARNOPT=auto' ] ], allowedFailure: [ @@ -812,7 +831,7 @@ set | sort -n """ dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', - 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=auto' + 'BUILD_WARNFATAL=no','BUILD_WARNOPT=auto' ] ], allowedFailure: [ @@ -931,7 +950,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] //+ [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] ] ], body) }, // getParStages @@ -972,13 +992,14 @@ set | sort -n """ mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] - ] + ] + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration - ,[name: 'GNU C standard builds with fatal warnings with GCC, without distcheck and docs (must pass)', + ,[name: 'GNU C standard out-of-tree builds with fatal warnings with GCC, without distcheck and docs (must pass)', disabled: dynacfgPipeline.disableSlowBuildCIBuild, //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, @@ -997,6 +1018,9 @@ set | sort -n """ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', 'BUILD_WARNFATAL=yes', + // Build in a subdirectory to check that out-of-dir + // builds are healthy too + 'CI_BUILDDIR=obj', // NOTE: "gcc-hard" warnings are still not as picky // as "clang-hard" and do not differ much from the // "gcc-medium" definition currently: @@ -1010,7 +1034,8 @@ set | sort -n """ mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=(?!GCC)/] - ] + ] + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -1073,6 +1098,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1111,6 +1137,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1146,6 +1173,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1191,6 +1219,7 @@ set | sort -n """ excludeCombos: [ dynacfgPipeline.axisCombos_ARCH32x64, dynacfgPipeline.axisCombos_ARCH64x32, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, dynacfgPipeline.axisCombos_NOT_WINDOWS ] ], body) diff --git a/Makefile.am b/Makefile.am index 7a66ac7dfd..c48c1fb6d2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -194,8 +194,18 @@ spellcheck spellcheck-interactive: (cd $(builddir)/data && $(MAKE) -s $@) || RES=$$? ; \ exit $$RES -doc spellcheck-sortdict: - cd $(srcdir)/docs && $(MAKE) $@ +# Note: the "all-docs" and "check-docs" targets may require tools not +# found by `configure` script (and so avoided by conventional recipes) +# such as PDF generators, so it should only be called at developer's +# discretion, choice and risk. The "check-man" targets covers source +# texts, man pages and HTML rendering of man pages, as enabled by tools. +doc spellcheck-sortdict \ +all-docs check-docs \ +man all-man man-man check-man man-html all-html: + cd $(builddir)/docs && $(MAKE) $@ + +check-NIT check-NIT-devel: + cd $(builddir)/tests/NIT && $(MAKE) $@ # This target adds syntax-checking for committed shell script files, # to avoid surprises and delays in finding fatal typos after packaging @@ -296,11 +306,15 @@ tools/gitlog2changelog.py: tools/gitlog2changelog.py.in # ---------------------------------------------------------------------- # Maintainers targets: distribution signature and hashes -dist-sig: +nut-@PACKAGE_VERSION@.tar.gz: dist +nut-@PACKAGE_VERSION@.tar.gz.sig: dist-sig +nut-@PACKAGE_VERSION@.tar.gz.md5 nut-@PACKAGE_VERSION@.tar.gz.sha256: dist-hash + +dist-sig: nut-@PACKAGE_VERSION@.tar.gz rm -f nut-@PACKAGE_VERSION@.tar.gz.sig gpg --detach-sign nut-@PACKAGE_VERSION@.tar.gz -dist-hash: +dist-hash: nut-@PACKAGE_VERSION@.tar.gz md5sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.md5 sha256sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.sha256 @@ -376,7 +390,7 @@ MAINTAINERCLEANFILES_PACKAGES += *.p5p MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_DISTBALL) MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_PACKAGES) -package: +package: dist DESTDIR="$(abs_builddir)/_install_pkgprotodir" ; export DESTDIR; \ rm -rf "$$DESTDIR"; \ case "`uname -s`" in \ diff --git a/NEWS b/NEWS index d7aad9d3b6..e77477e5d7 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,15 @@ ChangeLog file (generated for release archives), or to the Git version control history for "live" codebase. --------------------------------------------------------------------------- -Release notes for NUT 2.7.5 - what's new since 2.7.4: +Release notes for NUT 2.8.0 - what's new since 2.7.4: + +NOTE: Earlier discussions (mailing list threads, GitHub issues, etc.) could +refer to this change set (too long in the making) as NUT 2.7.5. + + - New (optional) keywords for configuration files were added, + so existing NUT 2.7.x builds would not accept them if some + deployments switch versions back and forth -- due to this, + semantically the version was bumped to NUT 2.8.x. - Add support for openssl-1.1.0 (Arjen de Korte) @@ -22,12 +30,34 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: server initially without any device configurations and reloading it later to apply config changes on the fly [PR #766] + - Add support for `debug_min=NUM` setting (ups.conf, upsd.conf, upsmon.conf) + to specify the minimum debug verbosity for daemons. This allows "in-vivo" + troubleshooting of service daemons without editing init scripts or service + unit definitions. + - Improve support for upsdrvctl for managing of numerous device configs, including default "maxretry=3" and a "nowait" option to complete the "start of everything" mode after triggering the drivers and not waiting for them to complete initializing. This matters on systems that monitor from dozens to hundreds of devices. + - Drivers support a new value for `synchronous` setting, which is the + new default now: `auto`. Initially after driver start-up this mode + acts as the older default `off`, but would fall back to `on` in case + the driver fails to send reports to `upsd` by overflowing the socket + buffer in async mode -- so the next connections of this driver uptime + would be synchronized (potentially slower, but safer -- blocking on + writes to the data server). This adaptation would primarily impact + and benefit devices with many (hundreds of) data points, such as + ePDUs and daisy chains. [issue #1309, PR #1315] + + - Daemons such as upsd, upsmon, upslog, and device drivers previously + implied that enabled debugging (or upslog to stdout) means foreground + running, otherwise the daemon was always sent to the background. + Now there are explicit options for this (`-F`/`-B`), although default + behavior is retained. This change is used for simplified service unit + definitions. + - Improvements for device discovery or driver "lock-picking", including general support for: * "Standalone" mode (`-s` option), to monitor a device which is not @@ -46,6 +76,9 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: * Emerson Avocent PM3000 PDU (SNMP) * HPE ePDU (SNMP) + - nutdrv_qx: enhanced estimation of remaining battery runtime based + on speed of voltage drop, which varies as they age [PR #1027] + - nutdrv_qx: several subdrivers added or improved, including: * "snr" subdriver with USB connection, for SNR-UPS-LID-XXXX [PR #1008]. Note that end-users should reference explicitly the `snr` subdriver @@ -78,6 +111,14 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: RS-232 Modbus device support of Huawei UPS2000/2000A (1kVA-3kVA) series, and possibly some related FSP UPS models. [PR #954] + - socomec_jbus: added new driver for modbus-based JBUS protocol over serial + RS-232 for Socomec UPS (tested with a DIGYS 3/3 15kVA model, working + on Linux x86-64 and Raspberry Pi 3 ARM). [PR #1313] + + - adelsystem_cbi: added new driver for ADELSYSTEM CBI2801224A, an all-in-one + 12/24Vdc DC-UPS, which supports the modbus RTU communication protocol + [PR #1282] + - generic_modbus: added new driver for TCP and Serial Modbus device support. The driver has been tested against PULS UPS (model UB40.241) via MOXA ioLogikR1212 (RS485) and ioLogikE1212 (TCP/IP), and configuration @@ -114,6 +155,12 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: - Added Russian translation for NUT-Monitor GUI client [PR #806] + - Separated NUT-Monitor UI into two applications, NUT-Monitor-py2gtk2 and + NUT-Monitor-py3qt5, suitable for two generations of Python ecosystem + with their great differences; `NUT-Monitor` name is retained for wrapper + script which calls one of these, such that the current system can execute + [PRs #1310, #1354] + - Various USB driver families: expanded device-matching with "device" in addition to "bus" and generic USB fields. This is needed to support multiple attached devices that seem identical by other fields (e.g. @@ -131,9 +178,18 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: subdriver rather than polluting the main code with UPS specific exceptions, and applied fixes for known mistakes in (some releases of firmware for) CyberPower CPS*EPFCLCD [issue #439, PR #1245] + * added `onlinedischarge` option for UPSes that report `OL+DISCHRG` + when wall power is lost [PR #811] + * changed detection of VendorID 0x06da handling of which is claimed + by Liebert/Phoenixtec HID historically, and MGE HID (for AEG PROTECT + NAS UPSes) since NUT 2.7.4, so that the higher-priority MGE subdriver + would not grab each and all of the devices exposing that ID [PR #1357] * CPS HID: add input.frequency and output.frequency * OpenUPS2: only check OEM Information string once (fewer log messages) * Liebert GXT4 USB VID:PID [10AF:0000] + * add battery voltage and input/output transfer voltage and frequency + in Liebert/Phoenixtec HID mapping, to support PowerWalker VFI 2000 TGS + better [PR #564, issue #560] * add a little delay between multicommands [PR #1228] * fix Eaton/MGE mapping for beeper handling * add IBM USB VID @@ -151,6 +207,7 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: [PR #1199, issue #732] * add new ever-hid subdriver to support EVER UPS devices (Sinline RT Series, Sinline RT XL Series, ECO PRO AVR CDS Series) [PR #431] + * add ability to set `battery.mfr.date` for APC HID UPS [PR #1318] - usbhid-ups / mge-shut: compute a realpower output load approximation for Eaton UPS when the needed data is not present @@ -210,6 +267,8 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: and in particular to manage power separately on one or two outlet groups [PR #1048] + - tripplite_usb: updated to recognize the "3005" protocol [PR #584] + - libnutclient: introduce getDevicesVariableValues() to improve performances when querying many devices (up to 15 times faster) @@ -232,6 +291,10 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: - upsrw: display the variable type beside ENUM / RANGE + - Added `PROTVER` as alias to `NETVER` to report the protocol version in use. + Note that NUT codebase itself does not use this value and handles commands + and reported errors individually [issue #1347] + - Implement status tracking for instant commands (instcmd) and variables settings (setvar): this allows to get the actual execution status from the driver, and is available in libraries and upscmd / upsrw [PR #659] @@ -239,6 +302,10 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: - Add support for extra parameter for instant commands, both in library and in upscmd + - dummy-ups can now specify `mode` as a driver argument, and separates the + notion of `dummy-once` (new default for `*.dev` files that do not change) + vs. `dummy-loop` (legacy default for `*.seq` and others) [issue #1385] + - new protocol variables: * `input.phase.shift` * `outlet.N.name` @@ -263,11 +330,25 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: ISO 8601 Calendar date "YYYY-MM-DD" was added to snmp-ups.c [PR #1078] - Master/Slave terminology was deprecated in favor of Primary/Secondary - modes of upsmon client. Respective keywords in the configuration are - supported as backwards-compatible settings, but obsoleted values are - no longer documented. [For details see issue #840 and several pull - requests referenced from it, and discussions on NUT mailing lists] - * Note: protocol keyword support currently was not updated + modes of `upsmon` client: + * Respective keywords in the configuration files (`upsd.users` and + `upsmon.conf`) are supported as backwards-compatible settings, + but the obsoleted values are no longer documented. + * Protocol keyword support was similarly updated, with `upsmon` now + first trying to elevate privileges with `PRIMARY ` request, + and falling back to `MASTER ` just in case it talks to an + older build of an `upsd` server. + * For the principle of least surprise, NUT codebase still exposes the + `net_master()` (as handler for `MASTER` net command) in header and + C code for the sake of existing linked binaries, and returns the + `OK MASTER-GRANTED` line to the older client that invoked it. + * Newly introduced `net_primary()` (as handler for `PRIMARY` net command) + calls the exact same application logic, but returns `OK PRIMARY-GRANTED` + line to the client. + * Python binding updated to handle both cases, as the only found in-tree + protocol consumer of the full-line text. + * For more details see issue #840 and several pull requests referenced + from it, and discussions on NUT mailing lists. - Build fixes: * In general, numerous fixes were applied to ensure portability and avoid @@ -323,7 +404,7 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: few changes also happened in header files installed for builds configured `--with-dev` and so may impact `upsclient` and `nutclient` (C++) consumers. At the very least, binaries for those consumers should be rebuilt to remain - stable with NUT 2.7.5 and not mismatch int-type sizes and other arguments. + stable with NUT 2.8.0 and not mismatch int-type sizes and other arguments. - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. diff --git a/README b/README index 9fe1c00fc5..c57dd89e97 100644 --- a/README +++ b/README @@ -102,7 +102,7 @@ This package is broken down into several categories: - *clients* - They talk to upsd and do things with the status data. - *cgi-bin* - Special class of clients that you can use with your web server. - *scripts* - Contains various scripts, like the Perl and Python binding, -integration bits and applications. +integration bits and applications. Drivers ------- @@ -249,7 +249,7 @@ Power distribution unit management NUT also provides an advanced support for power distribution units. You should read the <> -chapter to learn more about when to use this feature. +chapter to learn more about when to use this feature. Network Server -------------- @@ -508,7 +508,11 @@ The past stable trees were 1.0, 1.2, 1.4, 2.0, 2.2 and 2.4, with the latest stable tree designated 2.6. The development trees were 1.1, 1.3, 1.5, 2.1 and 2.3. As of the 2.4 release, there is no real development branch anymore since the code is available through a revision control -system (namely Subversion) and snapshots. +system (namely Subversion) and snapshots. Since 2.7 line of releases, +sources are tracked in Git revision control system, with the project +ecosystem being hosted on GitHub, and improvements or other contributions +merged through common pull request approach and custom NUT CI testing +on multiple platforms. Major release jumps are mostly due to large changes to the features list. There have also been a number of architectural changes which @@ -518,11 +522,11 @@ may not be noticeable to most users, but which can impact developers. Backwards and Forwards Compatibility ------------------------------------ -The old network code spans a range from about 0.41.1 when TCP support +The old network code spans a range from about 0.41.1 when TCP support was introduced up to the recent 1.4 series. It used variable names like STATUS, UTILITY, and LOADPCT. Many of these names go back to the earliest prototypes of this software from 1997. At that point there -was no way to know that so many drivers would come along and introduce +was no way to know that so many drivers would come along and introduce so many new variables and commands. The resulting mess grew out of control over the years. @@ -554,9 +558,9 @@ Here's a table to make it easier to visualize: |============================================= Version 2.0, and more recent, do not contain backwards compatibility for -the old protocol and variable/command names. As a result, 2.0 clients can't -talk to anything older than a 1.4 server. If you ask a 2.0 client to -fetch "STATUS", it will fail. You'll have to ask for "ups.status" +the old protocol and variable/command names. As a result, 2.0 clients can't +talk to anything older than a 1.4 server. If you ask a 2.0 client to +fetch "STATUS", it will fail. You'll have to ask for "ups.status" instead. Authors of separate monitoring programs should have used the 1.4 series diff --git a/TODO b/TODO index 20cbbded5b..81a601f2d6 100644 --- a/TODO +++ b/TODO @@ -13,7 +13,7 @@ Roadmap ^^^ This release is focused on the website and documentation rewrite, using -the excellent link:http://www.methods.co.nz/asciidoc[AsciiDoc]. +the excellent link:https://asciidoc.org/[AsciiDoc]. 2.8 ^^^ diff --git a/UPGRADING b/UPGRADING index ba120d90ef..4fafbe5dbf 100644 --- a/UPGRADING +++ b/UPGRADING @@ -7,7 +7,7 @@ This file lists changes that affect users who installed older versions of this software. When upgrading from an older version, be sure to check this file to see if you need to make changes to your system. -Changes from 2.7.4 to 2.7.5 +Changes from 2.7.4 to 2.8.0 --------------------------- - Note to distribution packagers: this version hopefully learns from many @@ -15,7 +15,11 @@ Changes from 2.7.4 to 2.7.5 remain, please consider making pull requests for upstream NUT codebase to share the fixes consistently across the ecosystem. Also note that some new types of drivers (so package groups with unique dependencies) - could have appeared since your packaging was written (e.g. with modbus). + could have appeared since your packaging was written (e.g. with modbus), + as well as new features in systemd integration (`nut-driver@instances` + and the `nut-driver-enumerator` to manage their population), as well as + updated Python 2 and Python 3 support (again, maybe dictating different + package groups) as detailed below. - Due to changes needed to resolve build warnings, mostly about mismatching data types for some variables, some structure definitions and API signatures @@ -25,7 +29,7 @@ Changes from 2.7.4 to 2.7.5 few changes also happened in header files installed for builds configured `--with-dev` and so may impact `upsclient` and `nutclient` (C++) consumers. At the very least, binaries for those consumers should be rebuilt to remain - stable with NUT 2.7.5 and not mismatch int-type sizes and other arguments. + stable with NUT 2.8.0 and not mismatch int-type sizes and other arguments. - libusb-1.0: NUT now defaults to building against libusb-1.0 API version if the configure script finds the development headers, falling back to @@ -46,10 +50,11 @@ Changes from 2.7.4 to 2.7.5 - oldmge-shut has been removed, and replaced by mge-shut. -- New drivers for devices with "Qx" family of protocols should be developed - as sub-drivers in the `nutdrv_qx` framework for USB and Serial connected - devices, not as updates/clones of older e.g. `blazer` family and `bestups`. - Sources of such older drivers were marked with "OBSOLETION WARNING". +- New drivers for devices with "Qx" (also known as "Megatec Q*") family of + protocols should be developed as sub-drivers in the `nutdrv_qx` framework + for USB and Serial connected devices, not as updates/clones of older e.g. + `blazer` family and `bestups`. Sources, man pages and start-up messages + of such older drivers were marked with "OBSOLETION WARNING". - liebert-esp2: some multi-phase variable names have been updated to match the rest of NUT. @@ -83,8 +88,33 @@ Changes from 2.7.4 to 2.7.5 search code in common/common.c. Please file an issue if this does not work with your platform. +- dummy-ups can now specify `mode` as a driver argument, and separates the + notion of `dummy-once` (new default for `*.dev` files that do not change) + vs. `dummy-loop` (legacy default for `*.seq` and others) [issue #1385] + + * Note this can break third-party test scripts which expected `*.dev` + files to work as a looping sequence with a `TIMER` keywords to change + values slowly; now such files should get processed to the end once. + Specify `mode=dummy-loop` driver option or rename the data file used + in the `port` option for legacy behavior. + Use/Test-cases which modified such files content externally should + not be impacted. + - Python: scripts have been updated to work with Python 3 as well as 2. + * PyNUT module (protocol binding) supports both Python generations. + + * NUT-Monitor (desktop UI client) got separated into two projects: + one with support for Python2 and GTK2, and another for Python3 and Qt5. + On operating systems that serve both environments, either of these + implementation should be usable. For distributions that deprecated + and removed Python2 support, it is a point to consider in NUT packages + and their build-time and installation dependencies. + The historic filenames for desktop integration (`NUT-Monitor` script + and `nut-monitor.desktop`) are still delivered, but now cover a wrapper + script which detects the environment capabilities and launches the best + suitable UI implementation (if both are available). + - apcsmart: updates to CS "hack" (see docs/man/apcsmart.txt for details) - upsdebugx(): added `[D#]` prefix to log entries with level > 0 diff --git a/autogen.sh b/autogen.sh index bcb2e915fd..20671a1bc2 100755 --- a/autogen.sh +++ b/autogen.sh @@ -15,7 +15,14 @@ if [ -n "${PYTHON-}" ] ; then } else PYTHON="" - for P in python python3 python2 ; do + for P in python python3 python2 \ + python-3.10 python3.10 \ + python-3.9 python3.9 \ + python-3.7 python3.7 \ + python-3.5 python3.5 \ + python-3.4 python3.4 \ + python-2.7 python2.7 \ + ; do if (command -v "$P" >/dev/null) && $P -c "import re,glob,codecs" ; then PYTHON="$P" break @@ -45,6 +52,7 @@ then touch scripts/augeas/nutupsconf.aug.in scripts/augeas/nutupsconf.aug.in.AUTOGEN_WITHOUT else echo "Aborting $0! To avoid this, please export WITHOUT_NUT_AUGEAS=true and re-run" >&2 + echo "or better yet, export PYTHON=python-x.y and re-run" >&2 exit 1 fi fi diff --git a/ci_build.sh b/ci_build.sh index bf95c42c93..3e419d1ea5 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -10,14 +10,45 @@ ################################################################################ set -e +SCRIPTDIR="`dirname "$0"`" +SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" # Quick hijack for interactive development like this: # BUILD_TYPE=fightwarn-clang ./ci_build.sh # or to quickly hit the first-found errors in a larger matrix # (and then easily `make` to iterate fixes), like this: # CI_REQUIRE_GOOD_GITIGNORE="false" CI_FAILFAST=true DO_CLEAN_CHECK=no BUILD_TYPE=fightwarn ./ci_build.sh +# +# For out-of-tree builds you can specify a CI_BUILDDIR (absolute or relative +# to SCRIPTDIR - not current path), or just call .../ci_build.sh while being +# in a different directory and then it would be used with a warning. This may +# require that you `make distclean` the original source checkout first: +# CI_BUILDDIR=obj BUILD_TYPE=default-all-errors ./ci_build.sh case "$BUILD_TYPE" in fightwarn) ;; # for default compiler + fightwarn-all) + # This recipe allows to test with different (default-named) + # compiler suites if available. Primary goal is to see whether + # everything is building ok on a given platform, with one shot. + TRIED_BUILD=false + if (command -v gcc) >/dev/null ; then + TRIED_BUILD=true + BUILD_TYPE=fightwarn-gcc "$0" || exit + else + echo "SKIPPING BUILD_TYPE=fightwarn-gcc: compiler not found" >&2 + fi + if (command -v clang) >/dev/null ; then + TRIED_BUILD=true + BUILD_TYPE=fightwarn-clang "$0" || exit + else + echo "SKIPPING BUILD_TYPE=fightwarn-clang: compiler not found" >&2 + fi + if ! $TRIED_BUILD ; then + echo "FAILED to run: no default-named compilers were found" >&2 + exit 1 + fi + exit 0 + ;; fightwarn-gcc) CC="gcc" CXX="g++" @@ -36,20 +67,26 @@ if [ "$BUILD_TYPE" = fightwarn ]; then # For CFLAGS/CXXFLAGS keep caller or compiler defaults # (including C/C++ revision) BUILD_TYPE=default-all-errors - BUILD_WARNFATAL=yes + #BUILD_WARNFATAL=yes + # configure => "yes" except for antique compilers + BUILD_WARNFATAL=auto - # Current fightwarn goal is to have no warnings at preset level below: + # Current fightwarn goal is to have no warnings at preset level below, + # or at the level defaulted with configure.ac (perhaps considering the + # compiler version, etc.): #[ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=hard - [ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=medium + #[ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=medium + # configure => default to medium, detect by compiler type + [ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=auto # Eventually this constraint would be removed to check all present # SSL implementations since their ifdef-driven codebases differ and # emit varied warnings. But so far would be nice to get the majority # of shared codebase clean first: - [ -n "$NUT_SSL_VARIANTS" ] || NUT_SSL_VARIANTS=auto + #[ -n "$NUT_SSL_VARIANTS" ] || NUT_SSL_VARIANTS=auto # Similarly for libusb implementations with varying support - [ -n "$NUT_USB_VARIANTS" ] || NUT_USB_VARIANTS=auto + #[ -n "$NUT_USB_VARIANTS" ] || NUT_USB_VARIANTS=auto fi # Set this to enable verbose profiling @@ -82,7 +119,36 @@ esac # (allowing to rebuild interactively and investigate that set-up)? [ -n "${CI_FAILFAST-}" ] || CI_FAILFAST=false -[ -n "$MAKE" ] || MAKE=make +# By default we configure and build in the same directory as source; +# and a `make distcheck` handles how we build from a tarball. +# However there are also cases where source is prepared (autogen) once, +# but is built in various directories with different configurations. +# This is something to test via CI, that recipes are not broken for +# such use-case. Note the path should be in .gitignore, e.g. equal to +# or under ./tmp/ or ./obj/ for the CI_REQUIRE_GOOD_GITIGNORE sanity +# checks to pass. +case "${CI_BUILDDIR-}" in + "") # Not set, likeliest case + CI_BUILDDIR="`pwd`" + if [ x"${SCRIPTDIR}" = x"${CI_BUILDDIR}" ] ; then + CI_BUILDDIR="." + else + echo "=== WARNING: This build will use '${CI_BUILDDIR}'" + echo "=== for an out-of-tree build of NUT with sources located" + echo "=== in '${SCRIPTDIR}'" + echo "=== PRESS CTRL+C NOW if you did not mean this! (Sleeping 5 sec)" + + sleep 5 + fi + ;; + ".") ;; # Is SCRIPTDIR, in-tree build + /*) ;; # Absolute path located somewhere else + *) # Non-trivial, relative to SCRIPTDIR, may not exist yet + CI_BUILDDIR="${SCRIPTDIR}/${CI_BUILDDIR}" + ;; +esac + +[ -n "$MAKE" ] || [ "$1" = spellcheck ] || MAKE=make [ -n "$GGREP" ] || GGREP=grep [ -n "$MAKE_FLAGS_QUIET" ] || MAKE_FLAGS_QUIET="VERBOSE=0 V=0 -s" @@ -202,15 +268,31 @@ done if [ -z "$CI_OS_NAME" ]; then # Check for dynaMatrix node labels support and map into a simple # classification styled after (compatible with) that in Travis CI - for CI_OS_HINT in "$OS_FAMILY-$OS_DISTRO" "`uname -o`" "`uname -s -r -v`" "`uname -a`" ; do + for CI_OS_HINT in \ + "$OS_FAMILY-$OS_DISTRO" \ + "`grep = /etc/os-release 2>/dev/null`" \ + "`cat /etc/release 2>/dev/null`" \ + "`uname -o 2>/dev/null`" \ + "`uname -s -r -v 2>/dev/null`" \ + "`uname -a`" \ + "`uname`" \ + ; do [ -z "$CI_OS_HINT" -o "$CI_OS_HINT" = "-" ] || break done case "`echo "$CI_OS_HINT" | tr 'A-Z' 'a-z'`" in *freebsd*) CI_OS_NAME="freebsd" ;; - *debian*|*linux*) + *openbsd*) + CI_OS_NAME="openbsd" ;; + *netbsd*) + CI_OS_NAME="netbsd" ;; + *debian*|*ubuntu*) CI_OS_NAME="debian" ;; + *centos*|*fedora*|*redhat*|*rhel*) + CI_OS_NAME="centos" ;; + *linux*) + CI_OS_NAME="linux" ;; *windows*) CI_OS_NAME="windows" ;; *[Mm]ac*|*arwin*|*[Oo][Ss][Xx]*) @@ -252,11 +334,20 @@ if [ -z "${CANBUILD_LIBGD_CGI-}" ]; then # See also below for some compiler-dependent decisions fi +if [ -z "${PKG_CONFIG-}" ]; then + # Default to using one from PATH, if any - mostly for config tuning done + # below in this script + # DO NOT "export" it here so configure script can find one for the build + PKG_CONFIG="pkg-config" +fi + configure_nut() { - local CONFIGURE_SCRIPT=./configure + local CONFIGURE_SCRIPT="configure" + cd "$SCRIPTDIR" + if [[ "$CI_OS_NAME" == "windows" ]] ; then find . -ls - CONFIGURE_SCRIPT=./configure.bat + CONFIGURE_SCRIPT="configure.bat" fi if [ ! -s "$CONFIGURE_SCRIPT" ]; then @@ -272,6 +363,15 @@ configure_nut() { fi || exit fi + if [ "${CI_BUILDDIR}" != "." ]; then + # Per above, we always start this routine in absolute $SCRIPTDIR + echo "=== Running NUT build out-of-tree in ${CI_BUILDDIR}" + mkdir -p "${CI_BUILDDIR}" && cd "${CI_BUILDDIR}" || exit + CONFIGURE_SCRIPT="${SCRIPTDIR}/${CONFIGURE_SCRIPT}" + else + CONFIGURE_SCRIPT="./${CONFIGURE_SCRIPT}" + fi + # Help copy-pasting build setups from CI logs to terminal: local CONFIG_OPTS_STR="`for F in "${CONFIG_OPTS[@]}" ; do echo "'$F' " ; done`" ### | tr '\n' ' '`" while : ; do # Note the CI_SHELL_IS_FLAKY=true support below @@ -293,9 +393,9 @@ configure_nut() { # Real-life story from the trenches: there are weird systems # which fail ./configure in random spots not due to script's # quality. Then we'd just loop here. - echo "WOULD BE FATAL: FAILED ($RES_CFG) to ./configure ${CONFIG_OPTS[*]} -- but asked to loop trying" >&2 + echo "WOULD BE FATAL: FAILED ($RES_CFG) to $CONFIGURE_SCRIPT ${CONFIG_OPTS[*]} -- but asked to loop trying" >&2 else - echo "FATAL: FAILED ($RES_CFG) to ./configure ${CONFIG_OPTS[*]}" >&2 + echo "FATAL: FAILED ($RES_CFG) to $CONFIGURE_SCRIPT ${CONFIG_OPTS[*]}" >&2 echo "If you are sure this is not a fault of scripting or config option, try" >&2 echo " CI_SHELL_IS_FLAKY=true $0" exit $RES_CFG @@ -394,7 +494,7 @@ check_gitignore() { ; then echo "FATAL: There are changes in $FILE_DESCR files listed above - tracked sources should be updated in the PR (even if generated - not all builders can do so), and build products should be added to a .gitignore file, everything made should be cleaned and no tracked files should be removed!" >&2 if [ "$GIT_DIFF_SHOW" = true ]; then - git diff -- "${FILE_GLOB}" || true + PAGER=cat git diff -- "${FILE_GLOB}" || true fi echo "===" return 1 @@ -464,6 +564,16 @@ optional_dist_clean_check() { if [ "$1" = spellcheck ] && [ -z "$BUILD_TYPE" ] ; then # Note: this is a little hack to reduce typing # and scrolling in (docs) developer iterations. + if [ -z "${MAKE-}" ] ; then + if (command -v gmake) >/dev/null 2>/dev/null ; then + # GNU make processes quiet mode better, which helps with this use-case + MAKE=gmake + else + # Use system default, there should be one + MAKE=make + fi + export MAKE + fi if [ -s Makefile ] && [ -s docs/Makefile ]; then echo "Processing quick and quiet spellcheck with already existing recipe files, will only report errors if any ..." build_to_only_catch_errors_target spellcheck ; exit @@ -476,7 +586,7 @@ fi echo "Processing BUILD_TYPE='${BUILD_TYPE}' ..." echo "Build host settings:" -set | egrep '^(CI_.*|CANBUILD_.*|NODE_LABELS|MAKE|C.*FLAGS|LDFLAGS|CC|CXX|DO_.*|BUILD_.*)=' || true +set | egrep '^(CI_.*|OS_*|CANBUILD_.*|NODE_LABELS|MAKE|C.*FLAGS|LDFLAGS|CC|CXX|DO_.*|BUILD_.*)=' || true uname -a echo "LONG_BIT:`getconf LONG_BIT` WORD_BIT:`getconf WORD_BIT`" || true if command -v xxd >/dev/null ; then xxd -c 1 -l 6 | tail -1; else if command -v od >/dev/null; then od -N 1 -j 5 -b | head -1 ; else hexdump -s 5 -n 1 -C | head -1; fi; fi < /bin/ls 2>/dev/null | awk '($2 == 1){print "Endianness: LE"}; ($2 == 2){print "Endianness: BE"}' || true @@ -530,11 +640,25 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp EXTRA_CXXFLAGS="" is_gnucc() { - if [ -n "$1" ] && "$1" --version 2>&1 | grep 'Free Software Foundation' > /dev/null ; then true ; else false ; fi + if [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep 'Free Software Foundation' > /dev/null ; then true ; else false ; fi } is_clang() { - if [ -n "$1" ] && "$1" --version 2>&1 | grep 'clang version' > /dev/null ; then true ; else false ; fi + if [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep 'clang version' > /dev/null ; then true ; else false ; fi + } + + filter_version() { + # Starting with number like "6.0.0" or "7.5.0-il-0" is fair game, + # but a "gcc-4.4.4-il-4" (starting with "gcc") is not + sed -e 's,^.* \([0-9][0-9]*\.[0-9][^ ),]*\).*$,\1,' -e 's, .*$,,' | grep -E '^[0-9]' | head -1 + } + + ver_gnucc() { + [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep -i gcc | filter_version + } + + ver_clang() { + [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep -i 'clang' | filter_version } COMPILER_FAMILY="" @@ -547,6 +671,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp export CC CXX fi else + # Generally we prefer GCC unless it is very old so we can't impact + # its warnings and complaints. if is_gnucc "gcc" && is_gnucc "g++" ; then # Autoconf would pick this by default COMPILER_FAMILY="GCC" @@ -558,17 +684,45 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp [ -n "$CC" ] || CC=cc [ -n "$CXX" ] || CXX=c++ export CC CXX - elif is_clang "clang" && is_clang "clang++" ; then - # Autoconf would pick this by default - COMPILER_FAMILY="CLANG" - [ -n "$CC" ] || CC=clang - [ -n "$CXX" ] || CXX=clang++ - export CC CXX - elif is_clang "cc" && is_clang "c++" ; then - COMPILER_FAMILY="CLANG" - [ -n "$CC" ] || CC=cc - [ -n "$CXX" ] || CXX=c++ - export CC CXX + fi + + if ( [ "$COMPILER_FAMILY" = "GCC" ] && \ + case "`ver_gnucc "$CC"`" in + [123].*) true ;; + 4.[0123][.,-]*) true ;; + 4.[0123]) true ;; + *) false ;; + esac && \ + case "`ver_gnucc "$CXX"`" in + [123].*) true ;; + 4.[0123][.,-]*) true ;; + 4.[0123]) true ;; + *) false ;; + esac + ) ; then + echo "NOTE: default GCC here is very old, do we have a CLANG instead?.." >&2 + COMPILER_FAMILY="GCC_OLD" + fi + + if [ -z "$COMPILER_FAMILY" ] || [ "$COMPILER_FAMILY" = "GCC_OLD" ]; then + if is_clang "clang" && is_clang "clang++" ; then + # Autoconf would pick this by default + [ "$COMPILER_FAMILY" = "GCC_OLD" ] && CC="" && CXX="" + COMPILER_FAMILY="CLANG" + [ -n "$CC" ] || CC=clang + [ -n "$CXX" ] || CXX=clang++ + export CC CXX + elif is_clang "cc" && is_clang "c++" ; then + [ "$COMPILER_FAMILY" = "GCC_OLD" ] && CC="" && CXX="" + COMPILER_FAMILY="CLANG" + [ -n "$CC" ] || CC=cc + [ -n "$CXX" ] || CXX=c++ + export CC CXX + fi + fi + + if [ "$COMPILER_FAMILY" = "GCC_OLD" ]; then + COMPILER_FAMILY="GCC" fi fi @@ -653,6 +807,12 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("--with-devd-dir=${BUILD_PREFIX}/etc/devd") CONFIG_OPTS+=("--with-hotplug-dir=${BUILD_PREFIX}/etc/hotplug") + # TODO: Consider `--enable-maintainer-mode` to add recipes that + # would quickly regenerate Makefile(.in) if you edit Makefile.am + # TODO: Resolve port-collision reliably (for multi-executor agents) + # and enable the test for CI runs. Bonus for making it quieter. + CONFIG_OPTS+=("--enable-check-NIT=no") + if [ -n "${PYTHON-}" ]; then # WARNING: Watch out for whitespaces, not handled here! CONFIG_OPTS+=("--with-python=${PYTHON}") @@ -689,6 +849,15 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("--with-valgrind=no") fi + if [ -n "${CI_CROSSBUILD_TARGET-}" ] || [ -n "${CI_CROSSBUILD_HOST-}" ] ; then + # at least one is e.g. "arm-linux-gnueabihf" + [ -z "${CI_CROSSBUILD_TARGET-}" ] && CI_CROSSBUILD_TARGET="${CI_CROSSBUILD_HOST}" + [ -z "${CI_CROSSBUILD_HOST-}" ] && CI_CROSSBUILD_HOST="${CI_CROSSBUILD_TARGET}" + echo "NOTE: Cross-build was requested, passing options to configure this for target '${CI_CROSSBUILD_TARGET}' host '${CI_CROSSBUILD_HOST}' (note you may need customized PKG_CONFIG_PATH)" >&2 + CONFIG_OPTS+=("--host=${CI_CROSSBUILD_HOST}") + CONFIG_OPTS+=("--target=${CI_CROSSBUILD_TARGET}") + fi + # This flag is primarily linked with (lack of) docs generation enabled # (or not) in some BUILD_TYPE scenarios or workers. Initial value may # be set by caller, but codepaths below have the final word. @@ -761,7 +930,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ "${CANBUILD_LIBGD_CGI-}" != "no" ] && [ "${BUILD_LIBGD_CGI-}" != "auto" ] ; then # Currently --with-all implies this, but better be sure to # really build everything we can to be certain it builds: - if pkg-config --exists libgd || pkg-config --exists libgd2 || pkg-config --exists libgd3 || pkg-config --exists gdlib ; then + if $PKG_CONFIG --exists libgd || $PKG_CONFIG --exists libgd2 || $PKG_CONFIG --exists libgd3 || $PKG_CONFIG --exists gdlib || $PKG_CONFIG --exists gd ; then CONFIG_OPTS+=("--with-cgi=yes") else # Note: CI-wise, our goal IS to test as much as we can @@ -918,7 +1087,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp fi if [ "$NO_PKG_CONFIG" == "true" ] && [ "$CI_OS_NAME" = "linux" ] && (command -v dpkg) ; then - echo "NO_PKG_CONFIG==true : BUTCHER pkg-config for this test case" >&2 + # This should be done in scratch containers... + echo "NO_PKG_CONFIG==true : BUTCHER pkg-config package for this test case" >&2 sudo dpkg -r --force all pkg-config fi @@ -1009,15 +1179,15 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Technically, let caller provide this setting explicitly if [ -z "$NUT_SSL_VARIANTS" ] ; then NUT_SSL_VARIANTS="auto" - if pkg-config --exists nss && pkg-config --exists openssl && [ "${BUILD_SSL_ONCE-}" != "true" ] ; then + if $PKG_CONFIG --exists nss && $PKG_CONFIG --exists openssl && [ "${BUILD_SSL_ONCE-}" != "true" ] ; then # Try builds for both cases as they are ifdef-ed # TODO: Extend if we begin to care about different # major versions of openssl (with their APIs), etc. NUT_SSL_VARIANTS="openssl nss" else if [ "${BUILD_SSL_ONCE-}" != "true" ]; then - pkg-config --exists nss 2>/dev/null && NUT_SSL_VARIANTS="nss" - pkg-config --exists openssl 2>/dev/null && NUT_SSL_VARIANTS="openssl" + $PKG_CONFIG --exists nss 2>/dev/null && NUT_SSL_VARIANTS="nss" + $PKG_CONFIG --exists openssl 2>/dev/null && NUT_SSL_VARIANTS="openssl" fi # else leave at "auto", if we skipped building # two variants while having two possibilities fi @@ -1030,12 +1200,12 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ -z "$NUT_USB_VARIANTS" ] ; then # Check preferred version first, in case BUILD_USB_ONCE==true - if pkg-config --exists libusb-1.0 ; then + if $PKG_CONFIG --exists libusb-1.0 ; then NUT_USB_VARIANTS="1.0" fi # TODO: Is there anywhere a `pkg-config --exists libusb-0.1`? - if pkg-config --exists libusb || ( command -v libusb-config || which libusb-config ) 2>/dev/null >/dev/null ; then + if $PKG_CONFIG --exists libusb || ( command -v libusb-config || which libusb-config ) 2>/dev/null >/dev/null ; then if [ -z "$NUT_USB_VARIANTS" ] ; then NUT_USB_VARIANTS="0.1" else @@ -1149,6 +1319,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp } echo "=== Configured NUT_SSL_VARIANT='$NUT_SSL_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" build_to_only_catch_errors && { SUCCEEDED="${SUCCEEDED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[build]" } || { @@ -1226,8 +1397,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Quietly build one scenario, whatever we can (or not) # configure regarding USB and other features NUT_USB_VARIANT=auto - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb") configure_nut @@ -1235,8 +1408,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; no) echo "=== Building without USB support (check mixed drivers coded for Serial/USB support)..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--without-usb") configure_nut @@ -1244,8 +1419,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; libusb-*) echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb=${NUT_USB_VARIANT}") configure_nut @@ -1253,8 +1430,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; *) echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb=libusb-${NUT_USB_VARIANT}") configure_nut @@ -1273,6 +1452,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp } echo "=== Configured NUT_USB_VARIANT='$NUT_USB_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" build_to_only_catch_errors && { SUCCEEDED="${SUCCEEDED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[build]" } || { @@ -1349,6 +1529,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ "$RES_ALLERRORS" != 0 ]; then # Leading space is included in FAILED echo "FAILED build(s) with code ${RES_ALLERRORS}:${FAILED}" >&2 + else + echo "(and no build scenarios had failed)" >&2 fi echo "Initially estimated ${BUILDSTODO_INITIAL} variations for BUILD_TYPE='$BUILD_TYPE'" >&2 @@ -1410,6 +1592,8 @@ bindings) sleep 5 fi echo "" + + cd "${SCRIPTDIR}" if [ -s Makefile ]; then # Let initial clean-up be at default verbosity echo "=== Starting initial clean-up (from old build products)" @@ -1419,13 +1603,24 @@ bindings) ./autogen.sh + if [ "${CI_BUILDDIR}" != "." ]; then + # Per above, we always start this routine in absolute $SCRIPTDIR + echo "=== Running NUT build out-of-tree in ${CI_BUILDDIR}" + mkdir -p "${CI_BUILDDIR}" && cd "${CI_BUILDDIR}" || exit + CONFIGURE_SCRIPT="${SCRIPTDIR}/configure" + else + CONFIGURE_SCRIPT="./configure" + fi + # NOTE: Default NUT "configure" actually insists on some features, # like serial port support unless told otherwise, or docs if possible. # Below we aim for really fast iterations of C/C++ development so # enable whatever is auto-detectable (except docs), and highlight - # any warnings if we can: - #./configure - ./configure --enable-Wcolor --with-all=auto --with-cgi=auto --with-serial=auto --with-dev=auto --with-doc=skip + # any warnings if we can. + ${CONFIGURE_SCRIPT} --enable-Wcolor \ + --with-all=auto --with-cgi=auto --with-serial=auto \ + --with-dev=auto --with-doc=skip \ + --enable-check-NIT --enable-maintainer-mode # NOTE: Currently parallel builds are expected to succeed (as far # as recipes are concerned), and the builds without a BUILD_TYPE diff --git a/clients/Makefile.am b/clients/Makefile.am index 8189724ae8..6a4d0b84d2 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -75,12 +75,12 @@ endif # object .so names would differ) # libupsclient version information -libupsclient_la_LDFLAGS = -version-info 5:0:0 -export-symbols-regex ^upscli_ +libupsclient_la_LDFLAGS = -version-info 6:0:0 -export-symbols-regex ^upscli_ if HAVE_CXX11 # libnutclient version information and build libnutclient_la_SOURCES = nutclient.h nutclient.cpp -libnutclient_la_LDFLAGS = -version-info 1:0:0 +libnutclient_la_LDFLAGS = -version-info 2:0:0 # Needed in not-standalone builds with -DHAVE_NUTCOMMON=1 # which is defined for in-tree CXX builds above: libnutclient_la_LIBADD = $(top_builddir)/common/libcommonclient.la diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index 34179527f5..6dfbe3a4bb 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -131,11 +131,11 @@ class Socket Socket(); ~Socket(); - void connect(const std::string& host, int port); + void connect(const std::string& host, uint16_t port); void disconnect(); bool isConnected()const; - void setTimeout(long timeout); + void setTimeout(time_t timeout); bool hasTimeout()const{return _tv.tv_sec>=0;} size_t read(void* buf, size_t sz); @@ -164,12 +164,12 @@ Socket::~Socket() disconnect(); } -void Socket::setTimeout(long timeout) +void Socket::setTimeout(time_t timeout) { _tv.tv_sec = timeout; } -void Socket::connect(const std::string& host, int port) +void Socket::connect(const std::string& host, uint16_t port) { int sock_fd; struct addrinfo hints, *res, *ai; @@ -186,7 +186,7 @@ void Socket::connect(const std::string& host, int port) throw nut::UnknownHostException(); } - snprintf(sport, sizeof(sport), "%hu", static_cast(port)); + snprintf(sport, sizeof(sport), "%ju", static_cast(port)); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; @@ -561,7 +561,7 @@ _socket(new internal::Socket) // Do not connect now } -TcpClient::TcpClient(const std::string& host, int port): +TcpClient::TcpClient(const std::string& host, uint16_t port): Client(), _timeout(0), _socket(new internal::Socket) @@ -574,7 +574,7 @@ TcpClient::~TcpClient() delete _socket; } -void TcpClient::connect(const std::string& host, int port) +void TcpClient::connect(const std::string& host, uint16_t port) { _host = host; _port = port; @@ -591,7 +591,7 @@ std::string TcpClient::getHost()const return _host; } -int TcpClient::getPort()const +uint16_t TcpClient::getPort()const { return _port; } @@ -606,12 +606,12 @@ void TcpClient::disconnect() _socket->disconnect(); } -void TcpClient::setTimeout(long timeout) +void TcpClient::setTimeout(time_t timeout) { _timeout = timeout; } -long TcpClient::getTimeout()const +time_t TcpClient::getTimeout()const { return _timeout; } @@ -811,12 +811,37 @@ void TcpClient::deviceLogin(const std::string& dev) detectError(sendQuery("LOGIN " + dev)); } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. +/* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary". + * For the sake of old/new server/client interoperability, + * practical implementations should try to use one and fall + * back to the other, and only fail if both return "ERR". */ void TcpClient::deviceMaster(const std::string& dev) { - detectError(sendQuery("MASTER " + dev)); + try { + detectError(sendQuery("MASTER " + dev)); + } catch (NutException &exOrig) { + try { + detectError(sendQuery("PRIMARY " + dev)); + } catch (NutException &exRetry) { + NUT_UNUSED_VARIABLE(exRetry); + throw exOrig; + } + } +} + +void TcpClient::devicePrimary(const std::string& dev) +{ + try { + detectError(sendQuery("PRIMARY " + dev)); + } catch (NutException &exOrig) { + try { + detectError(sendQuery("MASTER " + dev)); + } catch (NutException &exRetry) { + NUT_UNUSED_VARIABLE(exRetry); + throw exOrig; + } + } } void TcpClient::deviceForcedShutdown(const std::string& dev) @@ -1313,15 +1338,20 @@ void Device::login() getClient()->deviceLogin(getName()); } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. - */ +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void Device::master() { if (!isOk()) throw NutException("Invalid device"); getClient()->deviceMaster(getName()); } +void Device::primary() +{ + if (!isOk()) throw NutException("Invalid device"); + getClient()->devicePrimary(getName()); +} + void Device::forcedShutdown() { } @@ -1566,7 +1596,7 @@ strarr stringvector_to_strarr(const std::vector& strset) return arr; } -NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port) +NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port) { nut::TcpClient* client = new nut::TcpClient; try @@ -1635,7 +1665,7 @@ int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client) return -1; } -void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout) +void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout) { if(client) { @@ -1647,7 +1677,7 @@ void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout) } } -long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client) +time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client) { if(client) { @@ -1725,9 +1755,8 @@ int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev) return -1; } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. - */ +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void nutclient_device_master(NUTCLIENT_t client, const char* dev) { if(client) @@ -1744,6 +1773,22 @@ void nutclient_device_master(NUTCLIENT_t client, const char* dev) } } +void nutclient_device_primary(NUTCLIENT_t client, const char* dev) +{ + if(client) + { + nut::Client* cl = static_cast(client); + if(cl) + { + try + { + cl->devicePrimary(dev); + } + catch(...){} + } + } +} + void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev) { if(client) diff --git a/clients/nutclient.h b/clients/nutclient.h index 1458779bdd..e1020f2b67 100644 --- a/clients/nutclient.h +++ b/clients/nutclient.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include /* See include/common.h for details behind this */ #ifndef NUT_UNUSED_VARIABLE @@ -322,15 +324,18 @@ class Client */ virtual void deviceLogin(const std::string& dev) = 0; /** - * Retrieve the number of user longged in the specified device. + * Retrieve the number of user logged-in for the specified device. * \param dev Device name. * \return Number of logged-in users. */ virtual int deviceGetNumLogins(const std::string& dev) = 0; - /* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + /* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary". + * For the sake of old/new server/client interoperability, + * practical implementations should try to use one and fall + * back to the other, and only fail if both return "ERR". */ virtual void deviceMaster(const std::string& dev) = 0; + virtual void devicePrimary(const std::string& dev) = 0; virtual void deviceForcedShutdown(const std::string& dev) = 0; /** @@ -355,6 +360,11 @@ class Client */ class TcpClient : public Client { + /* We have a number of direct-call methods we do not expose + * generally, but still want covered with integration tests + */ + friend class NutActiveClientTest; + public: /** * Construct a nut TcpClient object. @@ -367,7 +377,7 @@ class TcpClient : public Client * \param host Server host name. * \param port Server port. */ - TcpClient(const std::string& host, int port = 3493); + TcpClient(const std::string& host, uint16_t port = 3493); ~TcpClient() override; /** @@ -375,7 +385,7 @@ class TcpClient : public Client * \param host Server host name. * \param port Server port. */ - void connect(const std::string& host, int port = 3493); + void connect(const std::string& host, uint16_t port = 3493); /** * Connect to the server. @@ -397,13 +407,13 @@ class TcpClient : public Client * Set the timeout in seconds. * \param timeout Timeout n seconds, negative to block operations. */ - void setTimeout(long timeout); + void setTimeout(time_t timeout); /** * Retrieve the timeout. * \returns Current timeout in seconds. */ - long getTimeout()const; + time_t getTimeout()const; /** * Retriueve the host name of the server the client is connected to. @@ -414,7 +424,7 @@ class TcpClient : public Client * Retriueve the port of host of the server the client is connected to. * \return Server port */ - int getPort()const; + uint16_t getPort()const; virtual void authenticate(const std::string& user, const std::string& passwd) override; virtual void logout() override; @@ -441,6 +451,7 @@ class TcpClient : public Client * and probably an API bump also, to rename/alias the routine. */ virtual void deviceMaster(const std::string& dev) override; + virtual void devicePrimary(const std::string& dev) override; virtual void deviceForcedShutdown(const std::string& dev) override; virtual int deviceGetNumLogins(const std::string& dev) override; @@ -466,8 +477,8 @@ class TcpClient : public Client private: std::string _host; - int _port; - long _timeout; + uint16_t _port; + time_t _timeout; internal::Socket* _socket; }; @@ -612,6 +623,7 @@ class Device * and probably an API bump also, to rename/alias the routine. */ void master(); + void primary(); void forcedShutdown(); /** * Retrieve the number of logged user for the device. @@ -873,6 +885,7 @@ int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); * and probably an API bump also, to rename/alias the routine. */ void nutclient_device_master(NUTCLIENT_t client, const char* dev); +void nutclient_device_primary(NUTCLIENT_t client, const char* dev); /** * Set the FSD flag for the device. @@ -1018,7 +1031,7 @@ typedef NUTCLIENT_t NUTCLIENT_TCP_t; * \param port Host port. * \return New client or nullptr if failed. */ -NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); +NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port); /** * Test if a nut TCP client is connected. * \param client Nut TCP client handle. @@ -1041,12 +1054,12 @@ int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); * Set the timeout value for the TCP connection. * \param timeout Timeout in seconds, negative for blocking. */ -void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); +void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout); /** * Retrieve the timeout value for the TCP connection. * \return Timeout value in seconds. */ -long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); +time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); /** \} */ diff --git a/clients/nutclientmem.cpp b/clients/nutclientmem.cpp index 4d214a5368..df97cb3ce1 100644 --- a/clients/nutclientmem.cpp +++ b/clients/nutclientmem.cpp @@ -180,12 +180,20 @@ void MemClientStub::deviceLogin(const std::string& dev) throw NutException("Not implemented"); } +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void MemClientStub::deviceMaster(const std::string& dev) { NUT_UNUSED_VARIABLE(dev); throw NutException("Not implemented"); } +void MemClientStub::devicePrimary(const std::string& dev) +{ + NUT_UNUSED_VARIABLE(dev); + throw NutException("Not implemented"); +} + void MemClientStub::deviceForcedShutdown(const std::string& dev) { NUT_UNUSED_VARIABLE(dev); @@ -242,6 +250,3 @@ NUTCLIENT_MEM_t nutclient_mem_create_client() } } /* extern "C" */ - - - diff --git a/clients/nutclientmem.h b/clients/nutclientmem.h index 6788a2bf60..3bd22439fa 100644 --- a/clients/nutclientmem.h +++ b/clients/nutclientmem.h @@ -69,7 +69,10 @@ class MemClientStub : public Client virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") override; virtual void deviceLogin(const std::string& dev) override; + /* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ virtual void deviceMaster(const std::string& dev) override; + virtual void devicePrimary(const std::string& dev) override; virtual void deviceForcedShutdown(const std::string& dev) override; virtual int deviceGetNumLogins(const std::string& dev) override; diff --git a/clients/upsc.c b/clients/upsc.c index 01959ae646..550b74a94c 100644 --- a/clients/upsc.c +++ b/clients/upsc.c @@ -25,6 +25,7 @@ #include #include +#include "nut_stdint.h" #include "upsclient.h" static char *upsname = NULL, *hostname = NULL; @@ -208,7 +209,8 @@ static void clean_exit(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; int varlist = 0, clientlist = 0, verbose = 0; const char *prog = xbasename(argv[0]); diff --git a/clients/upsclient.c b/clients/upsclient.c index afe815ec4c..2787783abe 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -38,10 +38,10 @@ #include #include -#include "upsclient.h" #include "common.h" #include "nut_stdint.h" #include "timehead.h" +#include "upsclient.h" /* WA for Solaris/i386 bug: non-blocking connect sets errno to ENOENT */ #if (defined NUT_PLATFORM_SOLARIS) @@ -630,7 +630,7 @@ static ssize_t upscli_select_read(const int fd, void *buf, const size_t buflen, # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" #endif /* internal: abstract the SSL calls for the other functions */ -static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout) +static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout) { ssize_t ret = -1; @@ -714,7 +714,7 @@ static ssize_t upscli_select_write(const int fd, const void *buf, const size_t b # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" #endif /* internal: abstract the SSL calls for the other functions */ -static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout) +static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout) { ssize_t ret = -1; @@ -948,7 +948,7 @@ static int upscli_sslinit(UPSCONN_t *ups, int verifycert) #endif /* WITH_SSL */ -int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,struct timeval * timeout) +int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags, struct timeval * timeout) { int sock_fd; struct addrinfo hints, *res, *ai; @@ -974,7 +974,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru return -1; } - snprintf(sport, sizeof(sport), "%hu", (unsigned short int)port); + snprintf(sport, sizeof(sport), "%ju", (uintmax_t)port); memset(&hints, 0, sizeof(hints)); @@ -1150,7 +1150,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru return 0; } -int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags) +int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags) { return upscli_tryconnect(ups,host,port,flags,NULL); } @@ -1441,7 +1441,7 @@ int upscli_list_next(UPSCONN_t *ups, size_t numq, const char **query, return 1; } -ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout) +ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout) { ssize_t ret; @@ -1479,7 +1479,7 @@ ssize_t upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) return upscli_sendline_timeout(ups, buf, buflen, 0); } -ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout) +ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout) { ssize_t ret; size_t recv; @@ -1538,7 +1538,7 @@ ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) } /* split upsname[@hostname[:port]] into separate components */ -int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port) +int upscli_splitname(const char *buf, char **upsname, char **hostname, uint16_t *port) { char *s, tmp[SMALLBUF], *last = NULL; @@ -1574,9 +1574,10 @@ int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port } /* split hostname[:port] into separate components */ -int upscli_splitaddr(const char *buf, char **hostname, int *port) +int upscli_splitaddr(const char *buf, char **hostname, uint16_t *port) { char *s, tmp[SMALLBUF], *last = NULL; + long l; /* paranoia */ if ((!buf) || (!hostname) || (!port)) { @@ -1619,12 +1620,13 @@ int upscli_splitaddr(const char *buf, char **hostname, int *port) } } - /* FIXME: This assumes but does not check that "long" port - * fits in an "int" and is in IP range (under 65535) */ - if ((*(++s) == '\0') || ((*port = (int)strtol(s, NULL, 10)) < 1 )) { + /* Check that "long" port fits in an "uint16_t" so is in IP range + * (under 65536) */ + if ((*(++s) == '\0') || ((l = strtol(s, NULL, 10)) < 1 ) || (l > 65535)) { fprintf(stderr, "upscli_splitaddr: no port specified after ':' separator\n"); return -1; } + *port = (uint16_t)l; return 0; } diff --git a/clients/upsclient.h b/clients/upsclient.h index 6608d1be44..08574b8f8b 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -21,13 +21,26 @@ #define UPSCLIENT_H_SEEN #ifdef WITH_OPENSSL - #include - #include + #include + #include #elif defined(WITH_NSS) /* WITH_OPENSSL */ #include #include #endif /* WITH_OPENSSL | WITH_NSS */ +/* Not including nut_stdint.h because this is part of end-user API */ +#if defined HAVE_INTTYPES_H + #include +#endif + +#if defined HAVE_STDINT_H + #include +#endif + +#if defined HAVE_LIMITS_H + #include +#endif + #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { @@ -41,7 +54,7 @@ extern "C" { typedef struct { char *host; - int port; + uint16_t port; int fd; int flags; int upserror; @@ -71,8 +84,8 @@ const char *upscli_strerror(UPSCONN_t *ups); int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd); int upscli_cleanup(void); -int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags, struct timeval *tv); -int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); +int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags, struct timeval *tv); +int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags); void upscli_add_host_cert(const char* hostname, const char* certname, int certverify, int forcessl); @@ -86,16 +99,16 @@ int upscli_list_start(UPSCONN_t *ups, size_t numq, const char **query); int upscli_list_next(UPSCONN_t *ups, size_t numq, const char **query, size_t *numa, char ***answer); -ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout); +ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout); ssize_t upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); -ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout); +ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout); ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_splitname(const char *buf, char **upsname, char **hostname, - int *port); + uint16_t *port); -int upscli_splitaddr(const char *buf, char **hostname, int *port); +int upscli_splitaddr(const char *buf, char **hostname, uint16_t *port); int upscli_disconnect(UPSCONN_t *ups); diff --git a/clients/upscmd.c b/clients/upscmd.c index 7134671103..c057303d01 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -197,9 +197,10 @@ static void do_cmd(char **argv, const int argc) #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* From the check above, we know that we have exactly UUID4_LEN chars - * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix. + * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix, + * plus the null-byte. */ - assert (UUID4_LEN == snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); + assert (UUID4_LEN == 1 + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION #pragma GCC diagnostic pop #endif @@ -269,7 +270,8 @@ static void clean_exit(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; ssize_t ret; int have_un = 0, have_pw = 0, cmdlist = 0; char buf[SMALLBUF * 2], username[SMALLBUF], password[SMALLBUF]; diff --git a/clients/upsimage.c b/clients/upsimage.c index e6d0c62398..d49156c5b0 100644 --- a/clients/upsimage.c +++ b/clients/upsimage.c @@ -37,20 +37,20 @@ */ #include "common.h" +#include "nut_stdint.h" #include "upsclient.h" #include "cgilib.h" #include #include #include -#include "nut_stdint.h" #include "upsimagearg.h" #define MAX_CGI_STRLEN 64 static char *monhost = NULL, *cmd = NULL; -static int port; +static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; diff --git a/clients/upslog.c b/clients/upslog.c index 1d8b2aec65..a0ebeaa40b 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -37,9 +37,11 @@ #include "config.h" #include "timehead.h" +#include "nut_stdint.h" #include "upslog.h" - static int port, reopen_flag = 0, exit_flag = 0; + static int reopen_flag = 0, exit_flag = 0; + static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; @@ -123,7 +125,9 @@ static void help(const char *prog) printf(" -f - Log format. See below for details.\n"); printf(" - Use -f \"\" so your shell doesn't break it up.\n"); printf(" -i - Time between updates, in seconds\n"); - printf(" -l - Log file name, or - for stdout\n"); + printf(" -l - Log file name, or - for stdout (foreground by default)\n"); + printf(" -F - stay foregrounded even if logging into a file\n"); + printf(" -B - stay backgrounded even if logging to stdout\n"); printf(" -p - Base name for PID file (defaults to \"%s\")\n", prog); printf(" -s - Monitor UPS - @[:]\n"); printf(" - Example: -s myups@server\n"); @@ -391,7 +395,7 @@ static void run_flist(void) int main(int argc, char **argv) { - int interval = 30, i; + int interval = 30, i, foreground = -1; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; @@ -403,7 +407,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); - while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:")) != -1) { + while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:FB")) != -1) { switch(i) { case 'h': help(prog); @@ -437,6 +441,14 @@ int main(int argc, char **argv) case 'p': pidfilebase = optarg; break; + + case 'F': + foreground = 1; + break; + + case 'B': + foreground = 0; + break; } } @@ -501,8 +513,17 @@ int main(int argc, char **argv) open_syslog(prog); - if (logfile != stdout) + if (foreground < 0) { + if (logfile == stdout) { + foreground = 1; + } else { + foreground = 0; + } + } + + if (!foreground) { background(); + } setup_signals(); diff --git a/clients/upsmon.c b/clients/upsmon.c index 37c908091f..4b65fc1795 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -89,6 +89,14 @@ static int opt_af = AF_UNSPEC; static struct sigaction sa; static sigset_t nut_upsmon_sigmask; +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the upsmon.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). + */ +static int nut_debug_level_global = -1; + static void setflag(int *val, int flag) { *val |= flag; @@ -209,9 +217,14 @@ static void do_notify(const utype_t *ups, int ntype) * we do not need to try becoming a primary). This currently * propagates further as the return value of do_upsd_auth(). */ +/* TODO: Includes API change in NUT 2.8.0 to replace deprecated + * keywords "MASTER" with "PRIMARY", and "SLAVE" with "SECONDARY", + * (and backwards-compatible alias handling) + */ static int apply_for_primary(utype_t *ups) { char buf[SMALLBUF]; + char upscli_readraw_error; /* don't bother if we're not configured as a primary for this ups */ if (!flag_isset(ups->status, ST_PRIMARY)) @@ -224,10 +237,13 @@ static int apply_for_primary(utype_t *ups) return 0; } - /* TODO: Use PRIMARY first but if talking to older server, retry with MASTER */ - snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname); + /* Use PRIMARY first but if talking to older server, retry with MASTER */ + snprintf(buf, sizeof(buf), "PRIMARY %s\n", ups->upsname); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { + /* File descriptor not suitable, net_write() errors, etc. + * Not connected to issues with PRIMARY vs. MASTER keyword. + */ upslogx(LOG_ALERT, "Can't set primary managerial mode on UPS [%s] - %s", ups->sys, upscli_strerror(&ups->conn)); return 0; @@ -237,8 +253,35 @@ static int apply_for_primary(utype_t *ups) if (!strncmp(buf, "OK", 2)) return 1; - /* not ERR, but not caught by readline either? */ + /* Try the older keyword */ + upsdebugx(3, + "%s: Server did not grant PRIMARY mode on UPS [%s], " + "retry with older MASTER keyword", + __func__, ups->upsname); + snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname); + + if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { + upslogx(LOG_ALERT, "Can't set primary managerial mode on UPS [%s] - %s", + ups->sys, upscli_strerror(&ups->conn)); + return 0; + } + + if (upscli_readline(&ups->conn, buf, sizeof(buf)) == 0) { + if (!strncmp(buf, "OK", 2)) + return 1; + + upscli_readraw_error = 0; + } + else { + upscli_readraw_error = 1; + } + } + else { + upscli_readraw_error = 1; + } + if (upscli_readraw_error == 0) { + /* not ERR, but not caught by readline either? */ upslogx(LOG_ALERT, "Primary managerial privileges unavailable on UPS [%s]", ups->sys); upslogx(LOG_ALERT, "Response: [%s]", buf); @@ -253,9 +296,6 @@ static int apply_for_primary(utype_t *ups) } /* authenticate to upsd, plus do LOGIN and MASTER if applicable */ -/* TODO: API change pending to replace deprecated MASTER with PRIMARY - * and SLAVE with SECONDARY (and backwards-compatible alias handling) - */ static int do_upsd_auth(utype_t *ups) { char buf[SMALLBUF]; @@ -1271,7 +1311,7 @@ static int parse_conf_arg(size_t numargs, char **arg) } /* RUN_AS_USER */ - if (!strcmp(arg[0], "RUN_AS_USER")) { + if (!strcmp(arg[0], "RUN_AS_USER")) { free(run_as_user); run_as_user = xstrdup(arg[1]); return 1; @@ -1296,6 +1336,18 @@ static int parse_conf_arg(size_t numargs, char **arg) return 1; } + /* 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, "WARNING : Invalid DEBUG_MIN value found in upsmon.conf global settings"); + } + return 1; + } + /* using up to arg[2] below */ if (numargs < 3) return 0; @@ -1373,6 +1425,13 @@ static void loadconfig(void) fatalx(EXIT_FAILURE, "%s", ctx.errmsg); } + if (reload_flag == 1) { + /* if upsmon.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", @@ -1399,6 +1458,15 @@ static void loadconfig(void) } } + if (reload_flag == 1) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsmon.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + pconf_finish(&ctx); } @@ -1789,7 +1857,10 @@ static void help(const char *arg_progname) printf(" - fsd: shutdown all primary-mode UPSes (use with caution)\n"); printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring 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(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -K checks POWERDOWNFLAG, sets exit code to 0 if set\n"); printf(" -p always run privileged (disable privileged parent)\n"); @@ -2020,7 +2091,8 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0; + int i, cmd = 0, cmdret = -1, checking_flag = 0, foreground = -1; + pid_t oldpid = -1; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -2031,7 +2103,7 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(RUN_AS_USER); - while ((i = getopt(argc, argv, "+Dhic:f:pu:VK46")) != -1) { + while ((i = getopt(argc, argv, "+DFBhic:P:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) @@ -2045,9 +2117,21 @@ int main(int argc, char *argv[]) if (cmd == 0) help(argv[0]); break; + + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(argv[0]); + break; + case 'D': nut_debug_level++; break; + case 'F': + foreground = 1; + break; + case 'B': + foreground = 0; + break; case 'f': free(configfile); configfile = xstrdup(optarg); @@ -2084,18 +2168,50 @@ int main(int argc, char *argv[]) } } + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + if (cmd) { - sendsignal(prog, cmd); - exit(EXIT_SUCCESS); + if (oldpid < 0) { + cmdret = sendsignal(prog, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + /* exit(EXIT_SUCCESS); */ + 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 (sendsignal(prog, 0) == 0) { + if (oldpid < 0) { + cmdret = sendsignal(prog, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } + switch (cmdret) { + case 0: printf("Fatal error: A previous upsmon 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 " + "to see if previous upsmon instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse")); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; @@ -2105,6 +2221,14 @@ int main(int argc, char *argv[]) loadconfig(); + /* CLI debug level can not be smaller than debug_min specified + * in upsmon.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); + if (checking_flag) exit(check_pdflag()); @@ -2127,9 +2251,11 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (nut_debug_level < 1) { + if (!foreground) { background(); - } else { + } + + if (nut_debug_level >= 1) { upsdebugx(1, "debug level is '%d'", nut_debug_level); } diff --git a/clients/upsmon.h b/clients/upsmon.h index 23315139b9..d60720b9c3 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -49,7 +49,7 @@ typedef struct { char *sys; /* raw system name from .conf */ char *upsname; /* just upsname */ char *hostname; /* just hostname */ - int port; /* just the port */ + uint16_t port; /* just the port */ unsigned int pv; /* power value from conf */ char *un; /* username (optional for now) */ diff --git a/clients/upsrw.c b/clients/upsrw.c index d2fe1d9efb..2789f9ac71 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -51,6 +51,7 @@ static void usage(const char *prog) printf(" -h display this help text\n"); printf(" -s specify variable to be changed\n"); printf(" use -s VAR=VALUE to avoid prompting for value\n"); + printf(" -l show all possible read/write variables.\n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of setting by the driver\n"); @@ -59,7 +60,7 @@ static void usage(const char *prog) printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); - printf("Call without -s to show all possible read/write variables.\n"); + printf("Call without -s to show all possible read/write variables (same as -l).\n"); } static void clean_exit(void) @@ -122,9 +123,10 @@ static void do_set(const char *varname, const char *newval) #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* From the check above, we know that we have exactly UUID4_LEN chars - * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix. + * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix, + * plus the null-byte. */ - assert (UUID4_LEN == snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); + assert (UUID4_LEN == 1 + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION #pragma GCC diagnostic pop #endif @@ -628,16 +630,23 @@ static void print_rwlist(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; const char *prog = xbasename(argv[0]); char *password = NULL, *username = NULL, *setvar = NULL; - while ((i = getopt(argc, argv, "+hs:p:t:u:wV")) != -1) { + while ((i = getopt(argc, argv, "+hls:p:t:u:wV")) != -1) { switch (i) { case 's': setvar = optarg; break; + case 'l': + if (setvar) { + upslogx(LOG_WARNING, "Listing mode requested, overriding setvar specified earlier!"); + setvar = NULL; + } + break; case 'p': password = optarg; break; diff --git a/clients/upssched.c b/clients/upssched.c index f35fe09ec7..8bb3547479 100644 --- a/clients/upssched.c +++ b/clients/upssched.c @@ -272,6 +272,7 @@ static int open_sock(void) int ret, fd; struct sockaddr_un ssaddr; + check_unix_socket_filename(pipefn); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) @@ -632,6 +633,8 @@ static int try_connect(void) int pipefd, ret; struct sockaddr_un saddr; + check_unix_socket_filename(pipefn); + memset(&saddr, '\0', sizeof(saddr)); saddr.sun_family = AF_UNIX; snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", pipefn); diff --git a/clients/upsset.c b/clients/upsset.c index ac8f2854ae..788b201b9f 100644 --- a/clients/upsset.c +++ b/clients/upsset.c @@ -24,6 +24,7 @@ #include #include +#include "nut_stdint.h" #include "upsclient.h" #include "cgilib.h" #include "parseconf.h" @@ -44,7 +45,7 @@ static char *monups, *username, *password, *function, *upscommand; /* set once the MAGIC_ENABLE_STRING is found in the upsset.conf */ static int magic_string_set = 0; -static int port; +static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; diff --git a/clients/upsstats.c b/clients/upsstats.c index c572f8e684..4b7f7547e5 100644 --- a/clients/upsstats.c +++ b/clients/upsstats.c @@ -19,14 +19,14 @@ */ #include "common.h" +#include "nut_stdint.h" +#include "timehead.h" #include "upsclient.h" #include "status.h" #include "cgilib.h" #include "parseconf.h" -#include "timehead.h" #include "upsstats.h" #include "upsimagearg.h" -#include "nut_stdint.h" #define MAX_CGI_STRLEN 128 #define MAX_PARSE_ARGS 16 @@ -37,7 +37,7 @@ static int use_celsius = 1, refreshdelay = -1, treemode = 0; /* from cgilib's checkhost() */ static char *monhostdesc = NULL; -static int port; +static uint16_t port; static char *upsname, *hostname; static char *upsimgpath="upsimage.cgi", *upsstatpath="upsstats.cgi"; static UPSCONN_t ups; @@ -351,7 +351,7 @@ static void ups_connect(void) { static ulist_t *lastups = NULL; char *newups, *newhost; - int newport; + uint16_t newport; /* try to minimize reconnects */ if (lastups) { diff --git a/common/common.c b/common/common.c index 88dbe573a3..fe50cfe0b3 100644 --- a/common/common.c +++ b/common/common.c @@ -1,7 +1,7 @@ /* common.c - common useful functions Copyright (C) 2000 Russell Kroll - Copyright (C) 2021 Jim Klimov + Copyright (C) 2021-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 @@ -26,6 +26,7 @@ #include #include #include +#include /* the reason we define UPS_VERSION as a static string, rather than a macro, is to make dependency tracking easier (only common.o depends @@ -298,7 +299,9 @@ void writepid(const char *name) pidf = fopen(fn, "w"); if (pidf) { - fprintf(pidf, "%d\n", (int) getpid()); + intmax_t pid = (intmax_t)getpid(); + upsdebugx(1, "Saving PID %jd into %s", pid, fn); + fprintf(pidf, "%jd\n", pid); fclose(pidf); } else { upslog_with_errno(LOG_NOTICE, "writepid: fopen %s", fn); @@ -307,61 +310,97 @@ void writepid(const char *name) umask(mask); } -/* open pidfn, get the pid, then send it sig */ -int sendsignalfn(const char *pidfn, int sig) +/* send sig to pid, returns -1 for error, or + * zero for a successfully sent signal + */ +int sendsignalpid(pid_t pid, int sig) { - char buf[SMALLBUF]; - FILE *pidf; - pid_t pid = -1; int ret; - pidf = fopen(pidfn, "r"); - if (!pidf) { - upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); + if (pid < 2 || pid > get_max_pid_t()) { + upslogx(LOG_NOTICE, + "Ignoring invalid pid number %" PRIdMAX, + (intmax_t) pid); return -1; } - if (fgets(buf, sizeof(buf), pidf) == NULL) { - upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); - fclose(pidf); + /* see if this is going to work first */ + ret = kill(pid, 0); + + if (ret < 0) { + perror("kill"); return -1; } - { /* scoping */ - intmax_t _pid = strtol(buf, (char **)NULL, 10); /* assuming 10 digits for a long */ - if (_pid <= get_max_pid_t()) { - pid = (pid_t)_pid; - } else { - upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); + if (sig != 0) { + /* now actually send it */ + ret = kill(pid, sig); + + if (ret < 0) { + perror("kill"); + return -1; } } - if (pid < 2) { - upslogx(LOG_NOTICE, "Ignoring invalid pid number %" PRIdMAX, (intmax_t) pid); - fclose(pidf); - return -1; + return 0; +} + +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error + */ +pid_t parsepid(const char *buf) +{ + pid_t pid = -1; + + /* assuming 10 digits for a long */ + intmax_t _pid = strtol(buf, (char **)NULL, 10); + if (_pid <= get_max_pid_t()) { + pid = (pid_t)_pid; + } else { + upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); } - /* see if this is going to work first */ - ret = kill(pid, 0); + return pid; +} - if (ret < 0) { - perror("kill"); +/* open pidfn, get the pid, then send it sig + * returns negative codes for errors, or + * zero for a successfully sent signal + */ +int sendsignalfn(const char *pidfn, int sig) +{ + char buf[SMALLBUF]; + FILE *pidf; + pid_t pid = -1; + int ret = -1; + + pidf = fopen(pidfn, "r"); + if (!pidf) { + upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); + return -3; + } + + if (fgets(buf, sizeof(buf), pidf) == NULL) { + upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); fclose(pidf); - return -1; + return -2; } + /* TOTHINK: Original code only closed pidf before + * exiting the method, on error or "normally". + * Why not here? Do we want an (exclusive?) hold + * on it while being active in the method? + */ - /* now actually send it */ - ret = kill(pid, sig); + /* this method actively reports errors, if any */ + pid = parsepid(buf); - if (ret < 0) { - perror("kill"); - fclose(pidf); - return -1; + if (pid >= 0) { + /* this method actively reports errors, if any */ + ret = sendsignalpid(pid, sig); } fclose(pidf); - return 0; + return ret; } int snprintfcat(char *dst, size_t size, const char *fmt, ...) @@ -538,6 +577,27 @@ const char * altpidpath(void) #endif } +/* Die with a standard message if socket filename is too long */ +void check_unix_socket_filename(const char *fn) { + struct sockaddr_un ssaddr; + if (strlen(fn) < sizeof(ssaddr.sun_path)) + return; + + /* Avoid useless truncated pathnames that + * other driver instances would conflict + * with, and upsd can not discover. + * Note this is quite short on many OSes + * varying 104-108 bytes (UNIX_PATH_MAX) + * as opposed to PATH_MAX or MAXPATHLEN + * typically of a kilobyte range. + */ + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + fn, strlen(fn), sizeof(ssaddr.sun_path)); +} + /* logs the formatted string to any configured logging devices + the output of strerror(errno) */ void upslog_with_errno(int priority, const char *fmt, ...) { diff --git a/common/parseconf.c b/common/parseconf.c index fe8f172b96..7c284b750f 100644 --- a/common/parseconf.c +++ b/common/parseconf.c @@ -89,6 +89,7 @@ #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" /* possible states */ @@ -241,7 +242,7 @@ static int findwordstart(PCONF_CTX_t *ctx) return STATE_FINDEOL; /* space = not in a word yet, so loop back */ - if (isspace(ctx->ch)) + if (isspace((size_t)ctx->ch)) return STATE_FINDWORDSTART; /* \ = literal = accept the next char blindly */ @@ -341,7 +342,7 @@ static int collect(PCONF_CTX_t *ctx) } /* space means the word is done */ - if (isspace(ctx->ch)) { + if (isspace((size_t)ctx->ch)) { endofword(ctx); return STATE_FINDWORDSTART; diff --git a/common/str.c b/common/str.c index 35666bb1bf..e064b128e1 100644 --- a/common/str.c +++ b/common/str.c @@ -120,7 +120,7 @@ char *str_ltrim_space(char *string) while ( *string != '\0' && - isspace(*string) + isspace((size_t)*string) ) memmove(string, string + 1, strlen(string)); @@ -141,7 +141,7 @@ char *str_rtrim_space(char *string) while ( ptr >= string && - isspace(*ptr) + isspace((size_t)*ptr) ) *ptr-- = '\0'; @@ -440,7 +440,7 @@ int str_to_long_strict(const char *string, long *number, const int base) if ( string == NULL || *string == '\0' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; @@ -506,7 +506,7 @@ int str_to_ulong_strict(const char *string, unsigned long *number, const int bas *string == '\0' || *string == '+' || *string == '-' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; @@ -570,7 +570,7 @@ int str_to_double_strict(const char *string, double *number, const int base) if ( string == NULL || *string == '\0' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; @@ -653,3 +653,16 @@ char * str_concat(size_t count, ...) return merged; } + +int str_ends_with(const char *s, const char *suff) { + size_t slen; + size_t sufflen; + + if (!s) return 0; /* null string does not end with anything */ + if (!suff) return 1; /* null suffix tails anything */ + + slen = strlen(s); + sufflen = strlen(suff); + + return (slen >= sufflen) && (!memcmp(s + slen - sufflen, suff, sufflen)); +} diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index 46b43b7816..a86a845854 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -45,7 +45,7 @@ # These directives are used by upsdrvctl only and should be specified outside # of a driver definition: # -# maxretry: Optional. Specify the number of attempts to start the driver(s), +# maxretry: OPTIONAL. Specify the number of attempts to start the driver(s), # in case of failure, before giving up. A delay of 'retrydelay' is # inserted between each attempt. Caution should be taken when using # this option, since it can impact the time taken by your system to @@ -53,17 +53,74 @@ # # The built-in default is 1 attempt. # -# retrydelay: Optional. Specify the delay between each restart attempt of the +# retrydelay: OPTIONAL. Specify the delay between each restart attempt of the # driver(s), as specified by 'maxretry'. Caution should be taken # when using this option, since it can impact the time taken by your # system to start. # # The default is 5 seconds. # +# chroot: OPTIONAL. Used for securing. See man page for details. +# +# driverpath: OPTIONAL. Used for custom setups. See man page for details. +# +# nowait: OPTIONAL. Tell upsdrvctl to not wait at all for the driver(s) +# to execute the requested command. Fire and forget. +# +# pollinterval: OPTIONAL. The status of the UPS will be refreshed after a +# maximum delay which is controlled by this setting (default +# 2 seconds). This may be useful if the driver is creating too +# much of a load on your system or network. +# Note that some drivers also have an option called *pollfreq* +# which controls how frequently some of the less critical +# parameters are polled. See respective driver man pages. +# # Set maxretry to 3 by default, this should mitigate race with slow devices: maxretry = 3 +# These directives can be set outside and inside a driver definition, with +# slightly different meanings per context: +# +# maxstartdelay: OPTIONAL. This can be set as a global variable +# above your first UPS definition and it can also be +# set in a UPS section. This value controls how long +# upsdrvctl will wait for the driver to finish starting. +# This keeps your system from getting stuck due to a +# broken driver or UPS. +# The default is 45 seconds. +# +# debug_min: OPTIONAL. Specify a minimum debug level for all driver daemons +# (when specified at global level), or for this driver daemon +# (when specified in a driver section), e.g. for troubleshooting +# a deployment. This does not directly impact the foreground or +# background running mode. If both the global and driver level +# `debug_min` are set, the driver-level setting takes precedence. +# Command-line option `-D` can only increase this verbosity level. +# +# user, group: OPTIONAL. Overrides the compiled-in (also global-section, +# when used in driver section) default unprivileged user/group +# name for NUT device driver. Impacts access rights used for +# the socket file access (group) and communication ports (user). +# +# synchronous: OPTIONAL. The driver work by default in asynchronous +# mode (like *no*) with fallback to synchronous if sending +# fails (i.e *synchronous=auto*). This means that all data +# are pushed by the driver on the communication socket to +# upsd (Unix socket on Unix, Named pipe on Windows) without +# waiting for these data to be actually consumed. With +# some HW, such as ePDUs, that can produce a lot of data, +# asynchronous mode may cause some congestion, resulting in +# the socket to be full, and the driver to appear as not +# connected. By enabling the 'synchronous' flag +# (value = 'yes'), the driver will wait for data to be +# consumed by upsd, prior to publishing more. This can be +# enabled either globally or per driver. +# +# The default is 'no' (i.e. asynchronous mode) for backward +# compatibility of the driver behavior. +# + # These directives are common to all drivers that support ups.conf: # # driver: REQUIRED. Specify the program to run to talk to this UPS. @@ -72,13 +129,15 @@ maxretry = 3 # port: REQUIRED. The serial port where your UPS is connected. # /dev/ttyS0 is usually the first port on Linux boxes, for example. # -# sdorder: optional. When you have multiple UPSes on your system, you +# sdorder: OPTIONAL. When you have multiple UPSes on your system, you # usually need to turn them off in a certain order. upsdrvctl # shuts down all the 0s, then the 1s, 2s, and so on. To exclude # a UPS from the shutdown sequence, set this to -1. # # The default value for this parameter is 0. # +# desc: optional, to keep a note of the UPS purpose, location, etc. +# # nolock: optional, and not recommended for use in this file. # # If you put nolock in here, the driver will not lock the @@ -89,30 +148,31 @@ maxretry = 3 # This is only intended to be used on systems where locking # absolutely must be disabled for the software to work. # -# maxstartdelay: optional. This can be set as a global variable -# above your first UPS definition and it can also be -# set in a UPS section. This value controls how long -# upsdrvctl will wait for the driver to finish starting. -# This keeps your system from getting stuck due to a -# broken driver or UPS. -# -# The default is 45 seconds. -# -# synchronous: optional. The driver work by default in asynchronous -# mode (i.e *synchronous=no*). This means that all data -# are pushed by the driver on the communication socket to -# upsd (Unix socket on Unix, Named pipe on Windows) without -# waiting for these data to be actually consumed. With -# some HW, such as ePDUs, that can produce a lot of data, -# asynchronous mode may cause some congestion, resulting in -# the socket to be full, and the driver to appear as not -# connected. By enabling the 'synchronous' flag -# (value = 'yes'), the driver will wait for data to be -# consumed by upsd, prior to publishing more. This can be -# enabled either globally or per driver. -# -# The default is 'no' (i.e. asynchronous mode) for backward -# compatibility of the driver behavior. +# ignorelb: OPTIONAL. Ignore low battery condition reported by device, +# and evaluate remaining battery charge or runtime instead. +# See man page for details. +# +# usb_set_altinterface(=num): OPTIONAL. Require that NUT calls this method +# to set the interface, even if 0 (default). Some devices require +# the call to initialize; others however can get stuck due to it - +# so it is not called by default. Yet others can be composite +# devices which use a non-zero interface to represent the UPS. +# +# default.: OPTIONAL. Set a default value for which is +# used in case the UPS doesn't provide a value, but which will be +# overwritten if a value is available from the UPS, e.g.: +# default.input.voltage.nominal = 230 +# will report the nominal input voltage to be 230, unless the UPS +# eventually tells us differently. +# +# override.: OPTIONAL. Set a value for that overrides +# (for NUT) any value that may be read from the UPS. +# Used for overriding values from the UPS that are clearly wrong +# (e.g. some devices report wrong values for battery voltage): +# override.battery.voltage.nominal = 12 +# Use with caution! This will only change the appearance of the +# variable to the outside world (and NUT calculations), internally +# in the UPS the original value is used. # # Anything else is passed through to the hardware-specific part of # the driver. diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 1c7e3c5fe7..fb420e3a78 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -145,3 +145,24 @@ # # Unless you have really ancient clients, you probably want to enable this. # Currently disabled by default to ensure compatibility with existing setups. + +# ======================================================================= +# DEBUG_MIN +# DEBUG_MIN 2 +# +# Optionally specify a minimum debug level for `upsd` data daemon, e.g. for +# troubleshooting a deployment, without impacting foreground or background +# running mode directly, and without need to edit init-scripts or service +# unit definitions. Note that command-line option `-D` can only increase +# this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index de38bff66f..aeec849eda 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -430,3 +430,24 @@ FINALDELAY 5 # support SSL, so don't use it unless all of them have it running. # When compiled with NSS support of SSL, can be overridden for host # specified with a CERTHOST directive. + +# -------------------------------------------------------------------------- +# DEBUG_MIN - specify minimal debugging level for upsmon daemon +# e.g. DEBUG_MIN 6 +# +# Optionally specify a minimum debug level for `upsmon` daemon, e.g. for +# troubleshooting a deployment, without impacting foreground or background +# running mode directly, and without need to edit init-scripts or service +# unit definitions. Note that command-line option `-D` can only increase +# this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/configure.ac b/configure.ac index b4716db544..c19975d7b9 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffixed macro like dnl NUT_VERSION_MACRO "2.7.4-2838-gdfc3ac08" dnl in include/nut_version.h (generated by make) -AC_INIT([nut],[2.7.4.1],[https://github.com/networkupstools/nut/issues]) +AC_INIT([nut],[2.8.0.1],[https://github.com/networkupstools/nut/issues]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) @@ -18,6 +18,7 @@ AC_PREFIX_DEFAULT(/usr/local/ups) AM_INIT_AUTOMAKE([subdir-objects]) dnl we need Autoconf 2.61 or better to enable features of Posix that are extensions to C +dnl (and actually 2.64 or better for m4/ax_check_compile_flag.m4 when it is sourced) AC_MSG_CHECKING(for autoconf macro to enable system extensions) m4_version_prereq(2.61, [ AC_MSG_RESULT(yes) @@ -26,6 +27,8 @@ m4_version_prereq(2.61, [ AC_MSG_RESULT(no) ]) +AC_PREREQ([2.64]) + dnl Use "./configure --enable-maintainer-mode" to keep Makefile.in and Makefile dnl in sync after Git updates. AM_MAINTAINER_MODE @@ -50,7 +53,7 @@ AC_DEFINE_UNQUOTED(TREE_VERSION, "${TREE_VERSION}", [NUT tree version]) dnl Should not be necessary, since old servers have well-defined errors for dnl unsupported commands: -NUT_NETVERSION="1.2" +NUT_NETVERSION="1.3" AC_DEFINE_UNQUOTED(NUT_NETVERSION, "${NUT_NETVERSION}", [NUT network protocol version]) @@ -101,6 +104,9 @@ fi RUN_AS_USER="nobody" RUN_AS_GROUP="nobody" +AS_IF([test -n "`getent group nogroup`" && ! test -n "`getent group "${RUN_AS_GROUP}"`"], + [RUN_AS_GROUP="nogroup"] +) PIDPATH="/var/run" dnl Define directory where LIBOBJS replacement functions are @@ -258,7 +264,7 @@ CODE_STRINGINCL='#ifdef HAVE_STRING_H AC_CACHE_CHECK([for strcasecmp(s1,s2)], [ac_cv_func_strcasecmp], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [if (strcasecmp("STR1", "str1") != 0) return 1])], [ac_cv_func_strcasecmp=yes], [ac_cv_func_strcasecmp=no] @@ -270,7 +276,7 @@ AS_IF([test x"${ac_cv_func_strcasecmp}" = xyes], AC_CACHE_CHECK([for strncasecmp(s1,s2,n)], [ac_cv_func_strncasecmp], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [if (strncasecmp("STR1", "strX", 2) != 0) return 1])], [ac_cv_func_strncasecmp=yes], [ac_cv_func_strncasecmp=no] @@ -282,7 +288,7 @@ AS_IF([test x"${ac_cv_func_strncasecmp}" = xyes], AC_CACHE_CHECK([for strdup(s)], [ac_cv_func_strdup], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [[int res = 0; char *t = "Test"; @@ -635,9 +641,9 @@ AC_ARG_WITH(drivers, dnl Break once, maybe a driver hits several categories ]) done - AS_IF([test -z "${DRV_HITS}"], - [AC_MSG_ERROR([Requested driver '$DRV' is not defined in drivers/Makefile.am (or configure.ac has a bug/lag detecting driver lists)])]) done + AS_IF([test -z "${DRV_HITS}"], + [AC_MSG_ERROR([Requested driver '$DRV' is not defined in drivers/Makefile.am (or configure.ac has a bug/lag detecting driver lists)])]) done ]) @@ -737,7 +743,7 @@ NUT_ARG_WITH([doc], [build and install documentation (see docs/configure.txt for dnl NOTE: Until X-Mas 2021, the default was "legacy" (now "medium") NUT_ARG_ENABLE([warnings], [enable warning presets that were picked as useful in maintainership and CI practice (variants include gcc-minimal, gcc-medium, gcc-hard, clang-minimal, clang-medium, clang-hard, all; auto-choosers: hard, medium, minimal, yes=auto='gcc or clang or all at hardcoded default difficulty')], - [medium]) + [auto]) NUT_ARG_ENABLE([Werror], [fail the build if compiler emits any warnings (treat them as errors)], [no]) @@ -1224,6 +1230,29 @@ esac AM_CONDITIONAL(WITH_CPPCHECK, test "${WITH_CPPCHECK}" = "yes") +dnl ---------------------------------------------------------------------- +dnl checks related to --enable-check-NIT + +AC_MSG_CHECKING(whether to run NIT among default make check target) +nut_enable_check_NIT="no" +AC_ARG_ENABLE([check-NIT], + AS_HELP_STRING([--enable-check-NIT], [Run check-NIT among default checks (no)]), +[ + case "${enableval}" in + no) + AC_MSG_RESULT(no) + ;; + *) + AC_MSG_RESULT(yes) + nut_enable_check_NIT="yes" + ;; + esac +], [ + AC_MSG_RESULT(no) +]) + +AM_CONDITIONAL(WITH_CHECK_NIT, test "${nut_enable_check_NIT}" = "yes") + dnl ---------------------------------------------------------------------- dnl checks related to --with-doc @@ -1687,7 +1716,7 @@ AC_MSG_RESULT(${PORT}) AC_MSG_CHECKING(user to run as) AC_ARG_WITH(user, - AS_HELP_STRING([--with-user=username], [user for programs started as root (nobody)]), + AS_HELP_STRING([--with-user=username], [user for programs started as root (${RUN_AS_USER})]), [ case "${withval}" in yes|no) @@ -1706,7 +1735,7 @@ AC_MSG_RESULT(${RUN_AS_USER}) AC_MSG_CHECKING(group membership of user to run as) AC_ARG_WITH(group, - AS_HELP_STRING([--with-group=groupname], [group membership of user for programs started as root (nogroup)]), + AS_HELP_STRING([--with-group=groupname], [group membership of user for programs started as root (${RUN_AS_GROUP})]), [ case "${withval}" in yes|no) @@ -1771,7 +1800,7 @@ AC_MSG_CHECKING(whether to strip debug symbols) AC_ARG_ENABLE(strip, AS_HELP_STRING([--enable-strip], [Strip debugging symbols from binaries (no)]), [ - case "${withval}" in + case "${enableval}" in no) AC_MSG_RESULT(no) ;; @@ -2105,11 +2134,17 @@ return res ? 0 : 1; ' my_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$myCXXFLAGS $pkg_cv_CPPUNIT_CFLAGS $pkg_cv_CPPUNIT_LIBS" + my_LDFLAGS="$LDFLAGS" + my_LIBS="$LIBS" + CXXFLAGS="$myCXXFLAGS $pkg_cv_CPPUNIT_CFLAGS $pkg_cv_CPPUNIT_CXXFLAGS" + LDFLAGS="$my_LDFLAGS $pkg_cv_CPPUNIT_LDFLAGS" + LIBS="$my_LIBS $pkg_cv_CPPUNIT_LIBS" AC_LANG_PUSH([C++]) - AC_RUN_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], [have_cppunit=yes], [have_cppunit=no]) CXXFLAGS="$my_CXXFLAGS" + LDFLAGS="$my_LDFLAGS" + LIBS="$my_LIBS" AC_LANG_POP([C++]) unset CPLUSPLUS_MAIN unset CPLUSPLUS_DECL @@ -3113,7 +3148,7 @@ dnl C89/C90/ANSI mode to be less noisy. Keep this in mind if changing the dnl default "nut_warning_difficulty" and/or the case handling below. dnl NOTE: Until X-Mas 2021, the default was "minimal" (now "medium") nut_warning_difficulty="medium" -AC_MSG_CHECKING([whether to pre-set warnings]) +AC_MSG_CHECKING([whether to pre-set warnings (from '${nut_enable_warnings}')]) AS_CASE(["${nut_enable_warnings}"], [no|all|gcc-legacy|gcc-minimal|clang-minimal|gcc-medium|clang-medium|gcc-hard|clang-hard], [], [clang], [nut_enable_warnings="${nut_enable_warnings}-${nut_warning_difficulty}"], @@ -3127,7 +3162,15 @@ AS_CASE(["${nut_enable_warnings}"], [AS_IF([test "${GCC}" = "yes"], [ AS_CASE(["${CFLAGS}"], [*89*|*90*|*ansi*], [nut_enable_warnings="gcc-minimal"], - [nut_enable_warnings="gcc-${nut_warning_difficulty}"] + [AS_CASE(["`$CC --version | grep -i gcc`"], + [*" "1.*|*" "2.*|3.*|*" "4.0*|*" "4.1*|*" "4.2*|*" "4.3*], [ + AC_MSG_WARN([Very old GCC in use, disabling warnings]) + dnl #AS_IF([test x"${nut_enable_Werror}" = xauto], [nut_enable_Werror="no"]) + nut_enable_Werror="no" + nut_enable_warnings="no"], + [*" "4.4*|*" "4.5*|*" "4.6*|*" "4.7*|*" "4.8*], [nut_enable_warnings="gcc-legacy"], + [nut_enable_warnings="gcc-${nut_warning_difficulty}"] + )] )], [nut_enable_warnings="all"]) ]) ], @@ -3488,6 +3531,7 @@ AC_CONFIG_FILES([ tools/nut-scanner/Makefile tools/nutconf/Makefile tests/Makefile + tests/NIT/Makefile Makefile ]) @@ -3497,7 +3541,8 @@ m4_foreach_w([SCRIPTFILE], [ scripts/DMF/jsonify-mib.py scripts/DMF/xmlify-mib.py scripts/HP-UX/postinstall - scripts/python/app/NUT-Monitor + scripts/python/app/NUT-Monitor-py2gtk2 + scripts/python/app/NUT-Monitor-py3qt5 scripts/augeas/gen-nutupsconf-aug.py scripts/python/module/test_nutclient.py scripts/upsdrvsvcctl/nut-driver-enumerator.sh diff --git a/data/driver.list.in b/data/driver.list.in index b9cd0a3908..ddc10ff3a9 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -35,9 +35,10 @@ # Duplicate text in the last field will be cooked out during the conversion # to HTML with ROWSPAN magic. They must be an exact match for this to work. -"Ablerex" "ups" "2" "MS-RT" "" "blazer_ser" "Ablerex" "ups" "2" "625L" "USB" "blazer_usb" "Ablerex" "ups" "2" "Hope Office 400/600" "" "blazer_ser" +"Ablerex" "ups" "2" "MARS MS3000RT" "" "blazer_ser" +"Ablerex" "ups" "2" "MS-RT" "" "blazer_ser" "Ablerex" "ups" "2" "MP series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "ARES Plus series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "MSII series" "USB" "nutdrv_qx" @@ -45,12 +46,14 @@ "Ablerex" "ups" "2" "GRs series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "GRs Plus series" "USB" "nutdrv_qx" - "ActivePower" "ups" "2" "400VA" "" "blazer_ser" "ActivePower" "ups" "2" "1400VA" "" "blazer_ser" "ActivePower" "ups" "2" "2000VA" "" "blazer_ser" "Advice" "ups" "2" "TopGuard 2000" "" "blazer_ser" +"Advice" "ups" "2" "Top V Pro 6-10K" "USB" "blazer_usb" # https://www.advice.co.il/en/advice-products/ups-systems/ups-online-systems/one-phase-ups-5k-10k/%D7%90%D7%9C-%D7%A4%D7%A1%D7%A7-%D7%90%D7%95%D7%9F-%D7%9C%D7%99%D7%99%D7%9F-top-v-pro-6-10k-detail https://github.com/networkupstools/nut/issues/744 +"Advice" "ups" "2" "PRS850" "USB" "blazer_usb" +"Advice" "ups" "2" "PRV700 Pro" "USB" "blazer_usb" "AEC" "ups" "1" "MiniGuard UPS 700" "Megatec M2501 cable" "genericups upstype=21" @@ -58,6 +61,7 @@ "AEG Power Solutions" "ups" "3" "PROTECT NAS" "" "usbhid-ups" "AEG Power Solutions" "ups" "3" "PROTECT B" "" "usbhid-ups" +"APC" "ups" "3" "APC AP9584 Serial-to-USB kit" "USB" "usbhid-ups" # Allows USB connections to Serial-port APC devices, https://github.com/networkupstools/nut/pull/181 "APC" "ups" "2" "Back-UPS 1200BR (Microsol)" "" "solis" "APC" "ups" "2" "Back-UPS BZ2200BI-BR (Microsol)" "" "solis" "APC" "ups" "1" "Back-UPS Pro" "" "apcsmart" @@ -72,6 +76,8 @@ "APC" "ups" "2" "Back-UPS ES/CyberFort 350" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS BF500" "USB" "usbhid-ups" "APC" "ups" "2" "BACK-UPS XS LCD" "USB" "usbhid-ups" +"APC" "ups" "2" "Back-UPS XS 1000M (Back-UPS Pro 1000, Model BX1000M)" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/139 +"APC" "ups" "2" "SMC2200BI-BR" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/557 "APC" "ups" "2" "Smart-UPS (USB)" "USB" "usbhid-ups" "APC" "ups" "1" "Back-UPS" "940-0095A/C cables" "genericups upstype=1" "APC" "ups" "1" "Back-UPS" "940-0020B/C cables" "genericups upstype=2" @@ -96,6 +102,8 @@ "Appro" "pdu" "1" "SWPDU" "48 outlets" "powerman-pdu (experimental)" +"ARES" "ups" "2" "AR265i" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 did not explicitly mention the driver parameters, but other reports in the discussion did + "ARTronic" "ups" "2" "ARTon Millenium 1/2/3/6/10 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Millenium 3.1 10/15/20 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Titanium 6/10 kVA" "Serial" "blazer_ser" @@ -128,6 +136,7 @@ "Atlantis Land" "ups" "2" "(various)" "USB" "nutdrv_qx" "Aviem Systems" "ups" "2" "Aviem Power RT 1000-3000VA" "" "blazer_ser" +"Aviem Systems" "ups" "2" "Aviem Pro 2000VA" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/827 "Baytech" "pdu" "1" "RPC3" "8 outlets" "powerman-pdu (experimental)" "Baytech" "pdu" "1" "RPC3-20NC" "8 outlets" "powerman-pdu (experimental)" @@ -195,6 +204,10 @@ "COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K EC" "" "blazer_usb" "COVER ENERGY SA" "ups" "2" "COVER PRM 6K/10K PR" "" "blazer_usb" +"CPC" "ups" "2" "RACK850VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 + +"Crown" "ups" "2" "CMU-SP1200IEC" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/1014 + "Cyber Power Systems" "ups" "1" "550SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "725SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "CPS1100AVR" "" "powerpanel" @@ -213,9 +226,12 @@ "Cyber Power Systems" "ups" "1" "PR2200" "" "powerpanel" "Cyber Power Systems" "ups" "1" "Power99" "" "genericups upstype=7" "Cyber Power Systems" "ups" "2" "AE550" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "CP1000AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "BL1250U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/battery-backup/bl1250u/ https://github.com/networkupstools/nut/issues/1012 +"Cyber Power Systems" "ups" "2" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "2" "CP850PFCLCD" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/605 +"Cyber Power Systems" "ups" "2" "CP1500PFCLCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/cp1500pfclcd/ https://github.com/networkupstools/nut/issues/520 "Cyber Power Systems" "ups" "2" "CP900AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS685AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS800AVR" "USB" "usbhid-ups" @@ -231,9 +247,14 @@ "Cyber Power Systems" "ups" "3" "CP825AVR-G / LE825G" "USB" "usbhid-ups" # http://www.cyberpowersystems.com/products/ups-systems/retail-products/LE825G.html "Cyber Power Systems" "ups" "3" "EC350G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/ecologic/ec350g "Cyber Power Systems" "ups" "3" "EC750G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/desktop/ec750g +"Cyber Power Systems" "ups" "3" "EC850LCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/ec850lcd/ https://github.com/networkupstools/nut/issues/622 +"Cyber Power Systems" "ups" "3" "OR1500ERM1U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1338 "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ +"Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 +"Cyber Power Systems" "ups" "3" "RT650EI" "USB" "usbhid-ups" # http://www.cyberpowersystems.de/produkte/backup-usv-serien/rt-serie.html https://github.com/networkupstools/nut/issues/453 +"Cyber Power Systems" "ups" "3" "UT2200E" "USB" "usbhid-ups" # https://www.cyberpower.com/ww/en/product/sku/UT2200E https://github.com/networkupstools/nut/issues/556 +"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ https://github.com/networkupstools/nut/issues/1191 "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR2200LCDRT2U" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD100" "" "snmp-ups" @@ -274,10 +295,19 @@ "Deltec" "ups" "1" "PowerRite Pro II" "" "genericups upstype=15" "Deltec" "ups" "4" "PRM 450/700/1000/1500" "" "upscode2" +"DEXP" "ups" "2" "MIX 850VA" "USB" "blazer_usb langid_fix=0x0409 runtimecal=240,100,720,50 default.battery.voltage.high=2.27 default.battery.voltage.low=1.72" # https://github.com/networkupstools/nut/issues/721 + "Digital Loggers" "pdu" "1" "LPC, EPCR2, DIN" "8 outlets" "powerman-pdu (experimental)" +"DigiTECH" "ups" "2" "Computer 650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/674 (may need longer pollinterval) + "Digitus" "ups" "1" "DN-170014" "USB" "richcomm_usb" # http://www.digitus.info/en/products/professional-network/security-and-surveillance/power-supply/uninterrruptable-power-supplies/ups-uninterruptible-power-systems-dn-170014/section/prof/ "Digitus" "ups" "2" "DN-170020" "" "blazer_ser" +"Digitus" "ups" "2" "DN-170040" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170041" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170040" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170041" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170076" "USB" "nutdrv_qx" # https://www.digitus.info/en/products/network-and-server-cabinets/power-supply/uninterruptible-power-supplies/dn-170076/ https://github.com/networkupstools/nut/issues/948 "Dynamix" "ups" "2" "UPS1700D" "" "blazer_ser" "Dynamix" "ups" "2" "UPS-650VA" "" "blazer_ser" @@ -324,6 +354,7 @@ "Eaton" "ups" "5" "5P" "Serial port" "mge-shut" "Eaton" "ups" "5" "9SX" "Serial port" "mge-shut" "Eaton" "ups" "5" "9PX" "Serial port" "mge-shut" +"Eaton" "ups" "5" "9PX 2000 RT" "USB port" "mge-shut" # https://github.com/networkupstools/nut/issues/540 "Eaton" "ups" "5" "9PX Split Phase 6/8/10 kVA" "Serial port" "mge-shut" "Eaton" "ups" "5" "9PX" "SNMP/Web card" "netxml-ups" "Eaton" "ups" "5" "9PX Split Phase 6/8/10 kVA" "SNMP/Web card" "netxml-ups" @@ -360,10 +391,14 @@ "Electrys" "ups" "2" "UPS 2500" "" "nutdrv_qx or blazer_ser" +"Elsist" "ups" "2" "Nemo2.0 160" "USB" "blazer_usb" # http://www.naicon.com/company/wp-content/uploads/2017/05/Manual-NEMO2.0-Ver.02-Eng.pdf https://github.com/networkupstools/nut/issues/719 + "Emerson" "pdu" "3" "PM3000 metered & switched" "" "snmp-ups" "Energy Sistem" "ups" "2" "(various)" "" "blazer_ser" +"Energy Technologies" "ups" "2" "DPK1/1-3" "Serial" "blazer_ser" # https://www.tensy.ru/podderzhka/dokumentatsiya/ibp-serii-dpk/ https://github.com/networkupstools/nut/issues/762 + "ETA" "ups" "1" "mini+UPS" "WinNT/Upsoft cable" "genericups upstype=7" "ETA" "ups" "1" "mini+UPS PRO" "UPS Explorer cable" "etapro" @@ -379,6 +414,7 @@ "EVER" "ups" "2" "DUO II Pro series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 1-3kVA series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 6-10kVA series" "USB port" "blazer_usb" +"EVER" "ups" "4" "ECO Pro AVR CDS series" "USB port" "usbhid-ups" "Exide" "ups" "1" "NetUPS SE" "" "genericups upstype=15" "Exide" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" @@ -430,6 +466,9 @@ "Grafenthal" "ups" "2" "PR-3000-HS" "SNMP/Web Minislot card (ref 149G0006)" "snmp-ups" # http://grafenthal.de/produkte/usv/online/pr-hs-serie/pr-3000-hs/?L=3et8 +"Greencell" "ups" "2" "Micropower 600" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1080 +"Greencell" "ups" "2" "Micropower 600" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1080 + "Gtec" "ups" "2" "ZP120N-1K / ZP120N-1KS / ZP120N-2K / ZP120N-2KS / ZP120N-3K / ZP120N-3KS" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-6K / ZP120N-6KS / ZP120N-10K-11 / ZP120N-10KS-11" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "USB port" "blazer_usb" @@ -437,6 +476,8 @@ "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "Serial port" "blazer_ser" "Gtec" "ups" "2" "AP160N-1K / AP160LCD-1K-KS / AP160N-2K / AP160LCD-2K-KS / AP160N-3K / AP160LCD-3K-KS / AP160N-6K-PDU / AP160N-10K-PDU" "Serial port" "blazer_ser" +"Guardian" "ups" "2" "LCD 1500 AP (IGA1500LCD)" "Serial" "nutdrv_qx" + "HP" "ups" "1" "PowerTrust 2997A" "HP 5061-2575 cable" "apcsmart" "HP" "ups" "3" "T750 G2" "Serial port" "bcmxcp" "HP" "ups" "3" "T1000 G3" "Serial port" "bcmxcp" @@ -463,6 +504,9 @@ "HPE" "pdu" "5" "Various (SNMP mode)" "" "snmp-ups" "Huawei" "ups" "4" "UPS5000-E" "" "snmp-ups" +"Huawei" "ups" "3" "UPS2000-G and UPS2000-A series" "MODBUS (USB with Linux 5.12+, or Serial RS-232)" "huawei-ups2000" # https://github.com/networkupstools/nut/issues/1066 https://github.com/networkupstools/nut/pull/1198 https://github.com/networkupstools/nut/pull/954 https://github.com/networkupstools/nut/issues/1017 + +"Hunnox" "ups" "2" "HNX-850" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 "IBM" "ups" "5" "Various" "USB port" "usbhid-ups" "IBM" "ups" "5" "Various" "Serial port" "mge-shut" @@ -493,6 +537,7 @@ "IPMI" "pdu" "1" "" "" "powerman-pdu (experimental)" +"Ippon" "ups" "2" "Back Basic 850 Euro" "USB" "blazer_usb (experimental)" # https://github.com/networkupstools/nut/issues/802 "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "" "blazer_ser" "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Back Comfo Pro 600/800" "" "blazer_ser" @@ -515,6 +560,8 @@ "Kanji" "ups" "1" "800 VA" "USB" "nutdrv_atcl_usb" "Kebo" "ups" "2" "1200D/D Series" "" "blazer_ser" +"Kebo" "ups" "2" "UPS-1000D (UPS-1000VA)" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/981 +"Kebo" "ups" "2" "UPS-650VA" "USB" "megatec_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K TOWER" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K XL TOWER" "" "blazer_usb" @@ -575,6 +622,8 @@ "Liebert" "ups" "1" "GXT2-3000RT230" "" "liebert-esp2 (experimental)" "Liebert" "ups" "2" "PowerSure Personal XT" "USB" "usbhid-ups" "Liebert" "ups" "2" "PowerSure PSA" "USB" "usbhid-ups" +"Liebert" "ups" "2" "PowerSure PSA 500" "USB" "usbhid-ups" +"Liebert" "ups" "2" "PowerSure PSA500MT3-230U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/601 "Liebert" "ups" "2" "PowerSure PSI 1440" "USB" "usbhid-ups" # http://www.emersonnetworkpower.com/en-US/Products/ACPower/Pages/LiebertPowerSurePSILineInteractiveUPS10003000VA.aspx "LNXI" "pdu" "1" "Icebox" "10 outlets" "powerman-pdu (experimental)" @@ -822,6 +871,10 @@ "Nitram" "ups" "1" "Elite 2002" "" "genericups upstype=16" "Nitram" "ups" "1" "Elite 2005" "" "powerpanel" +"nJoy" "ups" "2" "Keen 600" "USB" "blazer_ser port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 +"nJoy" "ups" "2" "Keen 600" "USB" "nutdrv_qx port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 +"nJoy" "ups" "3" "Aten PRO 3000" "SNMP" "snmp-ups" # At least basic status is served; https://www.njoy.global/product/aten-pro-3000 + "Novex" "ups" "1" "NUPS-650" "USB" "blazer_usb protocol=megatec" # http://komp.1k.by/periphery-ups/novex/Novex_NUPS_650-130052.html "Numeric" "ups" "2" "3000 SW" "" "blazer_ser" @@ -849,6 +902,7 @@ "Online" "ups" "2" "Zinto A" "" "blazer_usb" "Online" "ups" "1" "Zinto D" "" "optiups" "Online" "ups" "2" "Yunto YQ450" "" "blazer_usb" +"Online" "ups" "2" "Xanto S700" "/dev/ttyUSB0" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1279 "OnLite" "ups" "2" "AQUA" "50" "blazer_ser" @@ -860,6 +914,7 @@ "Orvaldi Power Protection" "ups" "2" "750 / 900SP" "" "blazer_usb" "Phasak" "ups" "2" "400VA / 600VA" "" "blazer_ser" +"Phasak" "ups" "2" "9465 or P6N" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1187 => Voltronic-QS-Hex protocol detected "PhoenixContact" "ups" "4" "QUINT-UPS/24DC" "2320461" "phoenixcontact_modbus" #https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 @@ -889,8 +944,14 @@ "Powercom" "ups" "5" "(various)" "USB (2009 models, product id: 00a?)" "usbhid-ups (experimental)" "Powercom" "ups" "5" "BNT-xxxAP" "USB (product id: 0004)" "usbhid-ups (experimental)" "Powercom" "ups" "1" "BNT-xxxAP" "USB (product id: 0001)" "usbhid-ups (experimental)" +"Powercom" "ups" "3" "RPT-600AP" "USB" "usbhid-ups" # http://pcmups.com.tw/eA/html/product/show.php?num=226&root=13&kind=105&page=1&keyword= https://github.com/networkupstools/nut/issues/633 +"Powercom" "ups" "3" "Raptor 2000" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/633 "Powercool" "ups" "1" "350VA to 1600VA" "USB" "nutdrv_atcl_usb" +"Powercool" "ups" "2" "650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 +"Powercool" "ups" "2" "650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 bus=001 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor norating noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 +"Powercool" "ups" "2" "1500VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 - e.g. battery percentage is not being returned +"Powercool" "ups" "2" "2000VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 "POWEREX" "ups" "2" "VI 1000 LED" "" "blazer_usb" @@ -919,6 +980,17 @@ "PowerWalker" "ups" "2" "Online VFI 1000RT/1500RT/2000RT/3000RT/6000RT/10000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en +"PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 +"PowerWalker" "ups" "2" "VI 650 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 +"PowerWalker" "ups" "2" "VI 650/850 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 +"PowerWalker" "ups" "2" "VI 2200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/756 +"PowerWalker" "ups" "2" "VI 1200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "2" "VI 2200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 +"PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 +"PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 +"PowerWalker" "ups" "2" "VI 750T/HID" "USB" "usbhid-ups" # https://powerwalker.com/?page=select&cat=VI_THID&lang=en https://github.com/networkupstools/nut/issues/774 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" @@ -999,12 +1071,17 @@ "Rucelf" "ups" "2" "Rucelf UPOII-3000-96-EL" "" "blazer_ser" # http://www.rucelf.ua/en/catalog/upoii-3000-96-el/ +"Salicru" "ups" "2" "SPS ONE 700VA" "USB" "blazer_usb" +"Salicru" "ups" "2" "SPS ONE 2000VA" "USB" "nutdrv_qx" # https://www.salicru.com/en/ups/sps-one.html https://github.com/networkupstools/nut/issues/554 "Salicru" "ups" "2" "SPS 650/850 HOME" "usb" "usbhid-ups (experimental)" "Salicru" "ups" "2" "SLC TWIN PRO2" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/450 "Salicru" "ups" "2" "SLC-1500-TWIN PRO3" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/1142 "Salicru" "ups" "2" "SLC TWINPRO3" "usb" "usbhid-ups (experimental)" "Salicru" "ups" "2" "SLC TWIN RT3" "usb" "usbhid-ups (experimental)" +"Santak" "ups" "2" "Castle C*K" "Serial" "blazer_ser" # https://github.com/networkupstools/nut/issues/1039 +"Santak" "ups" "2" "MT*-PRO" "Serial" "blazer_ser" # https://github.com/networkupstools/nut/issues/1039 + "Siemens" "ups" "4" "SITOP UPS500" "serial" "nutdrv_siemens_sitop (experimental, untested)" "Siemens" "ups" "4" "SITOP UPS500" "USB" "nutdrv_siemens_sitop (experimental)" @@ -1065,11 +1142,13 @@ "Sweex" "ups" "2" "INTELLIGENT UPS 1500VA P220" "USB" "blazer_usb (USB ID 0665:5161)" # http://www.sweex.com/en/notebook-pc-accessoires/ups/PP220/ "Syndome" "ups" "2" "Era 500VA" "USB" "blazer_usb" +"Syndome" "ups" "2" "Atom LCD-1000" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 "Sysgration" "ups" "2" "UPGUARDS Pro650" "" "blazer_ser" "Tecnoware" "ups" "2" "Easy Power 1200" "" "blazer_ser" -"Tecnoware" "ups" "2" "UPS ERA LCD 0.65" "" "blazer_usb langid_fix=0x409" +"Tecnoware" "ups" "2" "UPS ERA LCD 0.65" "USB" "blazer_usb langid_fix=0x409" +"Tecnoware" "ups" "2" "UPS ERA PLUS 1100" "USB" "blazer_usb" # https://www.tecnoware.com/Prodotti/FGCERAPL1100/ups-era-plus-1100.aspx https://github.com/networkupstools/nut/issues/747 "Tripp Lite" "ups" "1" "(various)" "Lan 2.2 interface - black 73-0844 cable" "genericups upstype=5" "Tripp Lite" "ups" "2" "1500 LCD" "USB" "usbhid-ups" @@ -1171,7 +1250,8 @@ "Tripp Lite" "ups" "3" "SMX3000RT2UTAA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4396 "Tripp Lite" "ups" "3" "SMX3000XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2694 "Tripp Lite" "ups" "3" "SMX3000XLRT2UA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5658 -"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (protocol 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2691 +"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" # https://www.tripplite.com/support/product/part-number/SMX500RT1U https://github.com/networkupstools/nut/pull/584 +"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (newer; protocol/product ID 3005)" "usbhid-ups" # https://www.tripplite.com/support/product/part-number/SMX500RT1U "Tripp Lite" "ups" "3" "SMX750SLT" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3021 "Tripp Lite" "ups" "3" "SU750RTXL2U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3194 "Tripp Lite" "ups" "3" "SU750RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5070 @@ -1222,6 +1302,8 @@ "UPSonic" "ups" "2" "PrOffice 650" "USB" "blazer_usb" "UPSonic" "ups" "2" "DS-800" "USB" "blazer_usb" +"V7" "ups" "2" "UPS1RM2U1500-1E" "USB" "blazer_usb" # http://www.v7world.com/uk/ups-1500va-rack-mount-2u-eu.html https://github.com/networkupstools/nut/issues/716 + "Various" "ups" "4" "(various)" "SEC protocol" "gamatronic" "Various" "ups" "1" "(various)" "Generic RUPS model" "genericups upstype=4" "Various" "ups" "1" "(various)" "Generic RUPS 2000 (Megatec M2501 cable)" "genericups upstype=21" @@ -1252,6 +1334,8 @@ "Voltronic Power" "ups" "2" "Frigate TX 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "USB" "nutdrv_qx" +"Voltronic Power" "ups" "2" "Galleon X9-RT LCD-1-3K" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Voltronic Power" "ups" "2" "Galleon X9-RT LCD-1-3K" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 "Voltronic Power" "ups" "2" "Imperial 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Imperial 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Prosine 800" "Serial" "nutdrv_qx" diff --git a/docs/.gitignore b/docs/.gitignore index 00076fcd45..555adc4b4a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,14 +1,24 @@ +# Intended build products /*.chunked/ /*.html /*.pdf + +# Temporary build resources (can come from OS) /docbook-xsl.css + +# Intermediate files from docs-building /ups-html.txt /user-manual.xml /developer-guide.xml +/packager-guide.xml +/solaris-usb.xml +/cables.xml /docinfo.xml /FAQ.xml /nut-dmf.xml /nut-names.xml + +# Temporary stuff nut.dict.sorted /*.bak /*.bak-* diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 59f0ba6105..9c59f100b4 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -177,8 +177,8 @@ Besides, if upsmon were rolled into upsd, upsd would get even bigger than it is now. You'd have one less process, but the RAM consumption would be pretty close to now. -See the "Data Room" section in docs/config-notes.txt for more configuration -ideas and explanations. +See the "Data Room" section in link:docs/config-notes.txt[] for +more configuration ideas and explanations. *Answer 2* @@ -305,7 +305,10 @@ that point. You *want* the system to die without reaching the part where the kernel tells it to shut down. A possible script might look like this: +------ # other shutdown stuff here (mount -o remount,ro ...) + # `upsmon -K` if available on still mounted filesystems + # at this point is more portable than the `test` below if (test -f /etc/killpower) then @@ -318,6 +321,7 @@ might look like this: fi halt -p +------ The other solution is to change your BIOS setting to "always power on" instead of "last state", assuming that's possible. @@ -349,14 +353,15 @@ cat some magic characters at /dev/adb to enable "server mode". This would instruct the system to reboot while unattended. From Usenet post <6boftzxz51.fsf@ecc-office.sp.cs.cmu.edu>: - +------ # Send packet over the ADB bus to the PowerMac CUDA chip # telling it to reboot automatically when power is restored # after a power failure. cat /etc/local/autoboot.adb > /dev/adb - autoboot.adb contains these three bytes (in hex): 01 13 01 + # autoboot.adb contains these three bytes (in hex): 01 13 01 +------ Later PowerPC Macs with a PMU and the appropriate kernel driver can achieve the same effect with the following command: @@ -442,7 +447,7 @@ update your startup scripts so it uses this procedure on your next boot. If you like this, you'll probably also find the chroot process to -be useful and interesting. See security.txt for more details. +be useful and interesting. See link:security.txt[] for more details. == What's the point of that 'security domains' concept above? @@ -452,8 +457,8 @@ to that one user account. Direct access to the serial device is not possible, since that is owned by another user. There is also the possibility of running the drivers and upsd in a -chroot jail. See the chroot.txt provided in the source -distribution for an example implementation. +chroot jail. See the chroot option in link:security.txt[], `upsd` +and driver documentation. Why give would-be vandals any sort of help? @@ -465,8 +470,8 @@ using this technique. You probably don't want to do this, since it doesn't maximize your runtime on battery. Assuming you have a good reason for it (see -the next entry), then look at scheduling.txt or the upssched(8) man -page for some ideas. +the next entry), then look at link:scheduling.txt[] or the +linkman:upssched[8] man page for some ideas. ///////////////////////////////////////////////////////////////// TODO: figure out how to link to the upssched man page above. @@ -494,7 +499,7 @@ with no visible interruption in service. If you purposely shut down early, you guarantee an interruption in service by bringing down the box. -See upssched.txt for information on how you can shutdown early if +See link:upssched.txt[] for information on how you can shutdown early if this is what you really want to do. == The CGI programs report 'access to that host is not authorized' -- what's going on? @@ -544,13 +549,37 @@ There are several driver to support USB models. - usbhid-ups supports various manufacturers complying to the HID Power Device Class (PDC) standard, - tripplite_usb supports various older Tripp-Lite units (with USB ProductID 0001) - bcmxcp_usb supports various Powerware units, -- nutdrv_qx and blazer_usb support various manufacturers that use the Megatec / Q1 protocol. +- blazer_usb supports various manufacturers that use the Megatec / Q1 protocol. +- nutdrv_qx supports various manufacturers that use the Megatec / Q* protocol + family. This is the driver slated to receive all further development in this + area, it was specially designed to support many more sub-drivers and has + added a lot over time, so please do try it first nowadays. Refer to the 'driver-name' (8) man page for more information. You can also consult the Hardware Compatibility List (HCL) and filter on USB: http://www.networkupstools.org/stable-hcl.html?connection=USB +== My USB UPS has a bogus Vendor ID 0x0001 and Product ID 0x0000, what driver supports it? + +Unfortunately, many devices are made without registering as a Vendor with +the corresponding standards body, and use generic USB chips for interfacing +with a computer (roughly similar to using a network interface card with a +random MAC address and PCI ID, and thus poorly identifiable device specifics +needed to automatically load some certain driver). Often they also lack a +unique serial number field, so monitoring several devices is problematic. + +One frequent case is with devices identifying as "Fry's Electronics" and/or +"MEC0003", if those data are served at all, or plain "0001/0000" in ID field. +In some cases they are accompanied by "UPSmart" software with a "MEGA(USB)" +connection option that works for Windows users. + +Your best bet is to search for community discussions of issues on NUT GitHub +at https://github.com/networkupstools/nut/issues?q=is%3Aissue and try options +there. Devices with these chips were known to connect with drivers for such +unrelated protocols as Megatec Q* (different sub-drivers, often `fabula` or +`hunnox`), ATCL, or USB-HID. + == My USB UPS is supported but doesn't work! On Linux, udev rules are provided to set the correct permissions on device @@ -604,6 +633,79 @@ There is a rudimentary locking mechanism in NUT, but there is a chance that the packages might not use the same directory as the NUT default, and the conflict will be reported by the kernel. +== Why does my (Eaton 5E) USB UPS on Linux connect but quickly disconnects soon? + +This issue was extensively investigated by NUT community members in +https://github.com/networkupstools/nut/issues/630 and resulted in a +chain of distribution bugs logged such as +https://bugzilla.redhat.com/show_bug.cgi?id=1715504 + +The gist of it is that some versions of Linux kernel used an "USB HID quirk" +for certain devices, see Linux kernel source `drivers/hid/hid-quirks.c` file, +including MGE/Eaton vendor ID (0x0463) based on an older device a contributor +had issues with. Firmware in newer devices no longer had the bug which needed +the "quirk" and misbehaved when it was enabled. While newer (and much older) +Linux kernels should not have the problem, with the quirk removed according +to https://lkml.org/lkml/2018/11/26/580 having the issue in the field really +depends on the combination of Linux kernel and device firmware that meet. + +Either way, it seems that problematic combinations preclude Linux from seeing +the device as a `hid-generic` first, to hand it over to a NUT driver. + +This quirk can be tuned with a kernel boot parameter (via GRUB etc.): + + usbhid.quirks=0x0463:0xffff:0x08 + +to re-enable the NOGET quirk. + +For context, according to https://bugzilla.redhat.com/show_bug.cgi?id=1875532 +the symptoms for the problem look like this: + + # Plug in the UPS and observe the dmesg logs, + # the following continuously appears: + [ 93.568082] usb 1-6: new full-speed USB device number 9 using xhci_hcd + [ 94.311469] usb 1-6: New USB device found, idVendor=0463, idProduct=ffff, bcdDevice= 0.01 + [ 94.311475] usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0 + [ 94.311483] usb 1-6: Product: 5E + [ 94.311486] usb 1-6: Manufacturer: EATON + [ 96.269989] hid-generic 0003:0463:FFFF.000A: hiddev96,hidraw2: USB HID v1.10 Device [EATON 5E] on usb-0000:00:14.0-6/input0 + [ 107.369425] hid-generic 0003:0463:FFFF.000A: usb_submit_urb(ctrl) failed: -1 + [ 107.369469] hid-generic 0003:0463:FFFF.000A: timeout initializing reports + [ 112.828826] usb 1-6: USB disconnect, device number 9 + [ 113.284452] usb 1-6: new full-speed USB device number 10 using xhci_hcd + [ 114.027693] usb 1-6: New USB device found, idVendor=0463, idProduct=ffff, bcdDevice= 0.01 + [ 114.027698] usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0 + [ 114.027701] usb 1-6: Product: 5E + [ 114.027704] usb 1-6: Manufacturer: EATON + [ 115.984222] hid-generic 0003:0463:FFFF.000B: hiddev96,hidraw2: USB HID v1.10 Device [EATON 5E] on usb-0000:00:14.0-6/input0 + [ 126.825756] hid-generic 0003:0463:FFFF.000B: usb_submit_urb(ctrl) failed: -1 + [ 126.825775] hid-generic 0003:0463:FFFF.000B: timeout initializing reports + [ 132.527809] usb 1-6: USB disconnect, device number 10 + +A similar report on the driver side may look like: + + usbhid-ups[4554]: libusb_get_report: Input/output error + upsd[4591]: Data for UPS [eaton] is stale - check driver + usbhid-ups[4554]: Can't claim USB device [0463:ffff]: No such file or directory + upsd[4591]: Can't connect to UPS [eaton] (usbhid-ups-eaton): No such file or directory + upsmon[5148]: Poll UPS [eaton@localhost] failed - Driver not connected + upsmon[5148]: Communications with UPS eaton@localhost lost + +Other similar looking issues may include improper setup of udev, upower +and similar frameworks to hand over the device from the OS to a driver +daemon; competition with other software probing USB devices (ModemManager +was mentioned in this context), including running several copies of the +NUT drivers trying to use same port (e.g. one started by services and +another manually as you tried to debug the problems). + +Software quirks aside, please do test with a different USB cable and/or port +on the computer. These were known to cause grief beyond what can be fixed +with a few key words ;) + +Finally, sometimes the issue is on the OS side (and/or USB chipset), to +the point that the USB driver can not be unloaded and re-attached until +you power cycle the system. + == Why doesn't my package work? Or a variation like... @@ -940,6 +1042,10 @@ they won't be stuck in the halted state with the UPS running on line power. Implement this by modifying your shutdown script like this: +------ + # `upsmon -K` if available on still mounted filesystems + # at this point is more portable than the `test` below + if (test -f /etc/killpower) then /sbin/upsdrvctl shutdown @@ -949,3 +1055,4 @@ Implement this by modifying your shutdown script like this: # uh oh, we never got shut down! (power race?) reboot fi +------ diff --git a/docs/Makefile.am b/docs/Makefile.am index 5bbf6df0c9..7f8cbbba21 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -87,7 +87,7 @@ doc: @DOC_BUILD_LIST@ # This target can be called by developers to go around the configure # script choices at their risk (e.g. missing tools are possible): -docs: pdf html-single html-chunked man +docs: pdf html-single html-chunked man-man html-man all-docs: docs @@ -130,7 +130,10 @@ check-html-chunked: $(ASCIIDOC_HTML_CHUNKED) # Note: usually the results from man-page check will be reported twice: # once as a SUBDIRS child makefile, and once via DOC_CHECK_LIST expansion -check-man: +# Note: default `make all` in the man directory caters to drivers etc. +# chosen during configure script execution. The "all-man" and "all-html" +# rules build everything documented. +check-man all-man man-man all-html html-man: cd $(top_builddir)/docs/man/ && $(MAKE) -f Makefile $@ man: @@ -159,15 +162,16 @@ solaris-usb.html solaris-usb.chunked solaris-usb.pdf: solaris-usb.txt asciidoc.c # generating the chunked HTML. In this case, export the environment # variable ASCIIDOC_VERBOSE to "-v", ie: # $ ASCIIDOC_VERBOSE=-v make -A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) --attribute icons \ +A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) \ + --attribute=icons \ --xsltproc-opts="--nonet" \ --xsltproc-opts="--stringparam nut.localdate \"`TZ=UTC date +%Y-%m-%d`\"" \ --xsltproc-opts="--stringparam nut.localtime \"`TZ=UTC date +%H:%M:%S`\"" \ --xsltproc-opts="--stringparam nut.nutversion \"@PACKAGE_VERSION@\"" \ - --attribute iconsdir=$(srcdir)/images \ + --attribute=iconsdir="$(srcdir)/images" \ --attribute=badges \ --attribute=external_title \ - --attribute tree_version=@TREE_VERSION@ \ + --attribute=tree_version="@TREE_VERSION@" \ -a toc -a numbered --destination-dir=$${A2X_OUTDIR} # NOTE: a2x newer than 8.6.8 says "--destination-dir" is only valid for HTML. # As of version 8.6.9 it lies, and the argument is required for our distcheck @@ -187,6 +191,10 @@ A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) --attribute icons \ # As a brutal workaround for the former problem, we chmod. For second one we # might try magic with .SEQUENTIAL recipe hints, but that is gmake-dependent. +# Note that empirically it treats "destination-dir" as the source root for +# PDF generation (even though it claims the argument is ignored for non-HTML +# targets) so we have to provide the "images/" in this case. ONLY for PDF! + # Note we only remove the original target (if present), if it is a directory - # e.g. created by "html-chunked" targets. DOCBUILD_BEGIN = { \ @@ -194,6 +202,9 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + case "$${A2X_OUTDIR}" in \ + tmp/pdf.*) ln -s ../../images "./$${A2X_OUTDIR}" ;; \ + esac; \ else A2X_OUTDIR='.' ; fi; \ if test -s "${builddir}/docbook-xsl.css" \ && test -r "${builddir}/docbook-xsl.css" \ diff --git a/docs/asciidoc.txt b/docs/asciidoc.txt index 6977051524..1f7b391e05 100644 --- a/docs/asciidoc.txt +++ b/docs/asciidoc.txt @@ -6,8 +6,8 @@ Charles Lepple Intro ----- -See http://www.methods.co.nz/asciidoc/[The AsciiDoc Manual] for more -information. +See the https://asciidoc-py.github.io/userguide.html[AsciiDoc User Guide] +for more information. Works in Progress ----------------- diff --git a/docs/ci-farm-lxc-setup.txt b/docs/ci-farm-lxc-setup.txt index 2ab82a4fe5..28fd13d111 100644 --- a/docs/ci-farm-lxc-setup.txt +++ b/docs/ci-farm-lxc-setup.txt @@ -33,14 +33,16 @@ Common preparations * Prepare an LVM partition (or preferably some other tech like ZFS) as `/srv/libvirt` and create a `/srv/libvirt/rootfs` to hold the containers -* Prepare `/home/abuild` on the host system (preferably in ZFS with dedup); +* Prepare `/home/abuild` on the host system (preferably in ZFS with + lightweight compression like lz4 -- and optionally, only if the amount + of available system RAM permits, with deduplication; otherwise avoid it); account user and group ID numbers are `399` as on the rest of the CI farm (historically, inherited from OBS workers) ** It may help to generate an ssh key without a passphrase for `abuild` that it would trust, to sub-login from CI agent sessions into the container. Then again, it may be not required if CI logs into the - host by SSH using authorized_keys and an SSH Agent, and the inner + host by SSH using `authorized_keys` and an SSH Agent, and the inner ssh client would forward that auth channel to the original agent. + ------ @@ -75,7 +77,7 @@ Setup a container Note that completeness of qemu CPU emulation varies, so not all distros can be installed, e.g. "s390x" failed for both debian10 and debian11 to -set up the openssh-server package, or once even to run /bin/true (seems +set up the `openssh-server` package, or once even to run `/bin/true` (seems to have installed an older release though, to match the outdated emulation?) While the `lxc-create` tool does not really specify the error cause and @@ -83,19 +85,22 @@ deletes the directories after failure, it shows the pathname where it writes the log (also deleted). Before re-trying the container creation, this file can be watched with e.g. `tail -F /var/cache/lxc/.../debootstrap.log` -NOTE: You can find the list of LXC "template" definitions on your system +[NOTE] +====== +You can find the list of LXC "template" definitions on your system by looking at the contents of the `/usr/share/lxc/templates/` directory, e.g. a script named `lxc-debian` for the "debian" template. You can see further options for each "template" by invoking its help action, e.g.: -+ ------ :; lxc-create -t debian -h ------ +====== * Install containers like this: + ------ -:; lxc-create -n jenkins-debian11-mips64el -P /srv/libvirt/rootfs -t debian -- \ +:; lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian11-mips64el -t debian -- \ -r bullseye -a mips64el ------ ** to specify a particular mirror (not everyone hosts everything -- @@ -107,21 +112,24 @@ further options for each "template" by invoking its help action, e.g.: + ------ :; MIRROR="http://ftp.br.debian.org/debian/" \ - lxc-create -n jenkins-debian10-mips -P /srv/libvirt/rootfs -t debian -- \ + lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian10-mips -t debian -- \ -r buster -a mips ------ ** ...or for EOLed distros, use the archive, e.g.: + ------ :; MIRROR="http://archive.debian.org/debian-archive/debian/" \ - lxc-create -n jenkins-debian8-s390x -P /srv/libvirt/rootfs -t debian -- \ + lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian8-s390x -t debian -- \ -r jessie -a s390x ------ ** ...Alternatively, other distributions can be used (as supported by your LXC scripts, typically in `/usr/share/debootstrap/scripts`), e.g. Ubuntu: + ------ -:; lxc-create -n jenkins-ubuntu1804-s390x -P /srv/libvirt/rootfs -t ubuntu -- \ +:; lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-ubuntu1804-s390x -t ubuntu -- \ -r bionic -a s390x ------ ** For distributions with a different packaging mechanism from that on the @@ -132,15 +140,19 @@ further options for each "template" by invoking its help action, e.g.: Otherwise, you risk seeing something like this: + ------ -root@debian:~# lxc-create -n jenkins-centos7-x86-64 -P /srv/libvirt/rootfs \ - -t centos -- -R 7 -a x86_64 -Host CPE ID from /etc/os-release: +root@debian:~# lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-centos7-x86-64 -t centos -- \ + -R 7 -a x86_64 + +Host CPE ID from /etc/os-release: 'yum' command is missing -lxc-create: jenkins-centos7-x86-64: lxccontainer.c: create_run_template: 1616 Failed to create container from template -lxc-create: jenkins-centos7-x86-64: tools/lxc_create.c: main: 319 Failed to create container jenkins-centos7-x86-64 +lxc-create: jenkins-centos7-x86-64: lxccontainer.c: + create_run_template: 1616 Failed to create container from template +lxc-create: jenkins-centos7-x86-64: tools/lxc_create.c: + main: 319 Failed to create container jenkins-centos7-x86-64 ------ + - Note also that with such "third-party" distributions you may face other +Note also that with such "third-party" distributions you may face other issues; for example, the CentOS helper did not generate some fields in the `config` file that were needed for conversion into libvirt "domxml" (as found by trial and error, and comparison to other `config` files): @@ -150,7 +162,7 @@ lxc.uts.name = jenkins-centos7-x86-64 lxc.arch = x86_64 ------ + - Also note the container/system naming without underscore in "x86_64" -- +Also note the container/system naming without underscore in "x86_64" -- the deployed system discards the character when assigning its hostname. Using "amd64" is another reasonable choice here. @@ -174,13 +186,15 @@ new host reservation! > /tmp/x && virsh define /tmp/x ------ + -NOTE: You may want to tune the default generic 64MB RAM allocation, so your - launched QEMU containers are not OOM-killed as they exceeded their memory - cgroup limit. In practice they do not eat that much resident memory, just - want to have it addressable by VMM, I guess (swap is not very used either), - at least not until active builds start (then it depends on `make` program - parallelism level you allow, e.g. by `MAXPARMAKES` envvar for `ci_build.sh`, - and on the number of Jenkins "executors" assigned to the build agent). +NOTE: You may want to tune the default generic 64MB RAM allocation, + so your launched QEMU containers are not OOM-killed as they exceeded + their memory `cgroup` limit. In practice they do not eat *that much* + resident memory, just want to have it addressable by VMM, I guess + (swap is not very used either), at least not until active builds + start (and then it depends on compiler appetite and `make` program + parallelism level you allow, e.g. by pre-exporting `MAXPARMAKES` + environment variable for `ci_build.sh`, and on the number of Jenkins + "executors" assigned to the build agent). + ** It may be needed to revert the generated "os/arch" to `x86_64` (and let QEMU handle the rest) in the `/tmp/x` file, and re-try the definition: @@ -189,8 +203,9 @@ NOTE: You may want to tune the default generic 64MB RAM allocation, so your :; virsh define /tmp/x ------ -* Then `virsh edit jenkins-debian11-armhf` (and other containers) to bind-mount - the common `/home/abuild`, adding this tag to their "devices": +* Then execute `virsh edit jenkins-debian11-armhf` (and same for other + containers) to bind-mount the common `/home/abuild` location, adding + this tag to their "devices": + ------ @@ -198,8 +213,8 @@ NOTE: You may want to tune the default generic 64MB RAM allocation, so your ------ -** Note that generated XML might not conform to current LXC schema so it - fails validation during save; this can be bypassed with "i" when it asks. +** Note that generated XML might not conform to current LXC schema, so it + fails validation during save; this can be bypassed with `i` when it asks. One such case was however with indeed invalid contents, the "dir:" schema removed by example above. @@ -260,8 +275,14 @@ Shepherd the herd ------ :; for ALTROOT in /srv/libvirt/rootfs/*/rootfs/ ; do \ echo "=== $ALTROOT :" >&2; \ - grep eth0 "$ALTROOT/etc/issue" || ( echo '\S{NAME} \S{VERSION_ID} \n \l@\b ; Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' >> "$ALTROOT/etc/issue" ) ; \ - grep eth0 "$ALTROOT/etc/issue.net" || ( echo '\S{NAME} \S{VERSION_ID} \n \l@\b ; Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' >> "$ALTROOT/etc/issue.net" ) ; \ + grep eth0 "$ALTROOT/etc/issue" || ( printf '%s %s\n' \ + '\S{NAME} \S{VERSION_ID} \n \l@\b ;' \ + 'Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' \ + >> "$ALTROOT/etc/issue" ) ; \ + grep eth0 "$ALTROOT/etc/issue.net" || ( printf '%s %s\n' \ + '\S{NAME} \S{VERSION_ID} \n \l@\b ;' \ + 'Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' \ + >> "$ALTROOT/etc/issue.net" ) ; \ groupadd -R "$ALTROOT" -g 399 abuild ; \ useradd -R "$ALTROOT" -u 399 -g abuild -M -N -s /bin/bash abuild \ || useradd -R "$ALTROOT" -u 399 -g 399 -M -N -s /bin/bash abuild \ @@ -277,7 +298,9 @@ Shepherd the herd } ; \ if [ -s "$ALTROOT/etc/ssh/sshd_config" ]; then \ grep 'AcceptEnv \*' "$ALTROOT/etc/ssh/sshd_config" || ( \ - ( echo ""; echo "# For CI: Allow passing any envvars:"; echo 'AcceptEnv *' ) \ + ( echo "" ; \ + echo "# For CI: Allow passing any envvars:"; \ + echo 'AcceptEnv *' ) \ >> "$ALTROOT/etc/ssh/sshd_config" \ ) ; \ fi ; \ @@ -287,10 +310,11 @@ Shepherd the herd Note that for some reason, in some of those other-arch distros `useradd` fails to find the group anyway; then we have to "manually" add them. -* Let the host know names/IPs of containers you assigned: +* Let the host know and resolve the names/IPs of containers you assigned: + ------ -:; grep -v '#' /etc/lxc/dnsmasq-hosts.conf | while IFS=, read N I ; do \ +:; grep -v '#' /etc/lxc/dnsmasq-hosts.conf \ + | while IFS=, read N I ; do \ getent hosts "$N" >&2 || echo "$I $N" ; \ done >> /etc/hosts ------ @@ -307,8 +331,9 @@ order to conserve space and run-time stress. Still, if there are significant version outliers (such as using an older distribution due to vCPU requirements), it can be installed fully just -to ensure non-regression -- that when adapting Makefile rule definitions -to modern tools, we do not lose ability to build with older ones. +to ensure non-regression -- e.g. that when adapting `Makefile` rule +definitions or compiler arguments to modern toolkits, we do not lose +the ability to build with older ones. For this, `chroot` from the host system can be used, e.g. to improve the interactive usability for a population of Debian(-compatible) containers @@ -324,15 +349,17 @@ may be not yet configured or still struggling to access the Internet): Similarly for `yum`-managed systems (CentOS and relatives), though specific package names can differ, and additional package repositories may need to -be enabled first (see link:config-prereqs.txt for details). +be enabled first (see link:config-prereqs.txt[config-prereqs.txt] for more +details such as recommended package names). Note that technically `(sudo) chroot ...` can also be used from the CI worker account on the host system to build in the prepared filesystems without the -overhead of running containers and several copies of Jenkins `agent.jar`. +overhead of running containers as complete operating environments with any +standard services and several copies of Jenkins `agent.jar` in them. -Also note that set-up of some packages, including the `ca-certificates` and -the JDK/JRE, require that the `/proc` filesystem is usable in the chroot. -This can be achieved with e.g.: +Also note that externally-driven set-up of some packages, including the +`ca-certificates` and the JDK/JRE, require that the `/proc` filesystem +is usable in the chroot environment. This can be achieved with e.g.: ------ :; for ALTROOT in /srv/libvirt/rootfs/*/rootfs/ ; do \ for D in proc ; do \ @@ -346,34 +373,69 @@ This can be achieved with e.g.: TODO: Test and document a working NAT and firewall setup for this, to allow SSH access to the containers via dedicated TCP ports exposed on the host. + Troubleshooting ~~~~~~~~~~~~~~~ -Q: Container won't start, its `virsh console` says something like: +* Q: Container won't start, its `virsh console` says something like: + ------ Failed to create symlink /sys/fs/cgroup/net_cls: Operation not permitted ------ - A: According to https://bugzilla.redhat.com/show_bug.cgi?id=1770763 -(skip to the end for summary) this can happen when a newer Linux host system -with cgroupsv2 runs an older guest distro that only knows about cgroupsv1, -such as CentOS 7. One workaround is to ensure that the guest systemd does -not try to join host facilities, by setting an explicit empty list: + (skip to the end for summary) this can happen when a newer Linux + host system with `cgroupsv2` capabilities runs an older guest distro + which only knows about `cgroupsv1`, such as when hosting a CentOS 7 + container on a Debian 11 server. +** One workaround is to ensure that the guest `systemd` does not try to + "join" host facilities, by setting an explicit empty list for that: + ------ :; echo 'JoinControllers=' >> "$ALTROOT/etc/systemd/system.conf" ------ +** Another approach is to upgrade `systemd` related packages in the guest + container. This may require additional "backport" repositories or + similar means, possibly maintained not by distribution itself but by + other community members, and arguably would logically compromise the + idea of non-regression builds in the old environment "as is". + +* Q: Server was set up with ZFS as recommended, and lots of I/O hit the + disk even when application writes are negligible ++ +A: This was seen on some servers and generally derives from data layout + and how ZFS maintains the tree structure of blocks. A small application + write (such as a new log line) means a new empty data block allocation, + an old block release, and bubble up through the whole metadata tree to + complete the transaction (grouped as TXG to flush to disk). ++ +** One solution is to use discardable build workspaces in RAM-backed + storage like `/dev/shm` (`tmpfs`) on Linux, or `/tmp` (`swap`) on + illumos hosting systems, and only use persistent storage for the home + directory with `.ccache` and `.gitcache-dynamatrix` directories. +** Another solution is to reduce the frequency of TXG sync from modern + default of 5 sec to conservative 30-60 sec. Check how to set the + `zfs_txg_timeout` on your platform. + Connecting Jenkins to the containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------ + +To properly cooperate with the +https://github.com/networkupstools/jenkins-dynamatrix[jenkins-dynamatrix] +project driving regular NUT CI builds, each build environment should be +exposed as an individual agent with labels describing its capabilities. -To cooperate with the jenkins-dynamatrix driving NUT CI builds, each build -environment should be exposed as an individual agent with labels describing -its capabilities. +Agent Labels +~~~~~~~~~~~~ -Labels -^^^^^^ +With the `jenkins-dynamatrix`, agent labels are used to calculate a large +"slow build" matrix to cover numerous scenarios for what can be tested +with the current population of the CI farm, across operating systems, +`make`, shell and compiler implementations and versions, and C/C++ language +revisions, to name a few common "axes" involved. + +Labels for QEMU +^^^^^^^^^^^^^^^ Emulated-CPU container builds are CPU-intensive, so for them we define as few capabilities as possible: here CI is more interested in checking how @@ -384,9 +446,9 @@ which is more efficient to test on native platforms. Still, we are interested in results from different compiler suites, so specify at least one version of each. -NOTE: Currently the NUT Jenkinsfile-dynamatrix only looks at various -`COMPILER` variants for this use-case, disregarding the versions and -just using one that the environment defaults to. +NOTE: Currently the NUT `Jenkinsfile-dynamatrix` only looks at various +`COMPILER` variants for `qemu-nut-builder` use-cases, disregarding the +versions and just using one that the environment defaults to. The reduced set of labels for QEMU workers looks like: @@ -398,8 +460,11 @@ COMPILER=GCC COMPILER=CLANG ARCH64=ppc64le ARCH_BITS=64 ------ +Labels for native builds +^^^^^^^^^^^^^^^^^^^^^^^^ + For contrast, a "real" build agent's set of labels, depending on -presence or known lack of some capabilities, looks like: +presence or known lack of some capabilities, looks something like this: ------ doc-builder nut-builder nut-builder:alldrv nut-builder:DMF NUT_BUILD_CAPS=drivers:all @@ -415,13 +480,19 @@ PYTHON=python2.7 PYTHON=python3.8 ------ Generic agent attributes -^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~ * Name: e.g. `ci-debian-altroot--jenkins-debian10-arm64` (note the pattern for "Conflicts With" detailed below) * Remote root directory: preferably unique per agent, to avoid surprises; e.g.: `/home/abuild/jenkins-nut-altroots/jenkins-debian10-armel` +** Note it may help that the system home directory itself is shared between + co-located containers, so that the `.ccache` or `.gitcache-dynamatrix` + are available to all builders with identical contents +** If RAM permits, the Jenkins Agent working directory may be placed in + a temporary filesystem not backed by disk (e.g. `/dev/shm` on modern + Linux distributions); roughly estimate 300Mb per executor for NUT builds. * Usage: "Only build jobs with label expressions matching this node" @@ -430,7 +501,7 @@ Generic agent attributes ** `PATH+LOCAL` => `/usr/lib/ccache` Where to run agent.jar -^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~ Depending on circumstances of the container, there are several options available to the NUT CI farm: @@ -439,8 +510,9 @@ available to the NUT CI farm: => the container may be exposed as a standalone host for direct SSH access (usually by NAT, exposing SSH on a dedicated port of the host; or by first connecting the Jenkins controller with the host as an SSH Build Agent, and - then calling SSH to the container as a prefix for running the agent), so - ultimately the build `agent.jar` would run in the container. + then calling SSH to the container as a prefix for running the agent; or + by using Jenkins Swarm agents), so ultimately the build `agent.jar` JVM + would run in the container. Filesystem for the `abuild` account may be or not be shared with the host. * Java can not run in the container (crashes on emulated CPU, or is too old @@ -455,18 +527,35 @@ available to the NUT CI farm: * Java is inefficient in the container (operations like un-stashing the source succeed but take minutes instead of seconds) => either of the above +Using Jenkins SSH Build Agents +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is a typical use-case for tightly integrated build farms under common +management, where the Jenkins controller can log by SSH into systems which +act as its build agents. It injects and launches the `agent.jar` to execute +child processes for the builds, and maintains a tunnel to communicate. + Methods below involving SSH assume that you have configured a password-less -key authentication from the host machine to the `abuild` account in container. -This can be an `ssh-keygen` result posted into `authorized_keys`, or a trusted -key passed by a chain of ssh agents from a Jenkins credential for connection -to the container-hoster into the container. +key authentication from the host machine to the `abuild` account in each +guest build environment container. +This can be an `ssh-keygen` result posted into `authorized_keys`, or a +trusted key passed by a chain of ssh agents from a Jenkins Credential +for connection to the container-hoster into the container. +The private SSH key involved may be secured by a pass-phrase, as long as +your Jenkins Credential storage knows it too. +Note that for the approaches explored below, the containers are not +directly exposed for log-in from any external network. * For passing the agent through an SSH connection from host to container, so that the `agent.jar` runs inside the container environment, configure: ** Launch method: "Agents via SSH" -** Host, Credentials, Port: as suitable for accessing the container hoster +** Host, Credentials, Port: as suitable for accessing the container-hoster ++ +NOTE: The container-hoster should have accessed the guest container from + the account used for intermediate access, e.g. `abuild`, so that its + `.ssh/known_hosts` file would trust the SSH server on the container. ** Prefix Start Agent Command: content depends on the container name, but generally looks like the example below to report some info about @@ -486,18 +575,18 @@ ssh jenkins-debian10-amd64 '( java -version & uname -a ; getconf LONG_BIT; getco * The other option is to run the `agent.jar` on the host, for all the network and filesystem magic the agent does, and only execute shell steps in the container. The solution relies on overridden `sh` step - implementation in the jenkins-dynamatrix shared library that uses a + implementation in the `jenkins-dynamatrix` shared library that uses a magic `CI_WRAP_SH` environment variable to execute a pipe into the container. Such pipes can be `ssh` or `chroot` with appropriate host setup described above. + NOTE: In case of ssh piping, remember that the container's -`/etc/ssh/sshd_config` should `AcceptEnv *` and the SSH -server should be restarted after such change. + `/etc/ssh/sshd_config` should `AcceptEnv *` and the SSH + server should be restarted after such configuration change. ** Launch method: "Agents via SSH" -** Host, Credentials, Port: as suitable for accessing the container hoster +** Host, Credentials, Port: as suitable for accessing the container-hoster ** Prefix Start Agent Command: content depends on the container name, but generally looks like the example below to report some info about @@ -514,18 +603,48 @@ echo PING > /dev/tcp/jenkins-debian11-ppc64el/22 && * Node properties / Environment variables: -** `CI_WRAP_SH` => `ssh -o SendEnv='*' "jenkins-debian11-ppc64el" /bin/sh -xe ` +** `CI_WRAP_SH` => ++ +------ +ssh -o SendEnv='*' "jenkins-debian11-ppc64el" /bin/sh -xe +------ + +Using Jenkins Swarm Agents +^^^^^^^^^^^^^^^^^^^^^^^^^^ +This approach allows remote systems to participate in the NUT CI farm by +dialing in and so defining an agent. A single contributing system may be +running a number of containers or virtual machines set up following the +instructions above, and each of those would be a separate build agent. + +Such systems should be "dedicated" to contribution in the sense that +they should be up and connected for days, and sometimes tasks would land. + +Configuration files maintained on the Swarm Agent system dictate which +labels or how many executors it would expose, etc. Credentials to access +the NUT CI farm Jenkins controller to register as an agent should be +arranged with the farm maintainers, and currently involve a GitHub account +with Jenkins role assignment for such access, and a token for authentication. + +The https://github.com/networkupstools/jenkins-swarm-nutci[jenkins-swarm-nutci] +repository contains example code from such setup with a back-up server +experiment for the NUT CI farm, including auto-start method scripts for +Linux systemd and upstart, illumos SMF, and OpenBSD rcctl. Sequentializing the stress -^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Running one agent at a time +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Another aspect of farm management is that emulation is a slow and intensive operation, so we can not run all agents and execute builds at the same time. -The current solution relies on https://github.com/jenkinsci/jenkins/pull/5764 -to allow build agents to conflict with each other -- one picks up a job from -the queue and blocks others from starting; when it is done, another may start. +The current solution relies on +https://github.com/jimklimov/conflict-aware-ondemand-retention-strategy-plugin +to allow co-located build agents to "conflict" with each other -- when one +picks up a job from the queue, it blocks neighbors from starting; when it +is done, another may start. Containers can be configured with "Availability => On demand", with shorter cycle to switch over faster (the core code sleeps a minute between attempts): @@ -540,3 +659,36 @@ cycle to switch over faster (the core code sleeps a minute between attempts): Also, the "executors" count should be reduced to the amount of compilers in that system (usually 2) and so avoid extra stress of scheduling too many emulated-CPU builds at once. + +Sequentializing the git cache access +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As part of the `jenkins-dynamatrix` optional optimizations, the NUT CI +recipe invoked via `Jenkinsfile-dynamatrix` maintains persistent git +reference repositories that can be used to cache NUT codebase (including +the tested commits) and so considerably speed up workspace preparation +when running numerous build scenarios on the same agent. + +Such `.gitcache-dynamatrix` cache directories are located in the build +workspace location (unique for each agent), but on a system with numerous +containers these names can be symlinks pointing to a shared location. + +To avoid collisions with several executors updating the same cache with +new commits, critical access windows are sequentialized with the use of +https://github.com/jenkinsci/lockable-resources-plugin[Lockable Resources +plugin]. On the `jenkins-dynamatrix` side this is facilitated by labels: +------ +DYNAMATRIX_UNSTASH_PREFERENCE=scm-ws:nut-ci-src +DYNAMATRIX_REFREPO_WORKSPACE_LOCKNAME=gitcache-dynamatrix:SHARED_HYPERVISOR_NAME +------ + +* The `DYNAMATRIX_UNSTASH_PREFERENCE` tells the `jenkins-dynamatrix` library + code which checkout/unstash strategy to use on a particular build agent + (following values defined in the library; `scm-ws` means SCM caching + under the agent workspace location, `nut-ci-src` names the cache for + this project); +* The `DYNAMATRIX_REFREPO_WORKSPACE_LOCKNAME` specifies a semi-unique + string: it should be same for all co-located agents which use the same + shared cache location, e.g. guests on the same hypervisor; and it should + be different for unrelated cache locations, e.g. different hypervisors + and stand-alone machines. diff --git a/docs/config-notes.txt b/docs/config-notes.txt index f6994c5942..8313028ca5 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -112,32 +112,35 @@ configure <>. image:images/simple.png[] -On operating systems with service management frameworks (such as Linux systemd -and Solaris/illumos SMF), the driver, data server and monitoring client daemons' -life-cycle is managed respectively by `nut-driver` (multi-instance), `nut-server` -and `nut-monitor` services. These are in turn wrapped by an "umbrella" service -(or systemd "target") conveniently called `nut` which allows to start or stop -those of the bundled services, which are enabled on a particular deployment. +On operating systems with service management frameworks (such as Linux +systemd and Solaris/illumos SMF), the life-cycle of driver, data server +and monitoring client daemons is managed respectively by `nut-driver` +(multi-instance service), `nut-server` and `nut-monitor` services. +These are in turn wrapped by an "umbrella" service (or systemd "target") +conveniently called `nut` which allows to easily start or stop all those +of the bundled services, which are enabled on a particular deployment. [[Driver_configuration]] Driver configuration ~~~~~~~~~~~~~~~~~~~~ -Create one section per UPS in ups.conf +Create one section per UPS in 'ups.conf' -NOTE: The default path for a source installation is `/usr/local/ups/etc`, while -packaged installation will vary. For example, `/etc/nut` is used on Debian and -derivatives, while `/etc/ups` or `/etc/upsd` is used on RedHat and derivatives. +NOTE: The default path for a source installation is `/usr/local/ups/etc`, +while packaged installation will vary. +For example, `/etc/nut` is used on Debian and derivatives, +while `/etc/ups` or `/etc/upsd` is used on RedHat and derivatives. -To find out which driver to use, check the <>, -or `data/driver.list`. +To find out which driver to use, check the +<>, +or `data/driver.list(.in)` source file. Once you have picked a driver, create a section for your UPS in -`ups.conf`. You must supply values for "driver" and "port". +'ups.conf'. You must supply values at least for "driver" and "port". Some drivers may require other flags or settings. The "desc" value is optional, but is recommended to provide a better description of -what your UPS is supporting. +what useful load your UPS is feeding. A typical device without any extra settings looks like this: @@ -146,19 +149,29 @@ A typical device without any extra settings looks like this: port = /dev/ttyS1 desc = "Workstation" -NOTE: USB drivers (usbhid-ups, bcmxcp_usb, tripplite_usb, blazer_usb and -richcomm_usb) are special cases and ignore the 'port' value. +[NOTE] +====== +USB drivers (such as `usbhid-ups` for non-SHUT mode, `nutdrv_qx` for +non-serial mode, `bcmxcp_usb`, `tripplite_usb`, `blazer_usb`, `riello_usb` +and `richcomm_usb`) are special cases and ignore the 'port' value. + You must still set this value, but it does not matter what you set -it to; a common and good practice is to set 'port' to *auto*, but you can -put whatever you like. If you only own one USB UPS, the driver will -find it automatically. If you own more than one, refer to the driver's -manual page for more information on matching a specific device. +it to; a common and good practice is to set 'port' to *auto*, but you +can put whatever you like. + +If you only own one USB UPS, the driver will find it automatically. + +If you own more than one, refer to the driver's manual page for more +information on matching a specific device. +====== References: linkman:ups.conf[5], linkman:nutupsdrv[8], linkman:bcmxcp_usb[8], linkman:blazer[8], +linkman:nutdrv_qx[8], linkman:richcomm_usb[8], +linkman:riello_usb[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] @@ -185,16 +198,21 @@ reference, a successful start of the `usbhid-ups` driver looks like this: If the driver doesn't start cleanly, make sure you have picked the right one for your hardware. You might need to try other drivers -by changing the "driver=" value in ups.conf. +by changing the "driver=" value in 'ups.conf'. Be sure to check the driver's man page to see if it needs any extra -settings in `ups.conf` to detect your hardware. +settings in 'ups.conf' to detect your hardware. If it says `can't bind /var/state/ups/...` or similar, then your state path probably isn't writable by the driver. Check the -<>. +<> vs. the +user account your driver starts as. -After making changes, try the <> step again. +After making changes, try the <> +step again. + +Driver(s) as a service +~~~~~~~~~~~~~~~~~~~~~~ On operating systems with init-scripts managing life-cycle of the operating environment, the `upsdrvctl` program is also commonly used in those scripts. @@ -209,7 +227,7 @@ This can be a big issue on systems which monitor multiple devices, such as big servers with multiple power sources, or administrative workstations which monitor a datacenter full of UPSes. -For this reason, NUT starting with version 2.7.5 supports startup of its +For this reason, NUT starting with version 2.8.0 supports startup of its drivers as independent instances of a `nut-driver` service under the Linux systemd and Solaris/illumos SMF service-management frameworks (corresponding files and scripts may be not pre-installed in packaging for other systems). @@ -223,7 +241,7 @@ disable startup of drivers). In both cases, a service named `nut-driver-enumerator` is registered, and when it is (re-)started it scans the currently defined device sections in -`ups.conf` and the currently defined instances of `nut-driver` service, +'ups.conf' and the currently defined instances of `nut-driver` service, and brings them in sync (adding or removing service instances), and if there were changes -- it restarts the corresponding drivers (via service instances) as well as the data server which only reads the list of sections @@ -268,18 +286,19 @@ of such section names, and the `upsdrvsvcctl` script supports this to map the user-friendly NUT configuration section names to actual service names that it would manage. -References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8] +References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8], +linkman:upsdrvsvcctl[8] Data server configuration (upsd) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Configure upsd, which serves data from the drivers to the clients. +Configure `upsd`, which serves data from the drivers to the clients. -First, edit upsd.conf to allow access to your client systems. By -default, upsd will only listen to localhost port 3493/tcp. If you want +First, edit 'upsd.conf' to allow access to your client systems. By +default, `upsd` will only listen to `localhost` port 3493/tcp. If you want to connect to it from other machines, you must specify each interface you -want upsd to listen on for connections, optionally with a port number. +want `upsd` to listen on for connections, optionally with a port number. LISTEN 127.0.0.1 3493 LISTEN ::1 3493 @@ -287,15 +306,15 @@ want upsd to listen on for connections, optionally with a port number. NOTE: Refer to the NUT user manual <> for information on how to access and secure upsd clients connections. -Next, create upsd.users. For now, this can be an empty file. +Next, create 'upsd.users'. For now, this can be an empty file. You can come back and add more to it later when it's time to -configure upsmon or run one of the management tools. +configure `upsmon` or run one of the management tools. Do not make either file world-readable, since they both hold access control data and passwords. They just need to be readable by the user you created in the preparation process. -The suggested configuration is to chown it to root, chgrp it to the +The suggested configuration is to `chown` it to `root`, `chgrp` it to the group you created, then make it readable by the group. chown root:nut upsd.conf upsd.users @@ -322,15 +341,22 @@ A successful run looks like this: listening on ::1 port 3493 Connected to UPS [eaton]: usbhid-ups-eaton -upsd prints dots while it waits for the driver to respond. Your +`upsd` prints dots while it waits for the driver to respond. Your system may print more or less depending on how many drivers you have and how fast they are. -NOTE: if upsd says that it can't connect to a UPS or that the data -is stale, then your ups.conf is not configured correctly, or you +NOTE: If `upsd` says that it can't connect to a UPS or that the data +is stale, then your 'ups.conf' is not configured correctly, or you have a driver that isn't working properly. You must fix this before going on to the next step. +NOTE: Normally `upsd` requires that at least one driver section is +defined in the 'ups.conf' file, and refuses to start otherwise. +If you intentionally do not have any driver sections defined (yet) +but still want the data server to run, respond and report zero devices +(e.g. on an automatically managed monitoring deployment), you can enable +the `ALLOW_NO_DEVICE true` option in the 'upsd.conf' file. + On operating systems with service management frameworks, the data server life-cycle is managed by `nut-server` service. @@ -343,6 +369,7 @@ Status data ^^^^^^^^^^^ Make sure that the UPS is providing good status data. +You can use the `upsc` command-line client for this: upsc myupsname@localhost ups.status @@ -350,8 +377,8 @@ You should see just one line in response: OL -OL means your system is running on line power. If it says something -else (like OB -- on battery, or LB -- low battery), your driver was +`OL` means your system is running on line power. If it says something +else (like `OB` -- on battery, or `LB` -- low battery), your driver was probably misconfigured during the <> step. If you reconfigure the driver, use `upsdrvctl stop` to stop it, then start it again as shown in the <> step. @@ -367,8 +394,8 @@ Look at all of the status data which is being monitored. upsc myupsname@localhost What happens now depends on the kind of device and driver you have. -In the list, you should see ups.status with the same value you got -above. A sample run on a UPS (Eaton Ellipse MAX 1100) looks like this: +In the list, you should see `ups.status` with the same value you got +above. A sample run on an UPS (Eaton Ellipse MAX 1100) looks like this: battery.charge: 100 battery.charge.low: 20 @@ -426,10 +453,14 @@ NOTE: This step is not necessary if you installed from packages. Edit your startup scripts, and make sure `upsdrvctl` and `upsd` are run every time your system starts. In newer versions of NUT, you may have a -`nut.conf` file which sets the `MODE` variable for bundled init-scripts, +'nut.conf' file which sets the `MODE` variable for bundled init-scripts, to facilitate enabling of certain features in the specific end-user deployments. +If you installed from source, check the `scripts` directory for reference +init-scripts, as well as systemd or SMF service methods and manifests. + + [[UPS_shutdown]] Configuring automatic shutdowns for low battery events ------------------------------------------------------ @@ -446,53 +477,78 @@ Shutdown design When your UPS batteries get low, the operating system needs to be brought down cleanly. Also, the UPS load should be turned off so that all devices -that are attached to it are forcibly rebooted. +that are attached to it are forcibly rebooted, and subsequently start in +the predictable order and state suitable for your data center. -Here are the steps that occur when a critical power event happens: +Here are the steps that occur when a critical power event happens, +for the simpler case of one UPS device feeding one or several systems: 1. The UPS goes on battery -2. The UPS reaches low battery (a "critical" UPS), that is to say - upsc displays: +2. The UPS reaches low battery (a "critical" UPS), that is to say, + `upsc` displays: + ups.status: OB LB + -The exact behavior depends on the specific device, and is related to: +The exact behavior depends on the specific device, and is related to +such settings and readings as: - - battery.charge and battery.charge.low - - battery.runtime and battery.runtime.low + - `battery.charge` and `battery.charge.low` + - `battery.runtime` and `battery.runtime.low` -3. The upsmon primary notices and sets "FSD" -- the "forced shutdown" - flag to tell all secondary systems that it will soon power down - the load. +3. The `upsmon` primary notices the "critical UPS" situation and sets + "FSD" -- the "forced shutdown" flag to tell all secondary systems + that it will soon power down the load. ++ +[WARNING] +========= +By design, since we require power-cycling the load and don't +want some systems to be powered off while others remain running +if the "wall power" returns at the wrong moment as usual, the "FSD" +flag can not be removed from the data server unless its daemon is +restarted. If we do take the first step in critical mode, then we +intend to go all the way -- shut down all the servers gracefully, +and power down the UPS. + +Keep in mind that some UPS devices and corresponding drivers would +latch the "FSD" again even if "wall power" is available, but the +remaining battery charge is below a threshold configured as "safe" +in the device (usually if you manually power on the UPS after a long +power outage). This is by design of respective UPS vendors, since +in such situation they can not guarantee that if a new power outage +happens, their UPS would safely shut down your systems again. +So it is deemed better and safer to stay dark until batteries +become sufficiently charged. +========= + (If you have no secondary systems, skip to step 6) -4. upsmon secondary systems see "FSD" and: +4. `upsmon` secondary systems see "FSD" and: - - generate a NOTIFY_SHUTDOWN event - - wait FINALDELAY seconds -- typically 5 - - call their SHUTDOWNCMD - - disconnect from upsd + - generate a `NOTIFY_SHUTDOWN` event + - wait `FINALDELAY` seconds -- typically `5` + - call their `SHUTDOWNCMD` + - disconnect from `upsd` -5. The upsmon primary system waits up to HOSTSYNC seconds (typically 15) - for the secondary systems to disconnect from upsd. If any are still - connected after this time, upsmon primary stops waiting and proceeds +5. The `upsmon` primary system waits up to `HOSTSYNC` seconds (typically `15`) + for the secondary systems to disconnect from `upsd`. If any are still + connected after this time, `upsmon` primary stops waiting and proceeds with the shutdown process. -6. The upsmon primary: +6. The `upsmon` primary: - - generates a NOTIFY_SHUTDOWN event - - waits FINALDELAY seconds -- typically 5 - - creates the POWERDOWNFLAG file -- usually `/etc/killpower` - - calls the SHUTDOWNCMD + - generates a `NOTIFY_SHUTDOWN` event + - waits `FINALDELAY` seconds -- typically `5` + - creates the `POWERDOWNFLAG` file in its local filesystem -- + usually `/etc/killpower` + - calls the `SHUTDOWNCMD` -7. On most systems, init takes over, kills your processes, syncs and +7. On most systems, `init` takes over, kills your processes, syncs and unmounts some filesystems, and remounts some read-only. -8. init then runs your shutdown script. This checks for the - POWERDOWNFLAG, finds it, and tells the UPS driver(s) to power off - the load. +8. `init` then runs your shutdown script. This checks for the + `POWERDOWNFLAG`, finds it, and tells the UPS driver(s) to power off + the load by sending commands to the connected UPS device(s) they manage. 9. All the systems lose power. @@ -500,6 +556,16 @@ The exact behavior depends on the specific device, and is related to: 11. All systems reboot and go back to work. +/////////////////////////////////// +https://github.com/networkupstools/nut/issues/1370 + +TODO: Check other docs and code to spell out expected behavior with +multiple UPS devices (when not all of them go critical or even on battery) +and servers with multiple inputs. + +Does the `upsmon` primary system power-cycle a "critical" UPS if that +is not the only one feeding it, so it is not shutting down now? +/////////////////////////////////// How you set it up ~~~~~~~~~~~~~~~~~ @@ -508,11 +574,13 @@ How you set it up NUT user creation ^^^^^^^^^^^^^^^^^ -Create a upsd user for upsmon to use while monitoring this UPS. +Create a `upsd` user for `upsmon` to use while monitoring this UPS. + +Edit 'upsd.users' and create a new section. The `upsmon` will connect +to `upsd` and use these user name (in brackets) and password to +authenticate (as specified in its configuration via `MONITOR` line). -Edit upsd.users and create a new section. upsmon will connect -to upsd and use this user name (in brackets) and password to -authenticate. This example is for a user called "monuser": +This example is for defining a user called "monuser": [monuser] password = mypass @@ -524,8 +592,8 @@ References: linkman:upsd[8], linkman:upsd.users[5] Reloading the data server ^^^^^^^^^^^^^^^^^^^^^^^^^ -Reload upsd. Depending on your configuration, you may be able to -do this without stopping upsd: +Reload `upsd`. Depending on your configuration, you may be able to +do this without stopping the `upsd` daemon process: upsd -c reload @@ -534,16 +602,20 @@ If that doesn't work (check the syslog), just restart it: upsd -c stop upsd -NOTE: if you want to make reloading work later, see the entry in the -link:FAQ.html[FAQ] about starting upsd as a different user. +For systems with integrated service management (Linux systemd, +illumos/Solaris SMF) their corresponding `reload` or `refresh` +service actions should handle this as well. + +NOTE: If you want to make reloading work later, see the entry in the +link:FAQ.html[FAQ] about starting `upsd` as a different user. Power Off flag file ^^^^^^^^^^^^^^^^^^^ -Set the POWERDOWNFLAG location for upsmon. +Set the `POWERDOWNFLAG` location for `upsmon`. -In upsmon.conf, add a POWERDOWNFLAG directive with a filename. -upsmon will create this file when the UPS needs to be powered off +In 'upsmon.conf', add a `POWERDOWNFLAG` directive with a filename. +The `upsmon` will create this file when the UPS needs to be powered off during a power failure when low battery is reached. We will test for the presence of this file in a later step. @@ -556,27 +628,27 @@ linkman:upsmon.conf[5] Securing upsmon.conf ^^^^^^^^^^^^^^^^^^^^ -The recommended setting is to have it owned by root:nut, then make it readable -by the group and not world. This file contains passwords that could be used by -an attacker to start a shutdown, so keep it secure. +The recommended setting is to have it owned by `root:nut`, then make it +readable by the group and not by the world. This file contains passwords +that could be used by an attacker to start a shutdown, so keep it secure. chown root:nut upsmon.conf chmod 0640 upsmon.conf -This step has been placed early in the process so you secure this file before -adding sensitive data in the next step. +This step has been placed early in the process so you secure this file +before adding sensitive data in the next step. Create a MONITOR directive for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Edit upsmon.conf and create a MONITOR line with the UPS definition +Edit 'upsmon.conf' and create a `MONITOR` line with the UPS definition (@), username and password from the <> step, and the "primary" or "secondary" setting. -If this system is the UPS manager (i.e., it's connected to this UPS directly -and can manage it using a suitable NUT driver), its upsmon is the primary: +If this system is the UPS manager (i.e. it's connected to this UPS directly +and can manage it using a suitable NUT driver), its `upsmon` is the primary: MONITOR myupsname@mybox 1 monuser mypass primary @@ -585,31 +657,33 @@ system is the primary, then this one is a secondary: MONITOR myupsname@mybox 1 monuser mypass secondary -The number "1" here is the power value. This should always be set -to 1 unless you have a very special (read: expensive) system with +The number `1` here is the "power value". This should always be set +to 1, unless you have a very special (read: expensive) system with redundant power supplies. In such cases, refer to the User Manual: - <>, - <>. -Note that the power value may also be 0 for a monitoring system which -only observes the UPS status but is not impacted by its power events, -and so does not shut down when the UPS does. +Note that the "power value" may also be 0 for a monitoring (administrative) +system which only observes the remote UPS status but is not impacted by its +power events, and so does not shut down when the UPS does. References: linkman:upsmon[8], linkman:upsmon.conf[5] Define a SHUTDOWNCMD for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Still in upsmon.conf, add a directive that tells upsmon how to shut down your -system. This example seems to work on most systems: +Still in 'upsmon.conf', add a directive that tells `upsmon` how to +shut down your system. This example seems to work on most systems: SHUTDOWNCMD "/sbin/shutdown -h +0" Notice the presence of "quotes" here to keep it together. -If your system has special needs, you may want to set this to a script which -does local shutdown tasks before calling init. +If your system has special needs (e.g. system-provided shutdown handler +is ungracefully time constrained), you may want to set this to a script +which does customized local shutdown tasks before calling `init` or +`shutdown` programs to handle the system side of this operation. Start upsmon @@ -625,8 +699,8 @@ life-cycle is managed by `nut-monitor` service. Checking upsmon ^^^^^^^^^^^^^^^ -Look for messages in the syslog to indicate success. It should look something -like this: +Look for messages in the `syslog` to indicate success. +It should look something like this: May 29 01:11:27 mybox upsmon[102]: Startup successful May 29 01:11:28 mybox upsd[100]: Client monuser@192.168.50.1 @@ -643,10 +717,12 @@ NOTE: This step is not need if you installed from packages. Edit your startup scripts, and add a call to `upsmon`. -Make sure `upsmon` starts when your system comes up. Do it after `upsdrvctl` -and `upsd`, or it will complain about not being able to contact the server. +Make sure `upsmon` starts when your system comes up. +On systems with `upsmon` primary (also running the data server), +do it after `upsdrvctl` and `upsd`, or it will complain about not +being able to contact the server. -You may delete the POWERDOWNFLAG in the startup scripts, but it is not +You may delete the `POWERDOWNFLAG` in the startup scripts, but it is not necessary. `upsmon` will clear that file for you when it starts. NOTE: Init script examples are provide in the 'scripts' directory of @@ -660,13 +736,14 @@ NOTE: This step is not need if you installed from packages. Edit your shutdown scripts, and add `upsdrvctl shutdown`. -You should configure your system to power down the UPS after the filesystems -are remounted read-only. Have it look for the presence of the POWERDOWNFLAG -(from linkman:upsmon.conf[5]), using this as an example: +You should configure your system to power down the UPS after the +filesystems are remounted read-only. Have it look for the presence +of the `POWERDOWNFLAG` (from linkman:upsmon.conf[5]), using this +as an example: ------------------------------------------------------------------------------ - if (test -f /etc/killpower) + if (/sbin/upsmon -K) then echo "Killing the power, bye!" /sbin/upsdrvctl shutdown @@ -682,13 +759,20 @@ are remounted read-only. Have it look for the presence of the POWERDOWNFLAG [WARNING] ============================================================================== -- Be careful that upsdrvctl command will probably power off your machine. +- Be careful that `upsdrvctl shutdown` command will probably power off +your machine and others fed by the UPS(es) which it manages. Don't use it unless your system is ready to be halted by force. If you run RAID, read the <<_raid_warning,RAID warning>> below! -- Make sure the filesystem(s) containing upsdrvctl, ups.conf and your UPS -driver(s) are mounted (possibly in read-only mode) when the system gets to +- Make sure the filesystem(s) containing `upsdrvctl`, `upsmon`, +the `POWERDOWNFLAG` file, 'ups.conf' and your UPS driver(s) are +mounted (possibly in read-only mode) when the system gets to this point. Otherwise it won't be able to figure out what to do. + +- If for some reason you can not ensure `upsmon` program is executable +at this point, your script can `(test -f /etc/killpower)` in a somewhat +non-portable manner, instead of asking `upsmon -K` for the verdict +according to its current configuration. ============================================================================== @@ -702,8 +786,8 @@ on your systems before leaving them unattended. A successful sequence is one where the OS halts before the battery runs out, and the system restarts when power returns. -The first step is to see how upsdrvctl will behave without actually -turning off the power. To do so, use the '-t' argument: +The first step is to see how `upsdrvctl` will behave without actually +turning off the power. To do so, use the `-t` argument: upsdrvctl -t shutdown @@ -743,35 +827,38 @@ is turned off and waits for the power to return. Once the power is back, the system reboots, pulls the snapshot back in, and keeps going from there. If the user happened to be away when it happened, they may return and have no idea that their system actually -shut down completely in the middle. +shut down completely in the middle (although network connections will drop). -In order for this to work, you need to shutdown NUT (UPS driver, upsd -server and upsmon client) in the suspend script and start them again in -the resume script. Don't try to keep them running. The upsd server +In order for this to work, you need to shutdown NUT (UPS driver, `upsd` +server and `upsmon` client) in the `suspend` script and start them again in +the `resume` script. Don't try to keep them running. The `upsd` server will latch the FSD state (so it won't be usable after resuming) and so -will the upsmon client. Some drivers may work after resuming, but many -don't and some UPSs will require re-initialization, so it's best not -to keep this running either. +will the `upsmon` client. Some drivers may work after resuming, but many +don't and some UPS devices will require re-initialization, so it's best not +to keep them running either. -After stopping driver, server and client you'll have to send the UPS -the command to shutdown only if the POWERDOWNFLAG is present. Note -that most likely you'll have to allow for a grace period after sending -'upsdrvctl shutdown' since the system will still have to take a -snapshot of itself after that. Not all drivers support this, so before -going down this road, make sure that the one you're using does. +After stopping NUT driver, server and client you'll have to send the UPS +the command to shutdown only if the `POWERDOWNFLAG` is present. Note +that most likely you'll have to allow for a grace period after calling +`upsdrvctl shutdown` since the system will still have to take a +snapshot of itself after that. Not all drivers and devices support this, +so before going down this road, make sure that the one you're using does. + +- see if you can query or configure settings named like `load.off.delay`, + `ups.delay.shutdown`, `offdelay` and/or `shutdown_delay` RAID warning ~~~~~~~~~~~~ -If you run any sort of RAID equipment, make sure your arrays are either halted -(if possible) or switched to "read-only" mode. Otherwise you may suffer a long -resync once the system comes back up. +If you run any sort of RAID equipment, make sure your arrays are +either halted (if possible) or switched to "read-only" mode. +Otherwise you may suffer a long resync once the system comes back up. -The kernel may not ever run its final shutdown procedure, so you must take care -of all array shutdowns in userspace before upsdrvctl runs. +The kernel may not ever run its final shutdown procedure, so you must take +care of all array shutdowns in userspace before `upsdrvctl shutdown` runs. -If you use software RAID (md) on Linux, get mdadm and try using -'mdadm --readonly' to put your arrays in a safe state. This has to +If you use software RAID (md) on Linux, get `mdadm` and try using +`mdadm --readonly` to put your arrays in a safe state. This has to happen after your shutdown scripts have remounted the filesystems. On hardware RAID or other kernels, you have to do some detective work. It may @@ -793,8 +880,8 @@ be configured using some general descriptions. There are two main elements: -1. There's a UPS attached to a communication (serial, USB or network) port on -this system. +1. There's a UPS attached to a communication (serial, USB or network) port + on this system. 2. This system depends on a UPS for power. You can play "mix and match" with those two to arrive at these descriptions @@ -804,36 +891,37 @@ for individual hosts: - B: 2 but not 1 - C: 1 and 2 -A small to medium sized data room usually has one C and a bunch of Bs. -This means that there's a system (type C) hooked to the UPS which depends -on it for power. There are also some other systems in there (type B) +A small to medium sized data room usually has one 'C' and a bunch of 'Bs'. +This means that there's a system (type 'C') hooked to the UPS which depends +on it for power. There are also some other systems in there (type 'B') which depend on that same UPS for power, but aren't directly connected to -it. +it communications-wise. Larger data rooms or those with multiple UPSes may have several "clusters" -of the "single C, many Bs" depending on how it's all wired. +of the "single 'C', many 'Bs'" depending on how it's all wired. -Finally, there's a special case. Type A systems are connected to a UPS's -serial port, but don't depend on it for power. This usually happens when -a UPS is physically close to a box and can reach the serial port, but -the wiring is such that it doesn't actually feed it. +Finally, there's a special case. Type 'A' systems are connected to +an UPS's communication port, but don't depend on it for power. +This usually happens when an UPS is physically close to a box and can +reach the serial port, but the power wiring is such that it doesn't +actually feed that box. Once you identify a system's type, use this list to decide which of the programs need to be run for monitoring: -- A: driver and upsd -- B: upsmon (in secondary mode) -- C: driver, upsd, and upsmon (in primary mode, as the UPS manager) +- A: driver and `upsd` +- B: `upsmon` (in secondary mode) +- C: driver, `upsd`, and `upsmon` (in primary mode, as the UPS manager) + +image:images/advanced.png[] To further complicate things, you can have a system that is hooked to multiple UPSes, but only depends on one for power. This particular -situation makes it an "A" relative to one UPS, and a "C" relative to the +situation makes it an `A` relative to one UPS, and a `C` relative to the other. The software can handle this -- you just have to tell it what to do. NOTE: NUT can also serve as a data proxy to increase the number of clients, -or share the communication load between several upsd instances. - -image:images/advanced.png[] +or share the communication load between several `upsd` instances. If you are running large server-class systems that have more than one power feed, see the next section for information on how to handle it @@ -843,8 +931,8 @@ properly. Typical setups for big servers with UPS redundancy -------------------------------------------------- -By using multiple MONITOR statements in upsmon.conf, you can configure an -environment where a large machine with redundant power monitors multiple +By using multiple `MONITOR` statements in 'upsmon.conf', you can configure +an environment where a large machine with redundant power monitors multiple separate UPSes. image:images/bigbox.png[] @@ -861,29 +949,30 @@ Two UPSes, 'Alpha' and 'Beta', are each driving two of the power supplies This means that either 'Alpha' *or* 'Beta' can totally shut down and the server will be able to keep running. -The upsmon.conf configuration that reflect this is the following: +The 'upsmon.conf' configuration which reflects this is the following: MONITOR ups-alpha@myhost 2 monuser mypass primary - MONITOR ups-beta@myhost 2 monuser mypass primary + MONITOR ups-beta@myhost 2 monuser mypass primary MINSUPPLIES 2 -With that configuration, upsmon will only shut down when both UPS reaches -a critical (on battery + low battery) condition, since 'Alpha' and 'Beta' -provide the same power value. +With such configuration, `upsmon` on this system will only shut down when +both UPS devices reach a critical (on battery + low battery) condition, +since 'Alpha' and 'Beta' each provide the same power value. As an added bonus, this means you can move a running server from one UPS to another (for maintenance purpose for example) without bringing it down since the minimum sufficient power will be provided at all times. -The MINSUPPLIES line tells upsmon that we need at least 2 power supplies +The `MINSUPPLIES` line tells `upsmon` that we need at least 2 power supplies to be receiving power from a good UPS (on line or on battery, just not -on battery and low battery). +on battery *and* low battery). -NOTE: we could have used a 'Power Value' of 1 for both UPS, and MINSUPPLIES -set to 1 too. These values are purely arbitrary, so you are free to use your -own rules. Here, we have linked these values to the number of power supplies -that each UPS is feeding (2) since this maps better to physical topology and -allows to throw a third or fourth UPS into the mix without much headache. +NOTE: We could have used a 'Power Value' of `1` for both UPS, and have +`MINSUPPLIES` set to `1` too. These values are purely arbitrary, so +you are free to use your own rules. Here, we have linked these values +to the number of power supplies that each UPS is feeding (2) since this +maps better to physical topology and allows to throw a third or fourth +UPS into the mix without much configuration headache. Multiple UPS shutdowns ordering @@ -891,11 +980,11 @@ Multiple UPS shutdowns ordering If you have multiple UPSes connected to your system, chances are that you need to shut them down in a specific order. The goal is to shut down -everything but the one keeping upsmon alive at first, then you do that one -last. +everything but the one keeping `upsmon` alive at first, then you do that +one last. To set the order in which your UPSes receive the shutdown commands, define -the 'sdorder' value in your ups.conf. +the `sdorder` value in your 'ups.conf' device sections. [bigone] driver = usbhid-ups @@ -913,11 +1002,11 @@ the 'sdorder' value in your ups.conf. sdorder = 0 The order runs from 0 to the highest number available. So, for this -configuration, the order of shutdowns would be 'misc', 'littleguy', and then -'bigone'. +configuration, the order of shutdowns would be 'misc', 'littleguy', +and then 'bigone'. -NOTE: If you have a UPS that shouldn't be shutdown when running 'upsdrvctl -shutdown', set the *sdorder* to *-1*. +NOTE: If you have a UPS that shouldn't be powered off when running +`upsdrvctl shutdown`, set its `sdorder` to `-1`. Other redundancy configurations diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 5beb269627..9a1502a69b 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -35,29 +35,32 @@ General call to Test the ability to configure and build Check out from git, generate files and configure to tailor to your build environment, and build some tests: ----- +------ :; mkdir -p nut && cd nut && \ - git clone https://github.com/networkupstools/nut/ -b fightwarn . + git clone https://github.com/networkupstools/nut/ -b master . :; ./autogen.sh && \ ./configure --with-doc=all --with-all --with-cgi && \ make all && make check && make spellcheck ----- +------ -NOTE: You can toggle some build options to check different dependency +You can toggle some `configure` options to check different dependency variants, e.g. `--with-ssl=nss` vs. `--with-ssl=openssl` -NOTE: For reproducible runs of various pre-sets of configuration during +For reproducible runs of various pre-sets of configuration during development, take a look at `ci_build.sh` script and different `BUILD_TYPE` (and other) environment variable settings that it supports. A minimal run -with it is just: -+ ----- +with it is just to call the script, e.g.: + +------ :; mkdir -p nut && cd nut && \ git clone https://github.com/networkupstools/nut/ -b fightwarn . :; ./ci_build.sh ----- +------ -NOTE: To build older releases, such as "vanilla" NUT 2.7.4, you may need to: +[NOTE] +====== +To build older releases, such as "vanilla" NUT 2.7.4 and older, +you may need to address some nuances: * Ensure that `python` in `PATH` points to a python-2.x implementation (`master` branch is fixed to work with python 2 and 3) @@ -66,43 +69,82 @@ NOTE: To build older releases, such as "vanilla" NUT 2.7.4, you may need to: generated `configure` script gets interpreted by it) * Generally you may have better results with GNU Make newer than 3.81 - than with other make implementations + than with other make implementations; however, builds are regularly + tested by CI with Sun dmake and BSD make as well, so recipes should + not expect GNU-only syntax and constructs to work + +* Parallel builds should be okay in current development version and + since NUT 2.8.0 (is a bug to log and fix, if not), but they may be + failure-prone in 2.7.4 and earlier releases +====== + +For intensive rebuilds, `ccache` is recommended. -* For intensive rebuilds, `ccache` is recommended -* Parallel builds should be okay in current development version (is a bug - to log, if not), but may be failure-prone in 2.7.4 and earlier releases +Build prerequisites to make NUT from scratch on various Operating Systems +------------------------------------------------------------------------- Debian 10/11 ~~~~~~~~~~~~ -Being a popular baseline among Linux distributions, Debian is an important -build target. Related common operating systems include Ubuntu and customized -distros for Raspberry Pi, as well as many others. The package list below -should largely apply to those as well, however note that some well-known -package names tend to differ. A few of those are noted below. +Being a popular baseline among Linux distributions, Debian is an +important build target. Related common operating systems include +Ubuntu and customized distros for Raspberry Pi, Proxmox, as well +as many others. -NOTE: While Debian distros I've seen (8 to 11) provide a "libusb-dev" +The package list below should largely apply to those as well, +however note that some well-known package names tend to differ. +A few of those are noted below. + +[NOTE] +====== +While Debian distros I've seen (8 to 11) provide a "libusb-dev" for libusb-0.1 headers, the binary library package name is specifically versioned package by default of the current release (e.g. "libusb-0.1-4"), while names of both the library and development packages for libusb-1.0 -must be determined with `apt-cache search 'libusb.*1\.0.*` yielding e.g. -"libusb-1.0-0-dev" (string name was seen with different actual package -source versions on both Debian 8 Jessie and Debian 11 Buster). +must be determined with: +------ +:; apt-cache search 'libusb.*1\.0.* +------ +yielding e.g. "libusb-1.0-0-dev" (string name was seen with different +actual package source versions on both Debian 8 "Jessie" and +Debian 11 "Buster"). +====== ----- +Debian-like package installations commonly start with an update of +metadata about recently published package revisions: + +------ :; apt-get update # NOTE: Older Debian-like distributions may lack a "libtool-bin" :; apt-get install \ ccache time \ - git python curl \ + git python perl curl \ make autoconf automake libltdl-dev libtool-bin libtool \ valgrind \ cppcheck \ pkg-config \ - gcc g++ clang \ - asciidoc source-highlight python3-pygments dblatex aspell \ + gcc g++ clang + +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; apt-get install python2 python2.7 python-is-python2 +# and/or: +# :; apt-get install python3 python3.9 +# You can find a list of what is (pre-)installed with: +# :; dpkg -l | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; apt-get install \ + aspell aspell-en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; apt-get install \ + asciidoc source-highlight python3-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; apt-get install \ libgd-dev # NOTE: Some older Debian-like distributions, could ship "libcrypto-dev" @@ -125,35 +167,34 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). :; apt-get install \ bash dash ksh busybox ----- +------ Alternatives that can depend on your system's other packaging choices: ----- +------ :; apt-get install libneon27-dev # ... or :; apt-get install libneon27-gnutls-dev ----- +------ Over time, Debian and Ubuntu had different packages and libraries providing the actual methods for I2C; if your system lacks the `libi2c` (and so fails to `./configure --with-all`), try adding the following packages: -+ ----- +------ :; apt-get install build-essential git-core libi2c-dev i2c-tools lm-sensors ----- +------ For cross-builds (note that not everything supports multilib approach, limiting standard package installations to one or another implementation; in that case local containers each with one ARCH may be a better choice, with `qemu-user-static` playing a role to "natively" run the other-ARCH complete environments): ----- +------ :; apt-get install \ gcc-multilib g++-multilib \ crossbuild-essential \ gcc-10:armhf gcc-10-base:armhf \ qemu-user-static ----- +------ NOTE: For Jenkins agents, also need to `apt-get install openjdk-11-jdk-headless` (technically, needs at least JRE 8+). You may have to ensure `/proc` is mounted @@ -173,12 +214,14 @@ well. These systems typically use the RPM package manager, using directly For CentOS 7 it seems that not all repositories are equally good; some of the software below is only served by EPEL (Extra Packages for Enterprise Linux), as detailed at: + * https://docs.fedoraproject.org/en-US/epel/ * https://www.redhat.com/en/blog/whats-epel-and-how-do-i-use-it * https://pkgs.org/download/epel-release You may have to specify a mirror as the `baseurl` in a `/etc/yum.repos.d/...` file (as the aged distributions become less served by mirrors), such as: + * https://www.mirrorservice.org/sites/dl.fedoraproject.org/pub/epel/7/x86_64/ + ------ @@ -189,7 +232,7 @@ file (as the aged distributions become less served by mirrors), such as: # lines, and comment away the mirrorlist= lines (if yum hiccups otherwise) ------ -General developer system helpers mentioned in `ci-farm-lxc-setup.txt`: +General developer system helpers mentioned in link:ci-farm-lxc-setup.txt[]: ------ :; yum update @@ -198,8 +241,8 @@ General developer system helpers mentioned in `ci-farm-lxc-setup.txt`: ------ NOTE: Below we request to install generic `python` per system defaults. -You may request specifically `python2` or `python3` (or both): NUT should -be compatible with both (2.7+ at least). +You may request specifically `python2` or `python3` (or both): current +NUT should be compatible with both (2.7+ at least). NOTE: On CentOS, `libusb` means 0.1.x and `libusbx` means 1.x.x API version. @@ -207,21 +250,35 @@ NOTE: On CentOS, it seems that development against libi2c/smbus is not supported. Neither the suitable devel packages were found, nor i2c-based drivers in distro packaging of NUT. Resolution and doc PRs are welcome. -NOTE: `busybox` is not packaged for CentOS 7 release; a static binary can -be downloaded if needed. For more details, see -https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos - ------ :; yum install \ ccache time \ file systemd-devel \ - git python curl \ + git python perl curl \ make autoconf automake libtool-ltdl-devel libtool \ valgrind \ cppcheck \ pkgconfig \ - gcc gcc-c++ clang \ - asciidoc source-highlight python-pygments dblatex aspell \ + gcc gcc-c++ clang + +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; yum install python-2.7.5 +# and/or: +# :; yum install python3 python3-3.6.8 +# You can find a list of what is (pre-)installed with: +# :; rpm -qa | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; yum install \ + aspell aspell-en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; yum install \ + asciidoc source-highlight python-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; yum install \ gd-devel # NOTE: "libusbx" is the CentOS way of naming "libusb-1.0" @@ -249,14 +306,18 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos :; yum install \ bash dash ksh ----- +------ + +NOTE: `busybox` is not packaged for CentOS 7 release; a static binary can +be downloaded if needed. For more details, see +https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos CentOS packaging for 64-bit systems delivers the directory for dispatching compiler symlinks as `/usr/lib64/ccache`. You can set it up same way as for other described environments by adding a symlink `/usr/lib/ccache`: ----- +------ :; ln -s ../lib64/ccache/ "$ALTROOT"/usr/lib/ ----- +------ NOTE: For Jenkins agents, also need to `yum install java-11-openjdk-headless` (technically, needs at least JRE 8+). @@ -264,25 +325,43 @@ NOTE: For Jenkins agents, also need to `yum install java-11-openjdk-headless` FreeBSD 12.2 ~~~~~~~~~~~~ -Note that `PATH` for builds on BSD should include `local`: +Note that `PATH` for builds on BSD should include `/usr/local/...`: ----- +------ :; PATH=/usr/local/libexec/ccache:/usr/local/bin:/usr/bin:$PATH :; export PATH ----- +------ NOTE: You may want to reference `ccache` even before all that, as detailed below. ----- +------ :; pkg install \ - git python curl \ + git python perl5 curl \ gmake autoconf automake autotools libltdl libtool \ valgrind \ cppcheck \ pkgconf \ - gcc clang \ - asciidoc source-highlight textproc/py-pygments dblatex en-aspell aspell \ + gcc clang + +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; pkg install python2 python27 +# and/or: +# :; pkg install python3 python37 +# You can find a list of what is (pre-)installed with: +# :; pkg info | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; pkg install \ + aspell en-aspell + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg install \ + asciidoc source-highlight textproc/py-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; pkg install \ libgd :; pkg install \ @@ -301,13 +380,13 @@ below. :; pkg install \ bash dash busybox ksh93 ----- +------ Recommended: ----- +------ :; pkg install ccache :; ccache-update-links ----- +------ For compatibility with common setups on other operating systems, can symlink `/usr/local/libexec/ccache` as `/usr/lib/ccache` and possibly add dash-number @@ -319,16 +398,17 @@ and do note its further OS configuration suggestions for special filesystem mounts. Due to BSD specific paths *when not using* an implementation of `pkg-config` -or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT m4 -scripts), better use this routine to test the config/build: +or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT +`m4` scripts), better use this routine to test the config/build: ---- :; ./configure --with-doc=all --with-all --with-cgi \ --without-avahi --without-powerman --without-modbus \ - ### CPPFLAGS="-I/usr/local/include -I/usr/include" LDFLAGS="-L/usr/local/lib -L/usr/lib" + ### CPPFLAGS="-I/usr/local/include -I/usr/include" \ + ### LDFLAGS="-L/usr/local/lib -L/usr/lib" ---- -Note the lack of `pkg-config` also precludes libcppunit tests, although they -also tend to mis-compile/mis-link with GCC (while CLANG seems okay). +Note the lack of `pkg-config` also precludes `libcppunit` tests, although +they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). For DMF, additionally `pkg install py27-pycparser-2.20 py38-pycparser-2.20` (assuming you want to test both relevant generations of Python), and may need @@ -338,12 +418,12 @@ already). OpenBSD 6.4 ~~~~~~~~~~~ -Note that `PATH` for builds on BSD should include `local`: +Note that `PATH` for builds on BSD should include `/usr/local/...`: ----- +------ :; PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH :; export PATH ----- +------ NOTE: You may want to reference `ccache` even before all that, as detailed below. @@ -369,19 +449,38 @@ at least not via http://ftp.netbsd.hu/mirrors/openbsd/6.4/packages/amd64/ mirror. The `gcc` version on CD image differed notably from that in the networked repository (4.2.x vs 4.9.x) ----- +------ :; pkg_add \ git python curl \ gmake autoconf automake libltdl libtool \ valgrind \ cppcheck \ pkgconf \ - gcc clang \ - asciidoc source-highlight py-pygments dblatex aspell \ - gd + gcc clang + +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; yum install python-2.7.15p0 +# and/or: +# :; yum install python-3.6.6p1 +# although you might succeed specifying shorter names and the packager +# will offer a list of matching variants. +# NOTE: "perl" is not currently a package, but seemingly part of base OS. +# You can find a list of what is (pre-)installed with: +# :; pkg_info | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; pkg_add \ + aspell + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg_add \ + asciidoc source-highlight py-pygments dblatex \ + docbook2x docbook-to-man -# Need to find proper package name and/or mirror for this: -:; pkg_add clang +# For CGI graph generation - massive packages (X11): +:; pkg_add \ + gd :; pkg_add \ cppunit \ @@ -398,26 +497,29 @@ networked repository (4.2.x vs 4.9.x) :; pkg_add \ bash dash busybox ksh93 ----- +------ -NOTE: With OpenBSD 6.4, building against freeipmi failed: its libtool +[NOTE] +====== +With OpenBSD 6.4, building against freeipmi failed: its libtool recipes referenced `-largp` which did not get installed in the system. Maybe some more packages are needed explicitly? -+ ----- + +------ :; pkg_add \ freeipmi ----- +------ +====== Recommended: ----- +------ :; pkg_add ccache :; ( mkdir -p /usr/lib/ccache && cd /usr/lib/ccache && \ for TOOL in cpp gcc g++ clang clang++ clang-cpp ; do \ ln -s ../../local/bin/ccache "$TOOL" ; \ done ; \ ) ----- +------ For compatibility with common setups on other operating systems, can add dash-number suffixed symlinks to compiler tools (e.g. `gcc-4.2.1` beside @@ -427,18 +529,142 @@ NOTE: For Jenkins agents, also need to `pkg_add jre` or `pkg_add jdk` (pick version 1.8 or 8, or 11+). Due to BSD specific paths *when not using* an implementation of `pkg-config` -or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT m4 -scripts), better use this routine to test the config/build: ----- +or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT +`m4` scripts), better use this routine to test the config/build: +------ :; ./configure --with-doc=all --with-all --with-cgi \ --without-avahi --without-powerman --without-modbus \ - ### CPPFLAGS="-I/usr/local/include -I/usr/include" LDFLAGS="-L/usr/local/lib -L/usr/lib" ----- + ### CPPFLAGS="-I/usr/local/include -I/usr/include" + ### LDFLAGS="-L/usr/local/lib -L/usr/lib" +------ + +Note the lack of `pkg-config` also precludes `libcppunit` tests, although +they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). + +NetBSD 9.2 +~~~~~~~~~~ + +Instructions below assume that `pkgin` tool (pkg-src component to +"install binary packages") is present on the system. Text below +was prepared with a VM where "everything" was installed from the +ISO image, including compilers and X11. It is possible that some +packages provided this way differ from those served by `pkgin`, +or on the contrary, that the list of suggested tool installation +below would not include something a bare-minimum system would +require to build NUT. + +Note that `PATH` for builds on NetBSD should include `local` and +`pkg`; the default after installation of the test system was: + +------ +:; PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/X11R7/bin:/usr/local/sbin:/usr/local/bin" +:; export PATH +------ + +NOTE: You may want to reference `ccache` even before all that, +as detailed below: + +------ +:; PATH="/usr/lib/ccache:$PATH" +:; export PATH +------ + +To use the `ci_build.sh` don't forget `bash` which is not part of OpenBSD +base installation. It is not required for "legacy" builds arranged by just +`autogen.sh` and `configure` scripts. + +------ +:; pkgin install \ + git python27 python39 perl curl \ + make gmake autoconf automake libltdl libtool \ + cppcheck \ + pkgconf \ + gcc7 clang + +;; ( cd /usr/pkg/bin && ( ln -fs python2.7 python2 ; ln -fs python3.9 python3 ) ) +# You can find a list of what is (pre-)installed with: +# :; pkgin list | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; pkgin install \ + aspell aspell-en + +# For man-page doc types, footprint on this platform is moderate: +:; pkgin install \ + asciidoc + +# For other doc types (PDF, HTML) generation - massive packages (TEX, X11): +:; pkgin install \ + source-highlight py39-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; pkgin install \ + gd openmp + +:; pkgin install \ + cppunit \ + openssl nss \ + augeas \ + libusb libusb1 \ + neon \ + net-snmp \ + avahi + +# Select a LUA-5.1 (or possibly 5.2?) version +:; pkgin install \ + lua51 + +:; pkgin install \ + bash dash ast-ksh oksh +------ -Note the lack of `pkg-config` also precludes libcppunit tests, although they -also tend to mis-compile/mis-link with GCC (while CLANG seems okay). +[NOTE] +====== +(TBD) On NetBSD 9.2 this package complains that it requires +OS ABI 9.0, or that `CHECK_OSABI=no` is set in `pkg_install.conf`. +Such file was not found in the test system... + +------ +:; pkgin install \ + openipmi +------ +====== -OpenIndiana 2021.04 +Recommended: For compatibility with common setups on other operating +systems, can add dash-number suffixed symlinks to compiler tools (e.g. +`gcc-7` beside the `gcc` installed by package) near the original +binaries and into `/usr/lib/ccache`: +------ +:; ( cd /usr/bin && for TOOL in cpp gcc g++ ; do \ + ln -s "$TOOL" "$TOOL-7" ; \ + done ) + +# Note that the one delivered binary is `clang-13` and many (unnumbered) +# symlinks to it. For NUT CI style of support for builds with many +# compilers, complete the known numbers: +:; ( cd /usr/pkg/bin && for TOOL in clang-cpp clang++ ; do \ + ln -s clang-13 "$TOOL-13" ; \ + done ) + +:; pkgin install ccache +:; ( mkdir -p /usr/lib/ccache && cd /usr/lib/ccache && \ + for TOOL in cpp gcc g++ clang ; do \ + for VER in "" "-7" ; do \ + ln -s ../../pkg/bin/ccache "$TOOL$VER" ; \ + done ; \ + done ; \ + for TOOL in clang clang++ clang-cpp ; do \ + for VER in "" "-13" ; do \ + ln -s ../../pkg/bin/ccache "$TOOL$VER" ; \ + done ; \ + done ; \ + ) +------ + +NOTE: For Jenkins agents, also need to `pkgin install openjdk11` (will be +in `JAVA_HOME=/usr/pkg/java/openjdk11`). + +OpenIndiana 2021.10 ~~~~~~~~~~~~~~~~~~~ Note that due to IPS and `pkg(5)`, a version of python is part of baseline @@ -450,21 +676,47 @@ To build older NUT releases (2.7.4 and before), you may need to explicitly Typical tooling would include: ----- +------ :; pkg install \ git curl wget \ gnu-make autoconf automake libltdl libtool \ valgrind \ pkg-config \ - gnu-binutils developer/linker \ - asciidoc libxslt aspell text/aspell/en \ + gnu-binutils developer/linker + +# NOTE: For python, some suitable version should be available since `pkg(5)` +# tool is written in it. Similarly, many system tools are written in perl +# so some version should be installed. You may specify additional variants +# like this (numbers depending on default or additional packages of your +# distro; recommended to group `pkg` calls with many packages at once to +# save processing time for calculating a build strategy): +# :; pkg install runtime/python-27 +# and/or: +# :; pkg install runtime/python-37 runtime/python-35 runtime/python-39 +# Similarly for perl variants, e.g.: +# :; pkg install runtime/perl-522 runtime/perl-524 runtime/perl-534 +# You can find a list of what is available in remote repositories with: +# :; pkg info -r | grep -Ei 'perl|python' + +# For spell-checking, highly recommended if you would propose pull requests: +:; pkg install \ + aspell text/aspell/en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg install \ + asciidoc libxslt \ docbook/dtds docbook/dsssl docbook/xsl docbook docbook/sgml-common pygments-39 \ - graphviz expect gd graphviz-tcl + graphviz expect graphviz-tcl + +# For CGI graph generation - massive packages (X11): +:; pkg install \ + gd :; pkg install \ openssl library/mozilla-nss \ library/augeas python/augeas \ libusb-1 libusbugen system/library/usb/libusb system/header/header-usb driver/usb/ugen \ + libmodbus \ neon \ net-snmp \ powerman \ @@ -484,26 +736,53 @@ Typical tooling would include: ### Maybe - after it gets fixed for GCC builds/linkage :; pkg install \ cppunit +------ -### Not yet in distro, PR pending: +For extra compiler coverage, we can install a large selection of versions, +although to meet NUT CI farm expectations we also need to expose "numbered" +filenames, as automated below: +------ :; pkg install \ - libmodbus ----- + gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 gcc-11 \ + clang-80 clang-90 \ + ccache -For extra compiler coverage, can also set up `gcc-4.4.4-il` (used to build -the OS, or was recently, and is a viable example of an old GCC baseline); -but note that so far it conflicts with libgd builds at `configure --with-cgi` -stage: ----- +# As of this writing, clang-13 refused to link (claiming issues with +# --fuse-ld which was never specified) on OI; maybe later it will: :; pkg install \ - illumos-gcc@4.4.4 \ - gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 \ - clang-80 clang-90 + developer/clang-13 runtime/clang-13 + +# Get clang-cpp-X visible in standard PATH (for CI to reference the right one), +# and make sure other frontends are exposed with versions (not all OI distro +# releases have such symlinks packaged right), e.g.: +:; (cd /usr/bin && for X in 8 9 13 ; do for T in "" "++" "-cpp"; do \ + ln -fs "../clang/$X.0/bin/clang$T" "clang${T}-${X}" ; \ + done; done) + +# If /usr/lib/ccache/ symlinks to compilers do not appear after package +# installation, or if you had to add links like above, call the service: +:; svcadm restart ccache-update-symlinks +------ -:; svcadm refresh clang-update-symlinks ----- +We can even include a `gcc-4.4.4-il` version (used to build the illumos OS +ecosystems, at least until recently, which is a viable example of an old +GCC baseline); but note that so far it conflicts with `libgd` builds at +`./configure --with-cgi` stage (its binaries require newer ecosystem): + +------ +:; pkg install \ + illumos-gcc@4.4.4 + +# Make it visible in standard PATH +:; (cd /usr/bin && for T in gcc g++ cpp ; do \ + ln -s ../../opt/gcc/4.4.4/bin/$T $T-4.4.4 ; \ + done) -OI currently also does not build cppunit-based tests well, at least +# If /usr/lib/ccache/ symlinks to these do not appear, call the service: +:; svcadm restart ccache-update-symlinks +------ + +OI currently also does not build `cppunit`-based tests well, at least not with GCC (they segfault at run-time with `ostream` issues); a CLANG build works for that however. @@ -514,7 +793,8 @@ from third-party repositories (e.g. SFE) and/or build from sources. No pre-packaged `cppcheck` was found, either. NOTE: For Jenkins agents, also need to `pkg install developer/java/openjdk8` -(or 11+). +(or `pkg install runtime/java/openjdk11` for JRE 11 -- currently the OS does +not offer in-distro JDK version 11+). For DMF, additionally install `library/python/pycparser` (or a versioned one matching your installed Python distribution, e.g. `library/python/pycparser-39` @@ -533,25 +813,33 @@ with that project's design goals. Note you may need not just the "Core" IPS package publisher, but also the "Extra" one. See OmniOS CE web site for setup details. ----- +------ :; pkg install \ developer/build/autoconf developer/build/automake developer/build/libtool \ build-essential ccache git developer/pkg-config \ + runtime/perl \ asciidoc \ libgd :; pkg install \ net-snmp ----- + +# NOTE: For python, some suitable version should be available since `pkg(5)` +# tool is written in it. You may specify an additional variant like this +# (numbers depending on default or additional packages of your distro): +# :; pkg install runtime/python-37 +# You can find a list of what is available in remote repositories with: +# :; pkg info -r | grep -Ei 'perl|python' +------ OmniOS lacks a pre-packaged libusb, however the binary build from contemporary OpenIndiana can be used (copy the header files and the library+symlinks for all architectures you would need). -You may need to set up `ccache` with the same dir used in other OS recipes; -assuming your Build Essentials pulled GCC 9, and ccache is under `/opt/ooce` -namespace, that would be like: ----- +You may need to set up `ccache` with the same `/usr/lib/ccache` dir used +in other OS recipes. Assuming your Build Essentials pulled GCC 9 version, +and ccache is under `/opt/ooce` namespace, that would be like: +------ :; mkdir -p /usr/lib/ccache :; cd /usr/lib/ccache :; ln -fs ../../../opt/ooce/bin/ccache gcc @@ -560,14 +848,14 @@ namespace, that would be like: :; ln -fs ../../../opt/ooce/bin/ccache gcc-9 :; ln -fs ../../../opt/ooce/bin/ccache g++-9 :; ln -fs ../../../opt/ooce/bin/ccache gcpp-9 ----- +------ Given that many of the dependencies can get installed into that namespace, you may have to specify where `pkg-config` will look for them (note that library and binary paths can be architecture bitness-dependent): ----- +------ :; ./configure PKG_CONFIG_PATH="/opt/ooce/lib/amd64/pkgconfig" --with-cgi ----- +------ Note also that the minimal footprint nature of OmniOS CE precludes building any large scope easily, so avoid docs and "all drivers" unless you provide diff --git a/docs/configure.txt b/docs/configure.txt index 2905c8d28a..446e6227d9 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -10,13 +10,20 @@ current and complete listing for the current version of the codebase. Driver selection ---------------- +Serial drivers +~~~~~~~~~~~~~~ + --with-serial +USB drivers +~~~~~~~~~~~ + Build and install the serial drivers (default: yes) --with-usb Build and install the USB drivers (default: auto-detect) + Note that you need to install the libusb development package or files, and that both libusb 0.1 and 1.0 are supported. In case both are available, libusb 1.0 takes precedence, and will be used by default. @@ -26,9 +33,13 @@ If you do specify the version to use (or `yes` for auto-detection), this option would fail if requested (or any) libusb version was not found. The default `auto` value would not fail in such case. +SNMP drivers +~~~~~~~~~~~~ + --with-snmp Build and install the SNMP drivers (default: auto-detect) + Note that you need to install libsnmp development package or files. --with-net-snmp-config @@ -37,41 +48,75 @@ In addition to the `--with-snmp` option above, this one allows to provide a custom program name (in `PATH`) or complete pathname to `net-snmp-config` (may have copies named per architecture, e.g. `net-snmp-config-32` and `net-snmp-config-64`). + This may be needed on build systems which support multiple architectures, or in cases where your distribution names this program differently. With a default value of `yes` it would mean preference of this program, compared to information from `pkg-config`, if both are available. + --with-dmf + +Build and install DMF (data mapping file) support, currently implemented +for snmp-ups (default: auto-detect) + +Note that you need to install libneon (mandatory), xmllint and lua development +packages or files. + +XML drivers and features +~~~~~~~~~~~~~~~~~~~~~~~~ + --with-neon Build and install the XML drivers (default: auto-detect) + Note that you need to install neon development package or files. +LLNC CHAOS Powerman driver +~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-powerman Build and install Powerman PDU client driver (default: auto-detect) + This allows to interact with the Powerman daemon, and the numerous -Power Distribution Units (PDU) supported by the project. +Power Distribution Units (PDU) supported by the +https://github.com/chaos/powerman[powerman] project. + Note that you need to install powerman development package or files. +IPMI drivers +~~~~~~~~~~~~ + --with-ipmi --with-freeipmi Build and install IPMI PSU driver (default: auto-detect) + This allows to monitor numerous Power Supply Units (PSU) found on servers. -Note that you need to install freeipmi (0.8.5 or higher, for nut-scanner ; and -1.0.1 or higher, for nut-ipmipsu) development package or files. + +Note that you need to install freeipmi (0.8.5 or higher, for nut-scanner; +and 1.0.1 or higher, for nut-ipmipsu) development package or files. + +I2C bus drivers +~~~~~~~~~~~~~~~ --with-linux_i2c Build and install i2c drivers (default: auto-detect) + Note that you need to install libi2c development package or files. - --with-dmf +Modbus drivers +~~~~~~~~~~~~~~ -Build and install DMF (data mapping file) support for snmp-ups (default: auto-detect) -Note that you need to install libneon (mandatory) and xmllint / lua development -package or files. + --with-modbus + +Build and install modbus (Serial, TCP) drivers (default: auto-detect) + +Note that you need to install libmodbus development package or files. + +Manual selection of drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ --with-drivers=,,... @@ -79,71 +124,116 @@ Specify exactly which driver or drivers to build and install (this works for serial, usb, and snmp drivers, and overrides the preceding three options). -As of the time of this writing (2010), there are 46 UPS drivers +As of the time of original writing (2010), there are 46 UPS drivers available. Most users will only need one, a few will need two or three, and very few people will need all of them. To save time during the compile and disk space later on, you can use this option to just build and install a subset of the drivers. -To select mge-shut and usbhid-ups, you'd do this: +For example, to select `mge-shut` and `usbhid-ups`, you'd do this: --with-drivers=apcsmart,usbhid-ups If you need to build more drivers later on, you will need to rerun -configure with a different list. To make it build all of the -drivers from scratch again, run 'make clean' before starting. +`configure` with a different list. To make it build all of the +drivers from scratch again, run `make clean` before starting. Optional features ----------------- +CGI client interface +~~~~~~~~~~~~~~~~~~~~ + --with-cgi (default: no) Build and install the optional CGI programs, HTML files, and sample CGI configuration files. This is not enabled by default, as they -are only useful on web servers. See data/html/README for additional +are only useful on web servers. See link:data/html/README[] for additional information on how to set up CGI programs. +Pretty documentation and man pages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-doc= (default: no) Build and install NUT documentation file(s). -The possible values are "html-single" for single page HTML, "html-chunked" -for multi pages HTML, "pdf" for a PDF file, and "man" for the usual manpages. +This feature requires AsciiDoc 8.6.3 or newer (see https://asciidoc.org). -If the "--with-doc" argument is passed without a list, or lists just "=yes" -or "=all", it enables all supported formats with a "=yes". +The possible documentation type values are: -An (explicit!) "--with-doc=auto" argument tries to enable all supported -formats with an "=auto". +* `html-single` for single page HTML, +* `html-chunked` for multi-paged HTML, +* `pdf` for a PDF file, and +* `man` for the usual manpages. -A "--with-doc=no" quietly skips generation of all types of documentation, -including manpages. +Other values understood for this option are listed below: + +* If the `--with-doc` argument is passed without a list, or specifies + just `=yes` or `=all`, it enables all supported formats with a `=yes` + to require them. + +* An (explicit!) `--with-doc=auto` argument tries to enable all supported + formats with an `=auto` but should not fail the build if something + can not be generated. + +* A `--with-doc=no` quietly skips generation of all types of documentation, + including manpages. + +* `--with-doc=skip` is used to configure some of the `make distcheck*` + scenarios to re-use man page files built and distributed by the main + build and not waste time on re-generation of those. Multiple documentation format values can be specified, separated with comma. -Each such value can be suffixed with "=yes" to require building of the this -documentation format (abort configuration if tools are missing), "=auto" to +Each such value can be suffixed with `=yes` to require building of this one +documentation format (abort configuration if tools are missing), `=auto` to detect and enable if we can build it on this system (and not abort if we -can not), and "=no" (or "=skip") to explicitly skip generation of this +can not), and `=no` (or `=skip`) to explicitly skip generation of this document format even if we do have the tools to build it. If a document format is mentioned in the list without a suffix, then it is -treated as a "=yes" requirement. - -Verbose output can be enabled using: ASCIIDOC_VERBOSE=-v make +treated as a `=yes` requirement. -This feature requires AsciiDoc 8.6.3 (http://www.methods.co.nz/asciidoc). +Verbose output can be enabled using: `ASCIIDOC_VERBOSE=-v make` Example valid formats of this flag: -* `--with-doc` + +* `--with-doc` without an argument, effectively same as `--with-doc=yes` * `--with-doc=` is a valid empty list, effectively same as `--with-doc=no` * `--with-doc=auto` * `--with-doc=pdf,html-chunked` * `--with-doc=man=no,pdf=auto,html-single` +Development files +~~~~~~~~~~~~~~~~~ + --with-dev (default: no) -Build and install the upsclient and nutclient library and header files. +Build and install the upsclient and nutclient library and header files, to +build further projects against NUT (such as wmNUT client and many others). + +Options for developers +~~~~~~~~~~~~~~~~~~~~~~ + + --enable-check-NIT (default: no) + +Add `make check-NIT` to default activity of `make check` to run the +NUT Integration Testing suite. This is potentially dangerous (e.g. due +to port conflicts when running many such tests in same environment), +so not active by default. + + --enable-maintainer-mode (default: no) + +Use maintainer mode to keep `Makefile.in` and `Makefile` in sync with +ever-changing `Makefile.am` content after Git updates or editing. + + --enable-cppcheck (default: no) + +Activate recipes for static analysis with `cppcheck` tools (if available). + +I want it all! +~~~~~~~~~~~~~~ --with-all (no default) @@ -151,42 +241,71 @@ Build and install all of the above (the serial, USB, SNMP, XML/HTTP and PowerMan drivers, the CGI programs and HTML files, and the upsclient library). +Networking transport security +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-ssl (default: auto-detect) --with-nss (default: auto-detect) --with-openssl (default: auto-detect) Enable SSL support, using either Mozilla NSS or OpenSSL. + If both are present, and nothing was specified, OpenSSL support will -be preferred. Read docs/security.txt for instructions on SSL support. +be preferred. + +Read link:docs/security.txt[] for instructions on SSL support. + +NOTE: Currently the two implementations differ in supported features. + +Networking access security +~~~~~~~~~~~~~~~~~~~~~~~~~~ --with-wrap (default: auto-detect) -Enable libwrap (tcp-wrappers) support. Refer to upsd man page for -more information. +Enable libwrap (tcp-wrappers) support. + +Refer to linkman:upsd[8] man page for more information. + +Networking IPv6 +~~~~~~~~~~~~~~~ --with-ipv6 (default: auto-detect) Enable IPv6 support. +AVAHI/mDNS +~~~~~~~~~~ + --with-avahi (default: auto-detect) Build and install Avahi support, to publish NUT server availability using mDNS protocol. This requires Avahi development files for the Core and Client parts. +LibLTDL +~~~~~~~ + --with-libltdl (default: auto-detect) Enable libltdl (Libtool dlopen abstraction) support. -This is required to build nut-scanner. + +This is required to build `nut-scanner` which loads third-party libraries +dynamically, based on requested scanning options. This allows to build and +package the tool without requiring all possible dependencies to be installed +in each run-time environment. Other configuration options --------------------------- +NUT data server port +~~~~~~~~~~~~~~~~~~~~ + --with-port=PORT -Change the TCP port used by the network code. Default is 3493. +Change the TCP port used by the network code. Default is 3493 +as registered with IANA. -Ancient versions of upsd used port 3305. NUT 2.0 and up use a +Ancient versions of `upsd` used port 3305. NUT 2.0 and up use a substantially different network protocol and are not able to communicate with anything older than the 1.4 series. @@ -194,41 +313,48 @@ If you have to monitor a mixed environment, use the last 1.4 version, as it contains compatibility code for both the old "REQ" and the new "GET" versions of the protocol. +Daemon user accounts +~~~~~~~~~~~~~~~~~~~~ + --with-user= --with-group= -Programs started as root will setuid() to for somewhat -safer operation. You can override this with -u in several -programs, including upsdrvctl (and all drivers by extension), upsd, -and upsmon. The "user" directive in ups.conf overrides this at run +Programs started as `root` will `setuid()` to `` for somewhat +safer operation. You can override this with `-u ` in several +programs, including `upsdrvctl` (and all drivers by extension), `upsd`, +and `upsmon`. The "user" directive in `ups.conf` overrides this at run time for the drivers. -NOTE: upsmon does not totally drop root because it may need to +NOTE: `upsmon` does not totally drop `root` because it may need to initiate a shutdown. There is always at least a stub process -remaining with root powers. The network code runs in another +remaining with `root` powers. The network code runs in another (separate) process as the new user. -The is used for the permissions of some files, +The `` is used for the permissions of some files, particularly the hotplugging rules for USB. The idea is that the device files for any UPS devices should be readable and writable by members of that group. -The default value for both the username and groupname is "nobody". +The default value for both the username and groupname is `nobody` +(or `nogroup` on systems that have it when `configure` script runs). This was done since it's slightly better than staying around as -root. Running things as nobody is not a good idea, since it's a +`root`. Running things as `nobody` is not a good idea, since it's a hack for NFS access. You should create at least one separate user for this software. -If you use one of the --with-user and --with-group options, then +If you use one of the `--with-user` and `--with-group` options, then you have to use the other one too. -See the INSTALL.nut document and the FAQ for more on this topic. +See the link:INSTALL.nut[] document and the FAQ for more on this topic. + +Syslog facility +~~~~~~~~~~~~~~~ --with-logfacility=FACILITY Change the facility used when writing to the log file. Read the man -page for openlog to get some idea of what's available on your system. -Default is LOG_DAEMON. +page for `openlog` to get some idea of what's available on your system. +Default is `LOG_DAEMON`. Installation directories @@ -238,111 +364,122 @@ Installation directories This is a fairly standard option with GNU autoconf, and it sets the base path for most of the other install directories. The default -is /usr/local/ups, which puts everything but the state sockets in one -easy place. +is `/usr/local/ups`, which puts everything but the state sockets in one +easy place, and does not conflict with usual distribution packaging. If you like having things to be at more of a "system" level, setting -the prefix to /usr/local or even /usr might be better. +the prefix to `/usr/local` or even `/usr` might be better. --exec_prefix=PATH This sets the base path for architecture dependent files. By -default, it is the same as . +default, it is the same as ``. --sysconfdir=PATH Changes the location where NUT's configuration files are stored. -By default this path is /etc. Setting this to /etc or -/etc/ups might be useful. +By default this path is `/etc`. Setting this to `/etc/nut` or +`/etc/ups` might be useful. -The NUT_CONFPATH environment variable overrides this at run time. +The `NUT_CONFPATH` environment variable overrides this at run time. - --bindir=PATH --sbindir=PATH + --bindir=PATH Where executable files will be installed. Files that are normally -executed by root (upsd, upsmon, upssched) go to sbindir, all others -to bindir. The defaults are /bin and /sbin. +executed by root (`upsd`, `upsmon`, `upssched`) go to ``, +all others to ``. The defaults are `/sbin` and +`/bin` respectively. + +See also `--with-drvpath` below. + + --with-drvpath=PATH + +The UPS drivers will be installed to this path. By default they +install to `/bin`, i.e. `/usr/local/ups/bin`. + +You would want a location that remains mounted when most of the system +is prepared to turn off, so some distributions package NUT drivers into +`/lib/nut` or similar. See link:config-notes.txt[] detailing how to +set up system shutdown. + +The `driverpath` global directive in the `ups.conf` file overrides this +at run time. --datadir=PATH Change the data directory, i.e., where architecture independent -read-only data is installed. By default this is /share, -i.e., /usr/local/ups/share. At the moment, this directory only -holds two files -- the optional cmdvartab and driver.list. +read-only data is installed. By default this is `/share`, +i.e. `/usr/local/ups/share`. At the moment, this directory only +holds two files -- the optional `cmdvartab` and `driver.list`. --mandir=PATH Sets the base directories for the man pages. The default is -/man, i.e., /usr/local/ups/man. +`/man`, i.e. `/usr/local/ups/man`. --includedir=PATH Sets the path for include files to be installed when `--with-dev` is -selected. For example, upsclient.h is installed here. The default -is /include. +selected. For example, `upsclient.h` is installed here. The default +is `/include`. --libdir=PATH -Sets the installation path for libraries. This is just the -upsclient library for now. The default is /lib. +Sets the installation path for libraries. Depending on the build +configuration, this can include the `libupsclient`, `libnutclient`, +`libnutclientsub`, `libnutscan` and their pkg-config metadata (see +`--with-pkgconfig-dir` option). The default is `/lib`. - --with-drvpath=PATH + --with-pkgconfig-dir=PATH -The UPS drivers will be installed to this path. By default they -install to "/bin", i.e., /usr/local/ups/bin. +Where to install pkg-config `*.pc` files. This option only has an +effect if `--with-dev` is selected, and causes a pkg-config file to +be installed in the named location. The default is +`/pkgconfig`. -The "driverpath" global directive in the ups.conf file overrides this -at run time. +Use `--without-pkgconfig-dir` to disable this feature altogether. --with-cgipath=PATH The CGI programs will be installed to this path. By default, they -install to "/cgi-bin", which is usually /usr/local/ups/cgi-bin. +install to `/cgi-bin`, which is usually +`/usr/local/ups/cgi-bin`. -If you set the prefix to something like /usr, you should set the -cgipath to something else, because /usr/cgi-bin is pretty ugly and +NOTE: If you set the prefix to something like `/usr`, you should set the +`cgipath` to something else, because `/usr/cgi-bin` is pretty ugly and non-standard. The CGI programs are not built or installed by default. Use -"./configure --with-cgi" to request that they are built and +`./configure --with-cgi` to request that they are built and installed. --with-htmlpath=PATH HTML files will be installed to this path. By default, this is -"/html". Note that HTML files are only installed if ---with-cgi is selected. - - --with-pkgconfig-dir=PATH - -Where to install pkg-config *.pc files. This option only has an -effect if `--with-dev` is selected, and causes a pkg-config file to -be installed in the named location. The default is -/pkgconfig. - -Use --without-pkgconfig-dir to disable this feature altogether. +`/html`. Note that HTML files are only installed if +`--with-cgi` is selected. --with-hotplug-dir=PATH -Where to install Linux 2.4 hotplugging rules. The default is -/etc/hotplug, if that directory exists, and not to install it +Where to install Linux 2.4 hotplugging rules. The default is to use +`/etc/hotplug`, if that directory exists, and to not install it otherwise. Note that this installation directory is not a -subdirectory of by default. When installing NUT as a +subdirectory of `` by default. When installing NUT as a non-root user, you may have to override this option. -Use --without-hotplug-dir to disable this feature altogether. +Use `--without-hotplug-dir` to disable this feature altogether. --with-udev-dir=PATH Where to install Linux 2.6 hotplugging rules, for kernels that have -the "udev" mechanism. The default is /etc/udev, if that directory -exists, and not to install it otherwise. Note that this -installation directory is not a subdirectory of by +the "udev" mechanism. The default is to use `/etc/udev`, if that +directory exists, and to not install it otherwise. Note that this +installation directory is not a subdirectory of `` by default. When installing NUT as a non-root user, you may have to override this option. -Use --without-udev-dir to disable this feature altogether. +Use `--without-udev-dir` to disable this feature altogether. --with-systemdsystemunitdir=PATH @@ -350,13 +487,13 @@ Where to install Linux systemd unit definitions. Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdsystemunitdir=auto (default) to detect the settings +Use `--with-systemdsystemunitdir=auto` (default) to detect the settings using pkg-config if possible. -Use --with-systemdsystemunitdir(=yes) to require detection of these +Use `--with-systemdsystemunitdir(=yes)` to require detection of these settings with pkg-config, or fail configuration if not possible. -Use --with-systemdsystemunitdir=no to disable this feature altogether. +Use `--with-systemdsystemunitdir=no` to disable this feature altogether. --with-systemdshutdowndir=PATH @@ -364,9 +501,9 @@ Where to install Linux systemd unit definitions for shutdown handling. Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdshutdowndir to detect the settings using pkg-config. +Use `--with-systemdshutdowndir` to detect the settings using pkg-config. -Use --with-systemdshutdowndir=no to disable this feature altogether. +Use `--with-systemdshutdowndir=no` to disable this feature altogether. --with-systemdtmpfilesdir=PATH @@ -375,16 +512,17 @@ automatically created locations for PID, state and similar run-time files). Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdtmpfilesdir to detect the settings using pkg-config. +Use `--with-systemdtmpfilesdir` to detect the settings using pkg-config. -Use --with-systemdtmpfilesdir=no to disable this feature altogether. +Use `--with-systemdtmpfilesdir=no` to disable this feature altogether. --with-augeas-lenses-dir=PATH -Where to install Augeas configuration-management lenses. Only useful and valid -if you use Augeas to parse and modify configuration files. The default is -/usr/share/augeas/lenses, if that directory exists, and not to install it -otherwise. +Where to install Augeas configuration-management lenses. + +Only useful and valid if you use Augeas to parse and modify configuration +files. The default is to use `/usr/share/augeas/lenses`, if that directory +exists, and to not install it otherwise. Directories used by NUT at run-time @@ -393,29 +531,30 @@ Directories used by NUT at run-time --with-pidpath=PATH Changes the directory where pid files are stored. By default this is -/var/run. Certain programs like upsmon will leave files here. +`/var/run`. Certain programs like `upsmon` will leave files here. --with-altpidpath=PATH -Programs that normally don't have root powers, like the drivers and -upsd, write their pid files here. By default this is whatever the -statepath is, as those programs should be able to write there. +Programs that normally don't have `root` powers, like the drivers and +`upsd`, write their pid files here. By default this is whatever the +statepath (below) is, as those programs should be able to write there. -The NUT_ALTPIDPATH environment variable overrides this at run time. +The `NUT_ALTPIDPATH` environment variable overrides this at run time. --with-statepath=PATH -Change the default location of the state sockets created by the -drivers. - -The NUT_STATEPATH environment variable overrides this at run time. +Change the default location of the state sockets created by the drivers +to interact with the data server `upsd`. Default is `/var/state/ups`. -Default is /var/state/ups. +The `NUT_STATEPATH` environment variable overrides this at run time. Things the compiler might need to find -------------------------------------- +LibGD +~~~~~ + --with-pkg-config This option allows to provide a custom program name (in `PATH`) or a @@ -426,16 +565,16 @@ may also want to set `PKG_CONFIG_PATH` to match your current build. --with-gd-includes="-I/foo/bar" -If you installed gd in some place where your C preprocessor can't -find the header files, use this switch to add additional -I flags. +If you installed `libgd` in some place where your C preprocessor can't +find the header files, use this switch to add additional `-I` flags. --with-gd-libs="-L/foo/bar -labcd -lxyz" -If your copy of gd isn't linking properly, use this to give the -proper -L and -l flags to make it work. See LIBS= in gd's Makefile. +If your copy of `libgd` isn't linking properly, use this to give the +proper `-L` and `-l` flags to make it work. See `LIBS=` in gd's `Makefile`. -NOTE: the --with-gd switches are not necessary if you have gd 2.0.8 -or higher installed properly. The gdlib-config script or pkgconfig +NOTE: the `--with-gd` switches are not necessary if you have gd 2.0.8 +or higher installed properly. The `gdlib-config` script or pkg-config manifest will be detected and used by default in that situation. --with-gdlib-config @@ -445,6 +584,9 @@ a complete pathname to `gdlib-config`. This may be needed on build systems which support multiple architectures, or in cases where your distribution names this program differently. +LibUSB +~~~~~~ + --with-libusb-config This option allows to provide a custom program name (in `PATH`) or @@ -454,6 +596,9 @@ build systems which support multiple architectures or provide several versions of libusb, or in cases where your distribution names this program differently. +Various +~~~~~~~ + --with-ssl-includes, --with-usb-includes, --with-snmp-includes, --with-neon-includes, --with-libltdl-includes, --with-powerman-includes="-I/foo/bar" diff --git a/docs/daisychain.txt b/docs/daisychain.txt index f752839541..c67d2875da 100644 --- a/docs/daisychain.txt +++ b/docs/daisychain.txt @@ -82,6 +82,7 @@ device(s) that have alarms vs. nothing?) Example ^^^^^^^ + Here is an example excerpt of three PDUs, connected in daisychain mode, with one master and two slaves: @@ -164,7 +165,7 @@ in the daisychain, then you would have to adapt it the following way: Templates with multiple definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If there exist already templates in the mapping structure, such as for +If there already exist templates in the mapping structure, such as for single outlets and outlet-groups, you also need to specify the position of the daisychain device index in the OID strings for all entries in the mapping table, to indicate where the daisychain insertion point is exactly. @@ -179,8 +180,9 @@ You would have to translate it to: { "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, NULL }, -SU_TYPE_DAISY_1 indicates that the daisychain index is the 1st specifier -("%i") in the string. If it is the second one, use SU_TYPE_DAISY_2. +`SU_TYPE_DAISY_1` flag indicates that the daisychain index is the first +`%i` specifier in the OID template string. If it is the second one, use +`SU_TYPE_DAISY_2`. Devices alarms handling @@ -189,26 +191,30 @@ Devices alarms handling Two functions are available to handle alarms on daisychain devices in your driver: -* device_alarm_init(): clear the current alarm buffer -* device_alarm_commit(const int device_number): commit the current alarm +* `device_alarm_init()`: clear the current alarm buffer +* `device_alarm_commit(const int device_number)`: commit the current alarm buffer to "device..ups.alarm", and increase the count of alarms. If the current alarms buffer is empty, the count of alarm is decreased, and the variable "device..ups.alarm" is removed from publication. Once the alarm count reaches "0", the main (device.0) ups.status will also remove the "ALARM" flag. -NOTE: when implementing a new driver, the following functions have to be +[NOTE] +====== +When implementing a new driver, the following functions have to be called: -* "alarm_init()" at the beginning of the main update loop, for the whole + +* `alarm_init()` at the beginning of the main update loop, for the whole daisychain. This will set the alarm count to "0", and reinitialize all alarms, -* "device_alarm_init()" at the beginning of the per-device update loop. +* `device_alarm_init()` at the beginning of the per-device update loop. This will only clear the alarms for the current device, -* "device_alarm_commit()" at the end of the per-device update loop. +* `device_alarm_commit()` at the end of the per-device update loop. This will flush the current alarms for the current device, -* also "device_alarm_init()" at the end of the per-device update loop. +* also `device_alarm_init()` at the end of the per-device update loop. This will clear the current alarms, and ensure that this buffer will not be considered by other subsequent devices, -- "alarm_commit()" at the end of the main update loop, for the whole +* `alarm_commit()` at the end of the main update loop, for the whole daisychain. This will take care of publishing or not the "ALARM" flag -in the main ups.status (device.0, root collection). +in the main ups.status (`device.0`, root collection). +====== diff --git a/docs/design.txt b/docs/design.txt index 18b3e00bca..9655f01005 100644 --- a/docs/design.txt +++ b/docs/design.txt @@ -39,7 +39,7 @@ The SERVER will connect to the socket of each DRIVER and will request a dump at that time. It retains this data in local storage for later use. It continues to listen on the socket for additional updates. -This protocol is documented in sock-protocol.txt. +This protocol is documented in link:sock-protocol.txt[]. From the server ~~~~~~~~~~~~~~~ @@ -50,14 +50,14 @@ immediately. When a request for data arrives from a CLIENT, the SERVER looks through the internal storage for that UPS and returns the requested data if it is available. -The format for requests from the CLIENT is documented in protocol.txt. +The format for requests from the CLIENT is documented in link:protocol.txt[]. Instant commands ---------------- -Instant commands is the term given to a set of actions that result in +"Instant commands" is the term given to a set of actions that result in something happening to the UPS. Some of the common ones are -test.battery.start to initiate a battery test and test.panel.start to +`test.battery.start` to initiate a battery test and `test.panel.start` to test the front panel of the UPS. They are passed to the SERVER from a CLIENT using an authenticated @@ -69,9 +69,12 @@ information. At this point, there is no confirmation to the SERVER of the command's execution. This is (still) planned for a future release. This has been delayed since returning a response involves some potentially interesting -timing issues. Remember that upsd services clients in a round-robin +timing issues. Remember that `upsd` services clients in a round-robin fashion, so all queries must be lightweight and speedy. +NOTE: FIXME: Wasn't "TRACKING" mechanism for "INSTCMD/SET VAR" introduced +to address just this? See https://github.com/networkupstools/nut/pull/659 + Setting variables ----------------- @@ -88,6 +91,9 @@ Like the instant commands, there is currently no acknowledgement of the command's completion from the DRIVER. This, too, is planned for a future release. +NOTE: FIXME: Wasn't "TRACKING" mechanism for "INSTCMD/SET VAR" introduced +to address just this? See https://github.com/networkupstools/nut/pull/659 + Example data path ----------------- @@ -97,24 +103,24 @@ delivering the alpha message to the admin. 1. EQUIPMENT reports on battery by setting flag in status register -2. DRIVER notices this flag and stores it in the ups.status variable as +2. DRIVER notices this flag and stores it in the `ups.status` variable as OB. This update gets pushed out to any listeners via the sockets. -3. SERVER upsd sees activity on the socket, reads it, parses it, and +3. SERVER `upsd` sees activity on the socket, reads it, parses it, and commits the new data to its local version of the status variable. -4. CLIENT upsmon does a routine poll of SERVER for "ups.status" and - gets "OB". +4. CLIENT `upsmon` does a routine poll of SERVER for `ups.status` and + gets `OB`. -5. CLIENT upsmon then invokes its NOTIFYCMD which is upssched. +5. CLIENT `upsmon` then invokes its `NOTIFYCMD` which is `upssched`. -6. upssched starts up a daemon to handle a timer which will expire about +6. `upssched` starts up a daemon to handle a timer which will expire about 30 seconds into the future. 7. 30 seconds later, the timer expires since the UPS is still on battery, - and upssched calls the CMDSCRIPT upssched-cmd. + and so `upssched` calls the `CMDSCRIPT` which is `upssched-cmd`. -8. upssched-cmd parses the args and calls sendmail. +8. `upssched-cmd` parses the args and calls `sendmail`. 9. Avian carriers, smoke signals, SMTP, and some magic result in the message getting from the pager company's gateway to a transmitter @@ -122,66 +128,66 @@ delivering the alpha message to the admin. This scenario requires some configuration, obviously: -1. There's a UPS driver running. +1. There's an UPS driver running. (Whatever applies for the hardware) -2. upsd has a valid UPS entry in ups.conf for this UPS. +2. `upsd` has a valid UPS entry in 'ups.conf' for this UPS. [myups] driver = nutupsdrv port = /dev/ttySx -3. upsd has a valid user for upsmon in upsd.users. +3. `upsd` has a valid user for `upsmon` in 'upsd.users' file. [monuser] password = somepass upsmon primary -4. upsmon is set to monitor this UPS in upsmon.conf. +4. `upsmon` is set to monitor this UPS with this user in 'upsmon.conf' file. MONITOR myups@localhost 1 monuser somepass primary -5. upsmon is set to EXEC the NOTIFYCMD for the ONBATT condition in - upsmon.conf. +5. `upsmon` is set to `EXEC` the `NOTIFYCMD` for the `ONBATT` condition in + 'upsmon.conf' file. NOTIFYFLAG ONBATT EXEC -6. upsmon calls upssched as the NOTIFYCMD in upsmon.conf. +6. `upsmon` calls `upssched` as the `NOTIFYCMD` in 'upsmon.conf' file. NOTIFYCMD /path/to/upssched -7. upssched has a 30 second timer for ONBATT in upssched.conf. +7. `upssched` has a 30 second timer for `ONBATT` in 'upssched.conf' file. AT ONBATT * START-TIMER upsonbatt 30 -8. upssched calls upssched-cmd as the CMDSCRIPT in upssched.conf. +8. `upssched` calls `upssched-cmd` as the `CMDSCRIPT` in 'upssched.conf'. CMDSCRIPT /path/to/upssched-cmd -9. upssched-cmd knows what to do with "upsonbatt" as its first argument - (A quick case..esac construct, see the examples) +9. `upssched-cmd` knows what to do with `upsonbatt` keyword as its first + argument (a quick `case..esac` construct, see the examples) History ------- The oldest versions of this software (1998) had no separation between -the driver and the network server and only supported the latest APC +the driver and the network server, and only supported the latest APC Smart-UPS hardware as a result. The network protocol used brittle binary structs. This had numerous bad implications for compatibility and portability. After the driver and server were separated, data was shared through the state file concept. Status was written into a static array (the "info -array") by drivers, and that array was stored on disk. upsd would +array") by drivers, and that array was stored on disk. The `upsd` would periodically read that file into a local copy of that array. Shared memory mode was added a bit later, and that removed some of the lag from the status updates. Unfortunately, it didn't have any locking originally, and the possibility for corruption due to races existed. -mmap() support was added at some point after that, and became the -default. The drivers and upsd would mmap() the file into memory and +`mmap()` support was added at some point after that, and became the +default. The drivers and `upsd` would `mmap()` the file into memory and read or write from it. Locking was done using the state file as the token, so contention problems were avoided. This method was relatively quick, but it involved at least 3 copies of the data (driver, disk/mmap, @@ -194,12 +200,13 @@ connections and push updates asynchronously to any listeners. They also recognize a few commands. Drivers also dampen updates, and only push them out when something actually changes. -As a result, upsd no longer has to poll any files on the disk, and can -just select() all of its fds and wait for activity. When one of them is -active, it reads the fd and parses the results. Updates from the -hardware now get to upsd about as fast as they possibly can. +As a result, `upsd` no longer has to poll any files on the disk, and can +just `select()` all of its file descriptors (fds) and wait for activity. +When one of them is active, it reads the fd and parses the results. +Updates from the hardware now get to `upsd` about as fast as they possibly +can. -Drivers used to call setinfo() to change the local array, and then would -call writeinfo() to push the array onto the disk, or into the +Drivers used to call `setinfo()` to change the local array, and then would +call `writeinfo()` to push the array onto the disk, or into the mmap/shared memory space. This introduced a lag since many drivers poll quite a few variables during an update. diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index b40629f56b..5cd8f2c2f8 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -73,18 +73,19 @@ This mode allows to simulate any kind of devices, even non existing ones. Using this method, you can either replay a real life sequence, <>, or directly interact -through upsrw or by editing the device file, to modify the variables values. +through `upsrw` or by editing the device file, to modify the variables' +values. Here is an example to setup a device simulation: - install NUT as usual, if not already done -- get a simulation file (.dev) or sequence (.seq), or generate one using the -<>. Sample files are provided in the 'data' -directory of the NUT source. You can also download these from the development -repository, such as the -link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2778&view=co[evolution500.seq]. -- copy the simulation file to your sysconfig directory, like /etc/nut or -/etc/ups +- get a simulation file (`.dev`) or sequence (`.seq`), or generate one using + the <>. Sample files are provided in the + `data` directory of the NUT source. You can also download these from + the development repository, such as the + link:https://github.com/networkupstools/nut/raw/master/data/evolution500.seq[evolution500.seq]. +- copy the simulation file to your sysconfig directory, like `/etc/nut` + or `/etc/ups` - configure NUT for simulation (linkman:ups.conf[5]): + [dummy] @@ -92,7 +93,7 @@ link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2 port = evolution500.dev desc = "dummy-ups in dummy mode" + -- now start NUT, at least dummy-ups and upsd: +- now start NUT, at least `dummy-ups` and `upsd`: + $ upsdrvctl start dummy $ upsd @@ -102,13 +103,13 @@ link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2 $ upsc dummy ... + -- you can also use upsrw to modify the data: +- you can also use `upsrw` to modify the data in memory: + $ upsrw -s ups.status="OB LB" -u user -p password dummy + -- or directly edit /etc/nut/evolution500.seq. In this case, modification will -only apply according to the TIMER events and the current position in the -sequence. +- or directly edit your copy of `/etc/nut/evolution500.seq`. + In this case, modification will only apply according to the `TIMER` + events and the current position in the sequence. For more information, refer to linkman:dummy-ups[8] manual page. @@ -118,11 +119,11 @@ For more information, refer to linkman:dummy-ups[8] manual page. Device recording ---------------- -To complete dummy-ups, NUT provides a device recorder script called -'nut-recorder.sh' and located in the 'tools/' directory of the +To complete `dummy-ups`, NUT provides a device recorder script called +`nut-recorder.sh` and located in the 'tools/' directory of the NUT source tree. -This script uses 'upsc' to record device information, and stores +This script uses `upsc` to record device information, and stores these in a differential fashion every 5 seconds (by default). Its usage is the following: @@ -134,8 +135,8 @@ For example, to record information from the device 'myups' every 10 seconds: nut-recorder.sh myups@localhost myups.seq 10 During the recording, you will want to generate power events, such as power -failure and restoration. These will be tracked in the simulation files, and be -eventually be replayed by the <> driver. +failure and restoration. These will be tracked in the simulation files, and +be eventually be replayed by the <> driver. NUT core development and maintenance diff --git a/docs/developers.txt b/docs/developers.txt index 57088290dc..11f8daae49 100644 --- a/docs/developers.txt +++ b/docs/developers.txt @@ -40,7 +40,7 @@ Debugging information The `upsdebug_with_errno()`, `upsdebugx()`, `upsdebug_hex()` and `upsdebug_ascii()` routines use the global `nut_debug_level`, so you -don't have to mess around with `printf()`s and `if`s yourself. +don't have to mess around with `printf()`'s and `if`'s yourself. Use them. Memory allocation @@ -53,21 +53,21 @@ Don't use the raw calls directly. Config file parsing ~~~~~~~~~~~~~~~~~~~ -The configuration parser, called parseconf, is now up to its fourth +The configuration parser, called `parseconf`, is now up to its fourth major version. It has multiple entry points, and can handle many different jobs. It's usually used for parsing files, but it can also take input a line at a time or even a character at a time. -You must initialize a context buffer with pconf_init before using any -other parseconf function. pconf_encode is the only exception, since it -operates on a buffer you supply and is an auxiliary function. +You must initialize a context buffer with `pconf_init()` before using any +other `parseconf` function. `pconf_encode()` is the only exception, since +it operates on a buffer you supply and is an auxiliary function. Escaping special characters and quoting multiple-word elements is all handled by the state machine. Using the same code for all config files avoids code duplication. NOTE: this does not apply to drivers. Driver authors should use the -`upsdrv_makevartable()` scheme to pick up values from ups.conf. +`upsdrv_makevartable()` scheme to pick up values from 'ups.conf' file. Drivers should not have their own config files. Drivers may have their own data files, such as lists of hardware, @@ -79,13 +79,13 @@ hardware support to a driver without recompiling. vs. ~~~~~~~~~~~~~~~~~~~~~~~~~ -This is already handled by autoconf, so just include "timehead.h" and you -will get the right headers on every system. +This is already handled by autoconf, so just `#include "timehead.h"` and +you will get the right headers on every system. Device drivers -- main.c ------------------------ -The device drivers use main.c as their core. +The device drivers use `main.c` as their core. To write a new driver, you create a file with a series of support functions that will be called by main. These all have names that start @@ -93,7 +93,7 @@ with `upsdrv_`, and they will be called at different times by main depending on what needs to happen. See the <> for information on writing -drivers, and also refer to the skeletal driver in skel.c. +drivers, and also refer to the skeletal driver in `skel.c`. Portability ----------- @@ -124,7 +124,7 @@ function do_stuff(void) While this will compile and run on these newer versions, it will fail miserably for anyone on an older system. That means you must not use -it. gcc only warns about this with -pedantic. +it. `gcc` only warns about this with `-pedantic` flag. Another feature that does not work on some compilers (e.g. conforming to "ANSI C"/C89/C90 standard) is initial variable declaration inside a @@ -362,12 +362,15 @@ party dependencies and a large matrix of `CFLAGS` and compiler versions last known to work or to not (yet) work on operating systems available to that CI solution. -NOTE: The cloud Travis CI offering became effectively defunct for +[NOTE] +====== +The cloud Travis CI offering became effectively defunct for open-source projects in mid-2021, so the `.travis.yml` file in NUT codebase is not actively maintained. -+ + Local private deployments of Travis CI are possible, so if anybody does use it and has updated markup to share, they are welcome to post PRs. +====== The NUT project on GitHub has integration with Travis CI to test a large set of compiler and option combinations, covering different versions of @@ -450,15 +453,15 @@ find ways to drop out of them when we can't go any further. There's another way to program this involving a big else chunk and a bunch of braces, and it can be hard to follow. You can read this from top to bottom and have a pretty good idea of what's going on without having to -track too much { } nesting and indenting. +track too much `{ }` nesting and indenting. -We don't really care for pretentiousVariableNamingSchemes, but you can +We don't really care for `pretentiousVariableNamingSchemes`, but you can probably get away with it in your own driver that we will never have to touch. If your function or variable names start pushing important code off the right margin of the screen, expect them to meet the byte chainsaw sooner or later. -All types defined with typedef should end in "_t", because this is +All types defined with typedef should end in `_t`, because this is easier to read, and it enables tools (such as indent and emacs) to display the source code correctly. @@ -483,6 +486,14 @@ One common example for this is multi-line if condition: something_else) { -------------------------------------------------------------------------------- +which may be written without mixing tabs and spaces to indent, as: + +-------------------------------------------------------------------------------- + if (something + && something_else + ) { +-------------------------------------------------------------------------------- + Another example is tables of definitions that are better aligned with (non-leading) spaces at least between names and values not too many characters wide; it still helps to align the columns with spaces at @@ -510,13 +521,13 @@ Line breaks It is better to have lines that are longer than 80 characters than to wrap lines in random places. This makes it easier to work with tools -such as "grep", and it also lets each developer choose their own +such as `grep`, and it also lets each developer choose their own window size and tab setting without being stuck to one particular choice. Of course, this does not mean that lines should be made unnecessarily long when there is a better alternative (see the note on -pretentiousVariableNamingSchemes above). Certainly there should not +`pretentiousVariableNamingSchemes` above). Certainly there should not be more than one statement per line. Please do not use ------------------------------------------------------------------------------- @@ -575,7 +586,7 @@ possible to set a tab stop to be 3 spaces, rather than the usual 8. (Note that in the saved file, one indentation level will still correspond to one tab stop; the difference is only how the file is rendered on screen). It is even possible to set this on a -per-directory basis, by putting something like this into your .emacs +per-directory basis, by putting something like this into your `.emacs` file: ------------------------------------------------------------------------------- @@ -598,12 +609,12 @@ file: Finishing touches ~~~~~~~~~~~~~~~~~ -We like code that uses const and static liberally. If you don't need to -expose a function or global variable to the outside world, static is +We like code that uses `const` and `static` liberally. If you don't need +to expose a function or global variable to the outside world, `static` is your friend. If nobody should edit the contents of some buffer that's -behind a pointer, const keeps them honest. +behind a pointer, `const` keeps them honest. -We always compile with -Wall, so things like const and static help you +We always compile with `-Wall`, so things like `const` and `static` help you find implementation flaws. Functions that attempt to modify a constant or access something outside their scope will throw a warning or even fail to compile in some cases. This is what we want. @@ -690,9 +701,12 @@ and many people have put a lot of time and energy to improve it. Submitting patches ------------------ -Small patches that arrive in unified format (diff -u) as plain text -attachments with no HTML and a brief summary at the top are the easiest -to handle. +Current preference for suggesting changes is to open a pull request on +GitHub for the https://github.com/networkupstools/nut/ project. + +For some cases, small patches that arrive by mailing list in unified +format (`diff -u`) as plain text attachments with no HTML and a brief +summary at the top are easy to handle, but sadly also easy to overlook. If a patch is sent to the nut-upsdev mailing list, it stands a better chance of being seen immediately. However, it is likely to be dropped @@ -736,14 +750,21 @@ the driver and hardware. The same remark goes for device entries: if you add support for new models, please remember to also complete the hardware compatibility list, present -in data/driver.list.in. This will be used to generate both textual, static -HTML and dynamic searchable HTML for the website. +in link:data/driver.list.in[]. This will be used to generate both textual, +static HTML and dynamic searchable HTML for the website. Finally, don't forget about fame and glory: if you added or substantially updated a driver, your copyright belongs in the heading comment (along with existing ones). For vendor backed (or sponsored) contributions we -welcome an entry in the docs/acknowledgements.txt file as well, to track -and know the industry players who help make NUT better and more useful. +welcome an entry in the link:docs/acknowledgements.txt[] file as well, +to track and know the industry players who help make NUT better and more +useful. + +It is nice to update the link:NEWS[] file for significant development +to be seen as part of next release, as well as to update the +link:UPGRADING[] file for potentially breaking changes and similar +heads-up notes for third-party teams (distribution packagers, clients +and bindings, etc.) Source code management ---------------------- @@ -946,12 +967,13 @@ link:http://git-scm.com/course/svn.html[following link] may be of use. Building the Code ----------------- -For a developer, the NUT build process starts with `./autogen.sh`. This script -generates the `./configure` script that end users typically invoke to build -NUT. If you are making a number of changes to the NUT source tree, configuring -with the `--enable-maintainer-mode` flag will ensure that after you change -`Makefile.am`, the `Makefile.in` and `Makefile` get regenerated. At a -minimum, you will need: +For a developer, the NUT build process starts with `./autogen.sh`. + +This script generates the `./configure` script that end users typically +invoke to build NUT. If you are making a number of changes to the NUT +source tree, configuring with the `--enable-maintainer-mode` flag will +ensure that after you change a `Makefile.am`, nearby `Makefile.in` and +`Makefile` get regenerated. At a minimum, you will need at least: * autoconf * automake @@ -959,41 +981,52 @@ minimum, you will need: * Python * Perl -After running `./autogen.sh`, you can pass your local configuration options to -`./configure` and run `make` from the top-level directory. To avoid the need -for root privileges when testing new NUT code, you may wish to use -`--prefix=$HOME/local/nut --with-statepath=/tmp`. You can also keep -compilation times down by only building the driver you are currently working -on: `--with-drivers=driver1,dummy-ups`. +[NOTE] +====== +See the link:config-prereqs.txt[] for better detailed package lists for +different operating systems. + +See `ci_build.sh` for automating many practical scenarios, for easier +iterations. +====== -Before pushing your commits upstream, please run +make distcheck-light+. +Even if you do not use your distribution's packages of NUT, installing the +distribution's list of build dependencies for NUT can reduce the amount of +trial-and-error when installing dependencies. For instance, in Debian, you +can run `apt-get build-dep nut` to install all of the auto* tools as well +as any development libraries and headers. + +After running `./autogen.sh`, you can pass your local configuration +options to `./configure` and run `make` from the top-level directory. +To avoid the need for root privileges when testing new NUT code, you +may wish to use `--prefix=$HOME/local/nut --with-statepath=/tmp`. +You can also keep compilation times down by only building the driver +which you are currently working on: `--with-drivers=driver1,dummy-ups`. + +Before pushing your commits upstream, please run `make distcheck-light`. This checks that the Makefiles are not broken, that all the relevant files are distributed, and that there are no compilation or installation errors. Note that unless you specifically pass `--with-doc=skip` to `configure`, this requires all of the dependencies necessary to build the documentation -to be locally installed on your system, including asciidoc, a2x, xsltproc, -dblatex and any additional XSL stylesheets. - -Running +make distcheck-light+ is especially important if you have added or -removed files, or updated configure.ac or some Makefile.am. Remember: simply -adding a file to Git does not mean it will be distributed. To distribute a -file, you must update the corresponding Makefile.am. - -There is also +make distcheck+, which runs an even stricter set of -tests than +make distcheck-light+, but will not work unless you have all the -optional libraries and features installed. - -Finally note, that since 2017 the GitHub upstream project is monitored by -Travis CI (in addition to multi-platform buildbots which occasionally do not -work). This means that if your posted improvements are based on current NUT +to be locally installed on your system, including `asciidoc`, `a2x`, +`xsltproc`, `dblatex` and any additional XSL stylesheets. + +Running `make distcheck-light` is especially important if you have added or +removed files, or updated `configure.ac` or some `Makefile.am` file. +Remember: simply adding a file to Git does not mean it will be distributed. +To distribute a file, you must update the corresponding `Makefile.am` with +`EXTRA_DIST` entry and possibly other recipe handling. + +There is also `make distcheck`, which runs an even stricter set of +tests than `make distcheck-light`, but will not work unless you have all +of the optional third-party libraries and features installed. + +Finally note, that since 2017 the GitHub upstream project is monitored +by Travis CI (in addition to earlier multi-platform buildbots which +occasionally do not work), replaced since 2021 by a dedicated NUT CI farm. +This means that if your posted improvements are based on current NUT "master" branch, the resulting pull request should get tested for a number of scenarios automatically. If your code adds a substantial feature, consider -extending the `.travis.yml` and/or `ci_build.sh` scripts in the workspace -root to add another `BUILD_TYPE` to the matrix of tests run in parallel. - -Even if you do not use your distribution's packages of NUT, installing the -distribution's list of build dependencies for NUT can reduce the amount of -trial-and-error when installing dependencies. For instance, in Debian, you can -run `apt-get build-dep nut` to install all of the auto* tools as well as any -development libraries and headers. - +extending the `Jenkinsfile-dynamatrix` and/or `ci_build.sh` scripts in the +workspace root to add another `BUILD_TYPE` to the matrix of tests run in +parallel. diff --git a/docs/documentation.txt b/docs/documentation.txt index b8d946aa00..c588e12174 100644 --- a/docs/documentation.txt +++ b/docs/documentation.txt @@ -63,6 +63,17 @@ or as a pull request against the link:https://github.com/networkupstools/nut-ddl[NUT Devices Dumps Library] following the naming and other rules described in the DDL documentation page. +Data dumps collected by the tools above, or by `upsc` client, or by drivers +in exploratory data-dumping mode (with `-d 1` argument), can be compared by +ifdef::website[] +link:https://raw.githubusercontent.com/networkupstools/nut/master/tools/nut-dumpdiff.sh[`tools/nut-dumpdiff.sh`] +endif::website[] +ifndef::website[] +`./tools/nut-dumpdiff.sh` +endif::website[] +script from the main NUT codebase, which strips away lines with only numeric +values (aiming to minimize the risk of losing meaningful changes like counters). + Offsite Links ------------- diff --git a/docs/download.txt b/docs/download.txt index a4852bf8b2..c6d2a63a39 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -68,12 +68,10 @@ use Git or <>. Browse code ^^^^^^^^^^^ -You can also browse the "vanilla NUT" code at -link:https://github.com/networkupstools/nut[GitHub], -or at packaging sources of operating system distributions such as: - -* link:https://salsa.debian.org/debian/nut/[Debian Salsa mirror] -* link:https://src.fedoraproject.org/rpms/nut/tree/rawhide[Fedora Rawhide mirror] +You can browse the "vanilla NUT" code at the +link:https://github.com/networkupstools/nut/[Main GitHub repository for NUT sources], +and some possibly modified copies as part of packaging recipe +sources of operating system distributions, as listed below. [[Snapshots]] Snapshots @@ -81,13 +79,23 @@ Snapshots GitHub has several download links for repository snapshots (for particular tags or branches), but you will need a number of tools such as autoconf, automake -and libtool to use these snapshots. +and libtool to use these snapshots to generate the `configure` script and some +other files. + +After you `configure` the source workspace, a `make dist-hash` recipe would +create the snapshot tarballs which do not require the auto* tools, and their +checksum files, such as those available on the NUT website and attached to +link:https://github.com/networkupstools/nut/releases[GitHub Releases page]. + +///////// +TODO: #1400 to replace this with a NUT CI farm service to publish the tarballs If our Buildbot instance is behaving, you can download a snapshot which does not require auto* tools from this link:http://buildbot.networkupstools.org/snapshots[builder]. Look for the latest *[tarball]* link towards the top of the page, and be sure to check the 'Build ##' link to verify the branch name. +///////// Older versions ~~~~~~~~~~~~~~ @@ -100,45 +108,70 @@ Binary packages NOTE: The only official releases from this project are source code. -NUT is already available in the following systems: +NUT is already available in the following operating systems (and +link:https://github.com/networkupstools/nut/wiki/Links-to-distribution-packaging-recipes-and-repository-sections[likely more]): + +- link:https://repology.org/project/nut/versions[Repology report on NUT] + lists 745 entries about NUT, as of this writing - Linux: -link:https://aur.archlinux.org/packages/network-ups-tools[Arch Linux], -link:http://packages.debian.org/nut[Debian], -link:http://packages.gentoo.org/package/sys-power/nut[Gentoo Linux], -Mandriva, -link:https://apps.fedoraproject.org/packages/nut[Red Hat / Fedora], -link:http://software.opensuse.org/package/nut[Novell SUSE / openSUSE], -link:https://github.com/openwrt/packages/tree/master/net/nut[OpenWrt], -link:http://sotirov-bg.net/slackpack/search.cgi?q=nut[Slackware], -link:http://packages.ubuntu.com/nut[Ubuntu], -link:https://github.com/voidlinux/xbps-packages/blob/master/srcpkgs/network-ups-tools/template[Void Linux]. + + * link:https://github.com/42ity/nut/tree/FTY/obs[42ITy.org packaging recipes for Debian-based releases] + * link:https://salsa.debian.org/debian/nut/[Debian Salsa recipes] + and link:http://packages.debian.org/nut[Debian packages] + * link:http://packages.ubuntu.com/nut[Ubuntu packages] + * link:https://src.fedoraproject.org/rpms/nut/tree/rawhide[Fedora Rawhide recipes] + and link:https://src.fedoraproject.org/rpms/nut[Red Hat / Fedora packages] + * link:https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=network-ups-tools-git[Arch Linux recipe] + and link:https://aur.archlinux.org/packages/network-ups-tools-git[Arch Linux package info] + * link:https://gitweb.gentoo.org/repo/gentoo.git/tree/sys-power/nut[Gentoo Linux recipe] + and link:http://packages.gentoo.org/package/sys-power/nut[Gentoo Linux package info] + * link:https://build.opensuse.org/package/show/openSUSE%3AFactory/nut[Novell SUSE / openSUSE official package base recipe] + and link:https://build.opensuse.org/package/show/hardware/nut[Novell SUSE / openSUSE official package development recipe], + and link:http://software.opensuse.org/package/nut[Novell SUSE / openSUSE official package overview] + * link:https://build.opensuse.org/search?search_text=nut[Numerous other recipes on Open Build System (not only by SUSE)] + * link:https://github.com/openwrt/packages/tree/master/net/nut[OpenWRT recipes] + * link:http://sotirov-bg.net/slackpack/search.cgi?q=nut[Slackware package overview] + * link:https://github.com/void-linux/void-packages/tree/master/srcpkgs/network-ups-tools[Void Linux recipes] - BSD systems: -link:http://www.FreeBSD.org/cgi/ports.cgi?query=^nut-&stype=name[FreeBSD], -link:http://pkgsrc.se/sysutils/ups-nut[NetBSD], -link:http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/nut/[OpenBSD], -link:http://doc.freenas.org/9.3/freenas_services.html#ups[FreeNAS]. + + * link:https://cgit.freebsd.org/ports/tree/sysutils/nut-devel[FreeBSD package recipe (devel)], + link:https://cgit.freebsd.org/ports/tree/sysutils/nut[FreeBSD package recipe] + and link:http://www.FreeBSD.org/cgi/ports.cgi?query=^nut-&stype=name[FreeBSD package overview] + * link:cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/sysutils/ups-nut/[NetBSD recipe] and link:http://pkgsrc.se/sysutils/ups-nut[NetBSD package overview] + * link:http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/nut/[OpenBSD recipe] + * link:https://github.com/freenas/iocage-ports/tree/master/sysutils/nut[FreeNAS iocage-ports recipe], + link:http://doc.freenas.org/9.3/freenas_services.html#ups[FreeNAS 9.3 docs on UPS integration] + and link:https://www.ixsystems.com/documentation/freenas/11.3-U5/services.html#ups[FreeNAS 11.3-U5 docs on UPS integration] - Mac OS X: -link:http://pdb.finkproject.org/pdb/package.php/nut[Fink], -link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts] + + * link:https://github.com/fink/fink-distributions/blob/master/10.9-libcxx/stable/main/finkinfo/net/nut.info[Fink recipe] + and link:http://pdb.finkproject.org/pdb/package.php/nut[Fink package overview] + * link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts recipe] + +- illumos/Solaris: + + * link:https://github.com/OpenIndiana/oi-userland/tree/oi/hipster/components/sysutils/nut[OpenIndiana oi-userland recipe] + and link:https://pkg.openindiana.org/hipster/en/search.shtml?token=nut&action=Search[OpenIndiana latest rolling builds] - Windows (complete port, Beta): -link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] + + * link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] Java packages ------------- -The jNut package has been split into its own -link:https://github.com/networkupstools/jNut[GitHub repository]. +- The jNut package has been split into its own + link:https://github.com/networkupstools/jNut[GitHub repository]. - NUT Java support (client side, Beta) -link:http://www.networkupstools.org/package/java/jNut-0.2-SNAPSHOT.tar.gz[jNUT 0.2-SNAPSHOT] + link:http://www.networkupstools.org/package/java/jNut-0.2-SNAPSHOT.tar.gz[jNUT 0.2-SNAPSHOT] - NUT Java Web support (client side using REST, Beta) -link:http://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] + link:http://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] Virtualization packages ----------------------- diff --git a/docs/hid-subdrivers.txt b/docs/hid-subdrivers.txt index e48ecf7b95..edf2b84395 100644 --- a/docs/hid-subdrivers.txt +++ b/docs/hid-subdrivers.txt @@ -127,6 +127,36 @@ shutdown.restart), and conversions of manufacturer specific data formats. +Usage macros in drivers/hidtypes.h +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `drivers/hidtypes.h` header provides a number of macro names +for entries in the standard usage tables for Power Device +`USAGE_POW_` and Battery System `USAGE_BAT_` +data pages. + +If NUT codebase would ever need to refresh those macros, here is +some background information (based on NUT issue #1189 and PR #1290): + +These data were parsed from (a very slightly updated version of) +https://github.com/abend0c1/hidrdd/blob/master/rd.conf file, which +incorporates the complete USB-IF usage definitions for Power Device +and Battery System pages (among many others), so we didn't have to +extract the names and values from the USB-IF standards documents +(did check it all by eye though). + +The file was processed with the following chain of commands: + +------ +:; grep -e '^0084' -e '^0085' rd.conf \ + | sed 's/,.*$//;s/ *$//' \ + | sed 's/ /_/g;s/_/ /' \ + | tr '[:lower:]' '[:upper:]' \ + | sed 's/\(0085.... \)/\1USAGE_BAT_/;s/\(0084.... \)/\1USAGE_POW_/;s/\([A-Z_]*\)_PAGE/PAGE_\1/' \ + | awk '{print "#define "$2" 0x"$1}' +------ + + Writing a subdriver ~~~~~~~~~~~~~~~~~~~ @@ -195,6 +225,25 @@ subdrivers are written: - tripplite-hid.c/h +Fixing report descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is a fact of life that fellow developers make mistakes, and firmware +authors do too. In some cases there are inconsistencies about bytes seen +on the wire vs. their logical values, such value range and signedness if +interpreting them according to standard. + +NUT drivers now include a way to detect and fix up known issues in such +flawed USB report descriptors, side-stepping the standard similarly where +deemed needed. A pointer to such hook method is part of the `subdriver_t` +structure detailing each `usbhid-ups` subdriver nuances, defaulting to +a `fix_report_desc()` trivial implementation. + +For some practical examples, see e.g. `apc_fix_report_desc()` method in the +`drivers/apc-hid.c` file, and `cps_fix_report_desc()` in `drivers/cps-hid.c` +file. + + Shutting down the UPS ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/.gitignore b/docs/man/.gitignore index 0afd404808..daf5c8b157 100644 --- a/docs/man/.gitignore +++ b/docs/man/.gitignore @@ -4,3 +4,5 @@ /*.8 /*.html /tmp/ +/linkman-driver-names.txt +/linkman-drivertool-names.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 9c3935d4b4..f8c094e5c9 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -49,14 +49,20 @@ HTML_CONF_MANS = \ upsmon.conf.html \ upssched.conf.html +# NOTE: Currently SRC_DRIVERTOOL_PAGES are a separate list to generate +# a linkman-drivertool-names.txt file, but historically remain part of +# MAN/HTML_CLIENT_PAGES in the bigger picture. +SRC_DRIVERTOOL_PAGES = \ + nut-driver-enumerator.txt \ + upsdrvctl.txt \ + upsdrvsvcctl.txt + SRC_CLIENT_PAGES = \ + $(SRC_DRIVERTOOL_PAGES) \ nutupsdrv.txt \ - nut-driver-enumerator.txt \ upsc.txt \ upscmd.txt \ upsd.txt \ - upsdrvctl.txt \ - upsdrvsvcctl.txt \ upslog.txt \ upsmon.txt \ upsrw.txt \ @@ -166,7 +172,7 @@ SRC_DEV_PAGES = \ nutscan.txt \ nutscan_scan_snmp.txt \ nutscan_scan_usb.txt \ - nutscan_scan_xml_http.txt \ + nutscan_scan_xml_http_range.txt \ nutscan_scan_nut.txt \ nutscan_scan_avahi.txt \ nutscan_scan_ipmi.txt \ @@ -279,7 +285,7 @@ MAN3_DEV_PAGES = \ nutscan.3 \ nutscan_scan_snmp.3 \ nutscan_scan_usb.3 \ - nutscan_scan_xml_http.3 \ + nutscan_scan_xml_http_range.3 \ nutscan_scan_nut.3 \ nutscan_scan_avahi.3 \ nutscan_scan_ipmi.3 \ @@ -341,7 +347,7 @@ HTML_DEV_MANS = \ nutscan.html \ nutscan_scan_snmp.html \ nutscan_scan_usb.html \ - nutscan_scan_xml_http.html \ + nutscan_scan_xml_http_range.html \ nutscan_scan_nut.html \ nutscan_scan_avahi.html \ nutscan_scan_ipmi.html \ @@ -634,11 +640,15 @@ HTML_MACOSX_MANS = macosx-ups.html SRC_MODBUS_PAGES = phoenixcontact_modbus.txt \ generic_modbus.txt \ - huawei-ups2000.txt + huawei-ups2000.txt \ + socomec_jbus.txt \ + adelsystem_cbi.txt if WITH_MANS MAN_MODBUS_PAGES = phoenixcontact_modbus.8 \ generic_modbus.8 \ - huawei-ups2000.8 + huawei-ups2000.8 \ + socomec_jbus.8 \ + adelsystem_cbi.8 endif if WITH_MODBUS @@ -647,7 +657,9 @@ endif HTML_MODBUS_MANS = phoenixcontact_modbus.html \ generic_modbus.html \ - huawei-ups2000.html + huawei-ups2000.html \ + socomec_jbus.html \ + adelsystem_cbi.html SRC_LINUX_I2C_PAGES = asem.txt pijuice.txt if WITH_MANS @@ -686,14 +698,7 @@ MAN_MANS += \ $(MAN_LINUX_I2C_PAGES) endif -# distribute everything, even those not installed by default -# Note that 'dist' target requires AsciiDoc! -SRC_ALL_PAGES = \ - $(SRC_CONF_PAGES) \ - $(SRC_CLIENT_PAGES) \ - $(SRC_TOOL_PAGES) \ - $(SRC_CGI_PAGES) \ - $(SRC_DEV_PAGES) \ +SRC_DRIVERS_PAGES = \ $(SRC_SERIAL_PAGES) \ $(SRC_SNMP_PAGES) \ $(SRC_SNMP_DMF_PAGES) \ @@ -706,6 +711,16 @@ SRC_ALL_PAGES = \ $(SRC_MODBUS_PAGES) \ $(SRC_LINUX_I2C_PAGES) +# distribute everything, even those not installed by default +# Note that 'dist' target requires AsciiDoc! +SRC_ALL_PAGES = \ + $(SRC_CONF_PAGES) \ + $(SRC_CLIENT_PAGES) \ + $(SRC_TOOL_PAGES) \ + $(SRC_CGI_PAGES) \ + $(SRC_DEV_PAGES) \ + $(SRC_DRIVERS_PAGES) + EXTRA_DIST = \ $(SRC_ALL_PAGES) \ $(MAN_MANS) \ @@ -747,9 +762,54 @@ HTML_MANS = \ $(HTML_MODBUS_MANS) \ $(HTML_LINUX_I2C_MANS) +# Note: target documents, except nutupsdrv.txt itself, start the +# list of drivers with `- linkman:nutupsdrv[8]` entry +# To regenerate these files, do `make distclean` first +LINKMAN_INCLUDE_GENERATED = linkman-driver-names.txt linkman-drivertool-names.txt +LINKMAN_INCLUDE_CONSUMERS = index.txt upsd.txt nutupsdrv.txt + +linkman-driver-names.txt: + @if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$@" && test -s "$(srcdir)/$(@F)" ; then \ + ln -fs "$(srcdir)/$(@F)" "$(builddir)/" ; \ + fi ; \ + fi + @if test -s "$@" ; then exit 0 ; fi ; \ + (LC_ALL=C; LANG=C; export LC_ALL LANG; \ + for F in $(SRC_DRIVERS_PAGES) ; do echo "$$F" ; done \ + | grep -vE '^nutupsdrv\.txt$$' \ + | sort -n | uniq \ + | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ + ) > "$@" + +linkman-drivertool-names.txt: + @if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$@" && test -s "$(srcdir)/$(@F)" ; then \ + ln -fs "$(srcdir)/$(@F)" "$(builddir)/" ; \ + fi ; \ + fi + @if test -s "$@" ; then exit 0 ; fi ; \ + (LC_ALL=C; LANG=C; export LC_ALL LANG; \ + for F in $(SRC_DRIVERTOOL_PAGES) ; do echo "$$F" ; done \ + | sort -n | uniq \ + | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ + ) > "$@" + +# Dependencies are about particular filenames, since over time +# we might have several use-cases for LINKMAN_INCLUDE_GENERATED: +$(LINKMAN_INCLUDE_CONSUMERS): linkman-driver-names.txt linkman-drivertool-names.txt + +# These files are generated when we build from git source so not tracked in +# git, and not part of tarball (to be in builddir) - so not in EXTRA_DIST. +DISTCLEANFILES = $(LINKMAN_INCLUDE_GENERATED) + all: -html-man: $(HTML_MANS) index.html +all-html html-man: $(HTML_MANS) index.html + +# Have a way to build all man pages, not just those that fit currently +# configured drivers, daemons, developer aspect, etc. +all-man man-man: $(MAN_MANS) if WITH_MANS if ! SKIP_MANS @@ -838,45 +898,66 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + for F in $(LINKMAN_INCLUDE_GENERATED) ; do \ + if [ -s "./$$F" ] ; then ln -f -s "../../$$F" "./$${A2X_OUTDIR}/" ; else \ + if [ -s "$(abs_srcdir)/$$F" ] ; then ln -f -s "$(abs_srcdir)/$$F" "./$${A2X_OUTDIR}/" ; fi ; fi ; \ + done ; \ else A2X_OUTDIR='.' ; fi; \ if test -s "${builddir}/docbook-xsl.css" \ && test -r "${builddir}/docbook-xsl.css" \ && ! test -w "${builddir}/docbook-xsl.css" \ ; then chmod u+w "${builddir}/docbook-xsl.css" ; fi ; \ chmod -R u+w "./$${A2X_OUTDIR}" || true ; \ + if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$<" && test -s "$(srcdir)/`basename $<`" ; then \ + ln -fs "$(srcdir)/`basename $<`" "$(builddir)/" ; \ + fi ; \ + fi ; \ + A2X_VERBOSE="$(ASCIIDOC_VERBOSE)"; if [ "$(V)" = 1 ]; then A2X_VERBOSE="--verbose"; fi; \ } # Note that documents with sub-pages (see LIBNUTCLIENT_*_DEPS above) -# may generate miltiple files in one go... so we move "*" and hope +# may generate multiple files in one go... so we move "*" and hope # for no required hidden files (or would have to `find` them all). DOCBUILD_END = { \ if test -n "$${A2X_OUTDIR}" && test "$${A2X_OUTDIR}" != '.' ; then \ chmod -R u+w "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ + for F in $(LINKMAN_INCLUDE_GENERATED) ; do \ + rm -f "./$${A2X_OUTDIR}/$$F" || true ; \ + done ; \ mv -f "./$${A2X_OUTDIR}/$(@F)" ./ || exit ; \ mv -f "./$${A2X_OUTDIR}/"*.* ./ 2>/dev/null || true ; \ rm -rf "./$${A2X_OUTDIR}" ; \ fi ; \ } +### Regarding absolute paths in attributes below: by default asciidoc +### resolves include paths relative to the main document, so while we +### see the relative builddir as "." in the makefile, for included +### data it would mean the source directory where the .txt resides. .txt.html: @A2X_OUTDIR="tmp/man-html.$(@F).$$$$" ; \ echo " DOC-MAN-HTML Generating $@"; \ $(DOCBUILD_BEGIN) ; RES=0; \ - $(ASCIIDOC) --backend=xhtml11 \ - --attribute localdate=`TZ=UTC date +%Y-%m-%d` \ - --attribute localtime=`TZ=UTC date +%H:%M:%S` \ + $(ASCIIDOC) --backend=xhtml11 $${A2X_VERBOSE} \ + --attribute localdate="`TZ=UTC date +%Y-%m-%d`" \ + --attribute localtime="`TZ=UTC date +%H:%M:%S`" \ --attribute nutversion="@PACKAGE_VERSION@" \ + --attribute srcdir="$(abs_srcdir)" \ + --attribute builddir="$(abs_builddir)" \ -o "$${A2X_OUTDIR}/$(@F)" $< || RES=$$? ; \ $(DOCBUILD_END) ; exit $$RES ### Prior to Asciidoc ~8.6.8, the --destination-dir flag didn't seem to affect the location of the intermediate .xml file. ### This parameter is currently required; see docs/Makefile.am for more detail. -A2X_MANPAGE_OPTS = --doctype manpage --format manpage \ +A2X_MANPAGE_OPTS = --doctype manpage --format manpage $${A2X_VERBOSE} \ --xsltproc-opts="--nonet" \ --attribute mansource="Network UPS Tools" \ --attribute manversion="@PACKAGE_VERSION@" \ --attribute manmanual="NUT Manual" \ + --attribute srcdir="$(abs_srcdir)" \ + --attribute builddir="$(abs_builddir)" \ --destination-dir="$${A2X_OUTDIR}" .txt.1: diff --git a/docs/man/adelsystem_cbi.txt b/docs/man/adelsystem_cbi.txt new file mode 100644 index 0000000000..e8d1bd9e03 --- /dev/null +++ b/docs/man/adelsystem_cbi.txt @@ -0,0 +1,111 @@ +ADELSYSTEM_CBI(8) +================= + +NAME +---- + +adelsystem_cbi - Driver for the ADELSYSTEM CB/CBI DC-UPS + +SYNOPSIS +-------- + +*adelsystem_cbi* -h + +*adelsystem_cbi* -a 'DEVICE_NAME' ['OPTIONS'] + +NOTE: This man page only documents the specific features of the *adelsystem_cbi* +driver. For information about the core driver, see linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +This is the driver for the adelsystem cb/cbi dc-ups devices. + +The driver has been tested against CBI2801224A, all in one 12/24Vdc DC-UPS. + +More information about this UPS can be found here: :: +https://www.adelsystem.com/en/products/dc-ups-/ + + +EXTRA ARGUMENTS +--------------- + +This driver supports the following optional settings in the +linkman:ups.conf[5] file: + +Serial: +~~~~~~ + +*ser_baud_rate*='value':: +A integer specifying the serial port baud rate (default 9600). + +*ser_data_bit*='value':: +A integer specifying the serial port data bit (default 8). + +*ser_parity*='value':: +A character specifying the serial port parity (default N). + +*ser_stop_bit*='value':: +An integer specifying the serial port stop bit (default 1). + +Modbus: +~~~~~~ + +*dev_slave_id*='value':: +An integer specifying the device modbus slave ID (default 1). + + +CONFIGURATION +------------- + +Here is an example of adelsystem_cbi driver configuration in *ups.conf* file: +---- +[adelsystem_cbi] + driver = adelsystem_cbi + port = /dev/ttyUSB0 + desc = "adelsystem cb/cbi ups driver" + # serial settings + ser_baud_rate = 9600 + ser_parity = N + ser_data_bit = 8 + ser_stop_bit = 1 + # modbus slave id + dev_slave_id = 5 +---- + +INSTANT COMMANDS +---------------- + +This driver support the following instant commands: + +load.off:: +executes "instant poweroff" + +INSTALLATION +------------ + +This driver is not built by default. You can build it by installing +libmodbus and running `configure --with-modbus=yes`. + +You also need to give proper permissions on the local serial device +file (/dev/ttyUSB0 for example) to allow the run-time NUT driver user +account to access it. + +AUTHOR +------ + +Dimitris Economou + +SEE ALSO +-------- + +The core driver: +~~~~~~~~~~~~~~~~ + +linkman:nutupsdrv[8], linkman:ups.conf[5] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/al175.txt b/docs/man/al175.txt index 4fcd2216ef..7ba616b730 100644 --- a/docs/man/al175.txt +++ b/docs/man/al175.txt @@ -3,16 +3,19 @@ AL175(8) NAME ---- + al175 - Driver for Eltek UPS models with AL175 alarm module NOTE ---- + This man page only documents the hardware-specific features of the *al175* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *al175* driver is known to work with the following UPSes: Eltek MPSU4000 with AL175 alarm module @@ -26,11 +29,13 @@ See documentation supplied with your hardware on how to do it. EXTRA ARGUMENTS --------------- + This driver does not support any extra settings in the linkman:ups.conf[5]. INSTANT COMMANDS ---------------- + This driver supports some extra commands (see linkman:upscmd[8]): *test.battery.start*:: @@ -41,6 +46,7 @@ Stop a battery test. VARIABLES --------- + Besides status, this driver reads UPS state into following variables: - *ups.test.result* @@ -53,12 +59,14 @@ Besides status, this driver reads UPS state into following variables: KNOWN ISSUES AND BUGS --------------------- + * Shutdown is not supported. FIXME * The driver was reworked to meet the project code quality criteria, without testing on real hardware. Some bugs may have crept in. AUTHOR ------ + Kirill Smelkov SEE ALSO @@ -66,8 +74,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/apcsmart-old.txt b/docs/man/apcsmart-old.txt index f2c8f4ae51..6ca4cffe35 100644 --- a/docs/man/apcsmart-old.txt +++ b/docs/man/apcsmart-old.txt @@ -83,21 +83,27 @@ other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. -AUTHOR ------- +AUTHORS AND HISTORY +------------------- + Nigel Metheringham (drawing -heavily on the original apcsmart driver by Russell Kroll). This driver -was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 -the driver was renamed to apcsmart-old, being superseded by updated version -with new features. +heavily on the original `apcsmart` driver by Russell Kroll). + +This driver was called `newapc` for a time and was renamed in +the 1.5 series. + +In 2.6.2 the driver was renamed to `apcsmart-old`, being superseded +by updated version with new features. SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/apcsmart.txt b/docs/man/apcsmart.txt index ff20bf9a10..503ff57701 100644 --- a/docs/man/apcsmart.txt +++ b/docs/man/apcsmart.txt @@ -367,14 +367,18 @@ other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. -AUTHOR ------- +AUTHORS AND HISTORY +------------------- Nigel Metheringham (drawing -heavily on the original apcsmart driver by Russell Kroll). This driver -was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 -it was renamed to apcsmart-old, being superseded by updated version with -new features, which is maintained by Michal Soltys +heavily on the original `apcsmart` driver by Russell Kroll). + +This driver was called `newapc` for a time and was renamed in +the 1.5 series. + +In 2.6.2 it was renamed to `apcsmart-old`, being superseded by +updated version with new features, which is maintained by Michal +Soltys SEE ALSO -------- @@ -384,6 +388,7 @@ linkman:solis[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ // vim: tw=80 ai si ts=8 sts=4 sw=4 et : diff --git a/docs/man/apcupsd-ups.txt b/docs/man/apcupsd-ups.txt index 74321de793..1017bd2f84 100644 --- a/docs/man/apcupsd-ups.txt +++ b/docs/man/apcupsd-ups.txt @@ -3,17 +3,20 @@ APCUPSD-UPS(8) NAME ---- + apcupsd-ups - Driver for apcupsd client access NOTE ---- + This man page only documents the specific features of the *apcupsd-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- -This driver is a client to *apcupsd*. + +This driver is a client to *apcupsd* (another UPS monitoring project). *apcupsd-ups* acts as an *apcupsd* client, simply forwarding data. This can be useful in cases where both protocols are required in a network, @@ -37,13 +40,15 @@ For instance: BACKGROUND ---------- -This driver was originally written in one evening to allow interoperating with *apcupsd*. +This driver was originally written in one evening to allow interoperating +with *apcupsd*. SUPPORTED VARIABLES ------------------- -The following variables are translated from *apcupsd* to NUT. All times should be -converted to seconds (please file a bug if you notice a mismatch in units). +The following variables are translated from *apcupsd* to NUT. +All times should be converted to seconds (please file a bug +if you notice a mismatch in units). [width="50%",cols="m,m",options="header"] |=============================== @@ -85,6 +90,7 @@ converted to seconds (please file a bug if you notice a mismatch in units). LIMITATIONS ----------- + Access to *apcupsd* is strictly read only: no commands can be issued. This stems from the design of *apcupsd*, where the settings are changed in *apctest*. In order to run *apctest*, *apcupsd* must be stopped (and *apcupsd* @@ -92,6 +98,7 @@ exposes the UPS to the network). AUTHOR ------ + Andreas Steinmetz SEE ALSO @@ -102,6 +109,6 @@ linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The apcupsd home page: http://www.apcupsd.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The apcupsd home page: http://www.apcupsd.org/ diff --git a/docs/man/asem.txt b/docs/man/asem.txt index 5501275ad0..b45cf11a36 100644 --- a/docs/man/asem.txt +++ b/docs/man/asem.txt @@ -3,16 +3,19 @@ ASEM(8) NAME ---- + asem - driver for UPS in ASEM PB1300 NOTE ---- + This man page only documents the hardware-specific features of the *asem* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *asem* driver supports the UPS in ASEM PB1300 embedded PCs. Likely other I2C devices from the same manufacturer will work too, since this is a "custom" charger. @@ -41,6 +44,7 @@ Set the high battery threshold to 'num' volts. INSTALLATION ------------ + This driver is specific to the Linux I2C API, and requires the lm_sensors libi2c-dev or its equivalent to compile. @@ -60,11 +64,13 @@ DIAGNOSTICS KNOWN ISSUES AND BUGS --------------------- + The driver shutdown function is not implemented, so other arrangements must be made to turn off the UPS. -AUTHORS -------- +AUTHOR +------ + Giuseppe Corbelli SEE ALSO @@ -72,12 +78,13 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -PB1300 specifications: http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ - -BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* PB1300 specifications: + http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ +* BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bcmxcp.txt b/docs/man/bcmxcp.txt index 6ad8555432..037e1b0f48 100644 --- a/docs/man/bcmxcp.txt +++ b/docs/man/bcmxcp.txt @@ -8,12 +8,14 @@ bcmxcp - Driver for UPSes supporting the serial BCM/XCP protocol NOTE ---- + This man page only documents the hardware-specific features of the bcmxcp driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should recognize all serial BCM/XCP-compatible UPSes. It has been developed and tested on Powerware PW5115 and PW9120 hardware. If your UPS has a USB connection, you may also consult the linkman:bcmxcp_usb[8] driver @@ -21,6 +23,7 @@ documentation. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]. @@ -36,11 +39,13 @@ connected with. If not included in the config, it defaults to baud-hunting. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + - *shutdown_delay =* '120' - *baud_rate =* 'none' INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands: *shutdown.return*:: @@ -67,22 +72,28 @@ Access the config register to change settings. BUGS ---- + None known. AUTHOR ------ + Tore Ørpetveit SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The USB BCM/XCP driver: ~~~~~~~~~~~~~~~~~~~~~~~ + linkman:bcmxcp_usb[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bcmxcp_usb.txt b/docs/man/bcmxcp_usb.txt index 25bb5cd1ed..2eae9f5e91 100644 --- a/docs/man/bcmxcp_usb.txt +++ b/docs/man/bcmxcp_usb.txt @@ -3,10 +3,12 @@ BCMXCP_USB(8) NAME ---- + bcmxcp_usb - Experimental driver for UPSes supporting the BCM/XCP protocol over USB NOTE ---- + This man page only documents the hardware-specific features of the bcmxcp_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -14,6 +16,7 @@ This driver is a variant of the serial driver bcmxcp and uses the same core code SUPPORTED HARDWARE ------------------ + This driver should recognize all BCM/XCP-compatible UPSes that are connected via USB. It has been developed and tested on Powerware PW3501 hardware. It also has been tested on PW5110 hardware. @@ -30,10 +33,12 @@ shutdown command and actually shutting off. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + *shutdown_delay =*'120' INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands: *shutdown.return*:: @@ -56,6 +61,7 @@ BCM/XCP supports reporting of UPS statistics data. EXPERIMENTAL DRIVER ------------------- + This driver has been tagged experimental, even if it has been reported to be stable. Thus it is not suitable for production systems and it is not built by default. This is mainly due to the fact that it is a @@ -63,6 +69,7 @@ new driver. INSTALLATION ------------ + This driver is not built by default. You can build it by using "configure --with-usb=yes". Note that it will also install other USB drivers. @@ -74,6 +81,7 @@ must have execution flag set (ie using chmod +x ...). IMPLEMENTATION -------------- + bcmxcp_usb only supports 1 UPS at this time. You can put the "auto" value for port in `ups.conf`, i.e.: @@ -83,6 +91,7 @@ bcmxcp_usb only supports 1 UPS at this time. You can put the KNOWN ISSUES AND BUGS --------------------- + "Got EPERM: Operation not permitted upon driver startup" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -90,18 +99,21 @@ You have forgotten to install the hotplug files, as explained in the INSTALLATION section above. Don't forget to restart hotplug so that it applies these changes. -AUTHOR ------- -Tore Ørpetveit , -Wolfgang Ocker +AUTHORS +------- + +* Tore Ørpetveit +* Wolfgang Ocker SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/belkin.txt b/docs/man/belkin.txt index 51760d9907..57e032042d 100644 --- a/docs/man/belkin.txt +++ b/docs/man/belkin.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *belkin* driver is known to support the Regulator Pro 525 (F6C525-SER). Other similar models such as the 425 and 625 should also work. @@ -46,10 +47,12 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other Belkin drivers: ~~~~~~~~~~~~~~~~~~~~~ + linkman:belkinunv[8], linkman:blazer_ser[8], linkman:blazer_usb[8], @@ -57,4 +60,5 @@ linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/belkinunv.txt b/docs/man/belkinunv.txt index 81517fbd5c..4d8d8c288f 100644 --- a/docs/man/belkinunv.txt +++ b/docs/man/belkinunv.txt @@ -8,6 +8,7 @@ belkinunv - Driver for Belkin "Universal UPS" and compatible NOTE ---- + This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -18,6 +19,7 @@ drivers you should use. SUPPORTED HARDWARE ------------------ + The belkinunv driver is known to work with the Belkin Universal UPS models F6C800-UNV and F6C120-UNV, and is expected to work with other Belkin Universal UPS models. The driver only supports serial @@ -32,6 +34,7 @@ are supported using the linkman:genericups[8] driver with SOFT SHUTDOWN WORKAROUND ------------------------ + One problem with the Belkin Universal UPS is that it cannot enter a soft shutdown (shut down the load until AC power returns) unless the batteries are completely depleted. Thus, one cannot just shut off the @@ -73,6 +76,7 @@ startup scripts. OPTIONS ------- + See also linkman:nutupsdrv[8] for generic options. Never use the *-k* option with this driver; it does not work properly. @@ -126,6 +130,7 @@ line. VARIABLES --------- + *battery.charge*:: *battery.runtime*:: @@ -327,15 +332,22 @@ EXTRA ARGUMENTS This driver does not support any extra settings in linkman:ups.conf[5]. +AUTHOR +------ + +Peter Selinger + SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other Belkin drivers: ~~~~~~~~~~~~~~~~~~~~~ + linkman:belkinunv[8], linkman:blazer_ser[8], linkman:blazer_usb[8], @@ -343,11 +355,9 @@ linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ - - The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - - The documentation for the protocol used by this UPS: -link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] -AUTHOR ------- - -Peter Selinger +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The documentation for the protocol used by this UPS: + link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] + (link:https://networkupstools.org/protocols/belkin-universal.html[replica + on NUT site]) diff --git a/docs/man/bestfcom.txt b/docs/man/bestfcom.txt index 5213132ec5..67001851ca 100644 --- a/docs/man/bestfcom.txt +++ b/docs/man/bestfcom.txt @@ -8,12 +8,14 @@ bestfcom - Driver for Best Power Fortress/Ferrups NOTE ---- + This man page only documents the hardware-specific features of the bestfcom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Best Power Fortress/Ferrups implementing the Fortress UPS Protocol (f-command set). @@ -25,21 +27,21 @@ linkman:ups.conf[5]. AUTHORS ------- -Kent Polk (bestfcom) - -Andreas Wrede, John Stone (bestuferrups) -Grant Taylor (bestfort) - -Russell Kroll (bestups) +* Kent Polk (bestfcom) +* Andreas Wrede, John Stone (bestuferrups) +* Grant Taylor (bestfort) +* Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestfortress.txt b/docs/man/bestfortress.txt index 8bc969ac94..aac0b8b448 100644 --- a/docs/man/bestfortress.txt +++ b/docs/man/bestfortress.txt @@ -15,10 +15,12 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports old Best Fortress UPS equipment using a serial connection. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -28,22 +30,26 @@ Set the speed of the serial connection - 1200, 2400, 4800 or 9600. *max_load*='VA':: Set the full-scale value of the *ups.load* variable. -AUTHOR ------- -Holger Dietze , -Stuart D. Gathman +AUTHORS +------- + +* Holger Dietze +* Stuart D. Gathman SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The newer Best Power drivers: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:bestups[8], linkman:bestuferrups[8], linkman:bestfcom[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestuferrups.txt b/docs/man/bestuferrups.txt index a84ebe4666..cfe857db89 100644 --- a/docs/man/bestuferrups.txt +++ b/docs/man/bestuferrups.txt @@ -8,12 +8,14 @@ bestuferrups - Driver for Best Power Micro-Ferrups NOTE ---- + This man page only documents the hardware-specific features of the bestuferrups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Best Power Micro-Ferrups ME3100, probably other similar models too. EXTRA ARGUMENTS @@ -24,19 +26,20 @@ linkman:ups.conf[5]. AUTHORS ------- -Andreas Wrede, John Stone (bestuferrups) - -Grant Taylor (bestfort) -Russell Kroll (bestups) +* Andreas Wrede, John Stone (bestuferrups) +* Grant Taylor (bestfort) +* Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestups.txt b/docs/man/bestups.txt index 9d80e69d64..f2904bf2d1 100644 --- a/docs/man/bestups.txt +++ b/docs/man/bestups.txt @@ -13,8 +13,22 @@ This man page only documents the hardware-specific features of the bestups driver. For information about the core driver, see linkman:nutupsdrv[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. + +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! + SUPPORTED HARDWARE ------------------ + *bestups* was designed to monitor Best Power UPS hardware like the Fortress, Fortress Telecom, Axxium Rackmount and Patriot Pro. It also recognizes and supports SOLA units such as the 325, 520 and 620. In addition, the @@ -101,17 +115,21 @@ things such as the perpetual 98.7% charge on the author's Fortress 750, even when it's been charging for weeks. You can use `nombattvolt=` in linkman:ups.conf[8] to fix this. -AUTHOR ------- -Russell Kroll, Jason White +AUTHORS +------- + +* Russell Kroll +* Jason White SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/blazer-common.txt b/docs/man/blazer-common.txt index 68d00814a9..dc53dc8feb 100644 --- a/docs/man/blazer-common.txt +++ b/docs/man/blazer-common.txt @@ -1,9 +1,22 @@ NOTE ---- + This man page only documents the hardware-specific features of the blazer driver. For information about the core driver, see linkman:nutupsdrv[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. + +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! SUPPORTED HARDWARE ------------------ @@ -304,8 +317,8 @@ The temperature and load value is known to be bogus in some models. AUTHORS ------- -Arjen de Korte , -Alexander Gordeev +* Arjen de Korte +* Alexander Gordeev SEE ALSO @@ -323,7 +336,5 @@ linkman:nutupsdrv[8], linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -The NUT HCL: http://www.networkupstools.org/stable-hcl.html - +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT HCL: http://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/clone.txt b/docs/man/clone.txt index b6be30831e..555e5c7478 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -3,22 +3,26 @@ CLONE(8) NAME ---- + clone - UPS driver clone NOTE ---- + This man page only documents the specific features of the clone driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This driver, which sits on top of another driver socket, allows users to group clients to a particular outlet of a device and deal with this output as if it was a normal UPS. EXTRA ARGUMENTS --------------- + This driver supports the following settings: *load.off*='command':: @@ -58,9 +62,11 @@ Set the remaining battery runtime when the clone UPS switches to LB IMPLEMENTATION -------------- + The port specification in the linkman:ups.conf[5] reference the driver socket that the "real" UPS driver is using. For example: +------ [realups] driver = usbhid-ups port = auto @@ -72,9 +78,11 @@ socket that the "real" UPS driver is using. For example: load.off = outlet.1.load.off load.status = outlet.1.status [...] +------ IMPORTANT --------- + Unlike a real UPS, you should *not* configure a upsmon primary mode for this driver. When a upsmon primary sees the OB LB flags and tells the upsd server it is OK to initiate the shutdown sequence, the server will latch the FSD @@ -88,6 +96,7 @@ FSD flag if needed without the help of a upsmon primary. CAVEATS ------- + The clone UPS will follow the status on the real UPS driver. You can only make the clone UPS shutdown earlier than the real UPS driver, not later. If the real UPS driver initiates a shutdown, the clone UPS driver will @@ -98,8 +107,19 @@ drivers are not affected, so if you tell the real UPS driver to shutdown the outlet of the clone UPS driver, your clients will lose power without warning. +If you use service management frameworks like systemd or SMF to manage the +dependencies between driver instances and other units, then you may have +to set up special dependencies (e.g. with systemd "drop-in" snippet files) +to queue your `clone` drivers to start after the "real" device drivers. + +////////////////////////////////////// +TODO later: declare the driver as "optional", see +https://github.com/networkupstools/nut/issues/1389 +////////////////////////////////////// + AUTHOR ------ + Arjen de Korte SEE ALSO @@ -110,6 +130,16 @@ linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] +Dummy driver: +~~~~~~~~~~~~~ + +The "repeater" mode of 'dummy-ups' driver is in some ways similar to the +'clone' driver, by relaying information from a locally or remotely running +"real" device driver (and NUT data server). + +linkman:dummy-ups[8] + Internet Resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index bf1ae18da8..e3ed2f656a 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -3,18 +3,25 @@ DUMMY-UPS(8) NAME ---- + dummy-ups - Driver for multi-purpose UPS emulation NOTE ---- + This man page only documents the specific features of the *dummy-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This program is a multi-purpose UPS emulation tool. -Its behavior depends on the running mode: "dummy" or "repeater". +Its general behavior depends on the running mode: "dummy" ("dummy-once" +or "dummy-loop"), or "repeater". +//////////////////////////////////////// +...or "meta" eventually. +//////////////////////////////////////// Dummy Mode ~~~~~~~~~~ @@ -27,6 +34,9 @@ through script files. It can be configured, launched and used as any other "real" NUT driver. This mode is mostly useful for development and testing purposes. +NOTE: See below about the differences of `dummy-once` vs. `dummy-loop` +modes -- the former may be more suitable for "interactive" uses and tests. + Repeater Mode ~~~~~~~~~~~~~ @@ -38,12 +48,21 @@ the UPS. This arrangement can also help with networked UPSes, whose network management cards can be overwhelmed with a farm of servers directly polling SNMP or other protocols every few seconds. +//////////////////////////////////////// +Future intention: Meta mode to aggregate several drivers as one device +e.g. to represent same UPS with Serial + USB + SNMP links, and/or cover +an SNMP UPS that supports different data in different MIBs. +//////////////////////////////////////// + IMPLEMENTATION -------------- The `port` specification in `ups.conf` depends on the running mode, and allows the driver to select the right mode of operation. +Since NUT v2.8.0, the `mode` specification in `ups.conf` allows users to +override the mode of operation which would be otherwise guessed by the driver. + Dummy Mode ~~~~~~~~~~ @@ -52,12 +71,55 @@ In this context, `port` in the `ups.conf` block defines a file name for the path name. In the latter case the NUT sysconfig directory (i.e. `/etc/nut`, `/usr/local/ups/etc`, ...) is prepended. +Since NUT v2.8.0 two aspects of this mode are differentiated: + +* `dummy-once` reads the specified file once to the end (interrupting for + `TIMER` lines, etc.) and does not re-process it until the filesystem + timestamp of the data file is changed; this reduces run-time stress if + you test with a lot of dummy devices, and allows use/test cases to + `upsrw` variables into the driver instance -- and they remain in memory + until the driver is restarted (or the file is touched or modified); ++ +Since NUT v2.8.0 `dummy-once` is assigned by default to files with a `*.dev` + naming pattern. + +* `dummy-loop` reads the specified file again and again, with a short sleep + between the processing cycles; for sequence files using a `TIMER` keyword + (see below), or for use/test cases which modify file contents with external + means, this allows an impression of a device whose state changes over time. ++ +Before NUT v2.8.0 this was the only aspect, so a simple `dummy` mode value + maps to this behavior for backwards compatibility. ++ +Since NUT v2.8.0 `dummy-loop` is assigned by default to files with a `*.seq` + naming pattern, and `dummy` is assigned by default to files with other + naming patterns that the driver could not classify. + +[NOTE] +====== +Said defaulting based on filename pattern can break third-party +test scripts which earlier expected `*.dev` files to work as a +looping sequence with a `TIMER` keywords to change values slowly. +Now such files should get processed to the end once. + +Specify `mode=dummy-loop` driver option or rename the data file +used in the `port` option for legacy behavior. + +Use/Test-cases which modified such files content externally should +not be impacted. +====== + For instance: - [dummy] + [dummy1] driver = dummy-ups port = evolution500.seq - desc = "dummy-ups in dummy mode" + desc = "dummy-ups in dummy-loop mode" + + [dummy2] + driver = dummy-ups + port = epdu-managed.dev + desc = "dummy-ups in dummy-once mode" This file is generally named `something.dev` or `something.seq`. It contains a list of all valid variables and associated values (you can later use `upsrw` @@ -65,7 +127,7 @@ only to modify values of these variables), and has the same format as an linkman:upsc[8] dump (`: `). So you can easily create definition files from an existing UPS using `upsc > file.dev`. -Note that the Network UPS project provides a +Note that the Network UPS project provides an extensive link:https://networkupstools.org/ddl/index.html[DDL (Devices Dumps Library)] with files which can be used for modelling real devices. Entries for the DDL library are best prepared with the @@ -78,13 +140,16 @@ available: `device.*`, `driver.*`, `ups.mfr`, `ups.model`, `ups.status` as filled by the driver itself. Some sample definition files are available in the `data` directory of the -NUT source tree, and generally in the sysconfig directory of your system -distribution. +NUT source tree, and generally in the sysconfig or share directory of your +system distribution. + +Since *dummy-ups* will usually loop on reading this file, you can dynamically +modify it with some external process to "interact" with the driver. +This will avoid message spam into your system log files, if you are +using NUT default configuration. -Since *dummy-ups* will loop on reading this file, you can dynamically modify -it with some external process to "interact" with the driver. This will avoid -message spam into your system log files, if you are using NUT default -configuration. +NOTE: By default since NUT v2.8.0, it will not loop on files in `dummy-once` +mode, e.g. those with a `.dev` extension, unless their timestamp changes. You can also use the `TIMER ` instruction to create scheduled event sequences (such files are traditionally named with the `.seq` extension). @@ -98,9 +163,9 @@ between "OL", "OB" and "OB LB" every minute: ups.status: OB LB TIMER 60 -It is wise to end the script with a `TIMER` keyword. Otherwise *dummy-ups* -will directly go back to the beginning of the file and, in particular, forget -any values you could have just set with `upsrw`. +It is wise to end the script for `dummy-loop` mode with a `TIMER` keyword. +Otherwise `dummy-ups` will directly go back to the beginning of the file +and, in particular, forget any values you could have just set with `upsrw`. Note that to avoid CPU overload with an infinite loop, the driver "sleeps" a bit between file-reading cycles (currently this delay is hardcoded to one @@ -140,7 +205,9 @@ linkman:upsrw[1] and linkman:upscmd[1] commands. Note that in simulation mode, new variables can be added on the fly, but only by adding these to the definition file (and waiting for it to be re-read). -Conversely, if you need to remove variable (such as transient ones, like +That is, the driver should not allow to define a new variable via `upsrw`. + +Conversely, if you need to remove a variable (such as transient ones, like `ups.alarm`), simply update these by setting an empty value. As a result, they will get removed from the data. @@ -151,25 +218,47 @@ BACKGROUND ---------- Dummy Mode was originally written in one evening to replace the previous -dummycons testing driver, which was too limited, and required a terminal for -interaction. +'dummycons' testing driver, which was too limited, and required a terminal +for interaction. *dummy-ups* is useful for NUT client development, and other testing purposes. -It also helps the NUT Quality Assurance effort, by automating some tests on the -NUT framework. +It also helps the NUT Quality Assurance effort, by automating some tests on +the NUT framework. It now offers a repeater mode. This will help in building the Meta UPS approach, which allows one to build a virtual device, composed of several other devices -(either UPS, PDUs). +(either UPS, PDUs), or perhaps represent the same device which supports +several communication protocols and different media (Serial, USB, SNMP...) BUGS ---- + Instant commands are not yet supported in Dummy Mode, and data need name/value checking enforcement, as well as boundaries or enumeration definition. +CAVEATS +------- + +If you use service management frameworks like systemd or SMF to manage +the dependencies between driver instances and the data server, and some +of these drivers are `dummy-ups` in repeater mode representing data +from another driver running on the same system, then you may have to +set up special dependencies (e.g. with systemd "drop-in" snippet files) +to allow your `nut-server` to start after the "real" device drivers and +before such repeater drivers (without a responding server, they would fail +to start anyway). This may also need special care in `upsd.conf` and/or +`ups.conf` files to not block the system start-up for too long while the +repeater driver has not started. + +////////////////////////////////////// +TODO later: declare the driver as "optional", see +https://github.com/networkupstools/nut/issues/1389 +////////////////////////////////////// + AUTHOR ------ + Arnaud Quette SEE ALSO @@ -180,6 +269,17 @@ linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] +Clone driver: +~~~~~~~~~~~~~ + +The "repeater" mode of 'dummy-ups' driver is in some ways similar to the +'clone' driver, which sits on top of another driver socket, and allows +users to group clients to a particular outlet of a device and deal with +this output as if it were a normal UPS. + +linkman:clone[8] + Internet Resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/etapro.txt b/docs/man/etapro.txt index 3b827e2a3f..86d921286a 100644 --- a/docs/man/etapro.txt +++ b/docs/man/etapro.txt @@ -3,33 +3,41 @@ ETAPRO(8) NAME ---- + etapro - Driver for ETA UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the etapro driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports ETA UPS equipment with the "PRO" option for smart mode. EXTRA ARGUMENTS --------------- + This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ + Marek Michalkiewicz SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/everups.txt b/docs/man/everups.txt index ca02d29a92..7700982051 100644 --- a/docs/man/everups.txt +++ b/docs/man/everups.txt @@ -3,16 +3,19 @@ EVERUPS(8) NAME ---- + everups - Driver for Ever UPS models NOTE ---- + This man page only documents the hardware-specific features of the everups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should recognize the NET *-DPC and AP *-PRO models. EXTRA ARGUMENTS @@ -30,6 +33,7 @@ don't sleep and force a reboot. AUTHOR ------ + Bartek Szady SEE ALSO @@ -37,8 +41,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/gamatronic.txt b/docs/man/gamatronic.txt index ac9db3fde7..95122954ba 100644 --- a/docs/man/gamatronic.txt +++ b/docs/man/gamatronic.txt @@ -3,16 +3,19 @@ GAMATRONIC(8) NAME ---- + gamatronic - Driver for Gamatronic UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the gamatronic driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Various - Rebuilt to work with Gamatronic UPS Units, but should recognize any UPS that speaks the SEC protocol at 1200-19200 bps. @@ -24,6 +27,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Nadav Moskovitch SEE ALSO @@ -31,8 +35,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/generic_modbus.txt b/docs/man/generic_modbus.txt index 3302b974c0..37f9de60c7 100644 --- a/docs/man/generic_modbus.txt +++ b/docs/man/generic_modbus.txt @@ -229,16 +229,19 @@ HB etc) by writing over those registers. AUTHOR ------ + Dimitris Economou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/genericups.txt b/docs/man/genericups.txt index b948d0e3dc..54c4a6b01d 100644 --- a/docs/man/genericups.txt +++ b/docs/man/genericups.txt @@ -3,15 +3,18 @@ GENERICUPS(8) NAME ---- + genericups - Driver for contact-closure UPS equipment NOTE ---- + This man page only documents the specific features of the genericups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports hardware from many different manufacturers as it only uses the very simplest of signaling schemes. Contact closure refers to a kind of interface where basic high/low signals are provided to indicate @@ -23,11 +26,13 @@ a smarter UPS. CABLING ------- + Cabling is different for every kind of UPS. See the table below for information on what is known to work with a given UPS type. EXTRA ARGUMENTS --------------- + This driver supports the following settings in the linkman:ups.conf[5]: upstype='type':: @@ -89,6 +94,7 @@ recognizes a low battery condition when DCD is not held high. TYPE INFORMATION ---------------- + The essence of a UPS definition in this driver is how it uses the serial lines that are available. These are the abbreviations you will see below: @@ -392,8 +398,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] -Internet resources -~~~~~~~~~~~~~~~~~~ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/hosts.conf.txt b/docs/man/hosts.conf.txt index 273ae98368..8d86ea8789 100644 --- a/docs/man/hosts.conf.txt +++ b/docs/man/hosts.conf.txt @@ -36,4 +36,5 @@ linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/huawei-ups2000.txt b/docs/man/huawei-ups2000.txt index 9f5d7d473e..af3835055c 100644 --- a/docs/man/huawei-ups2000.txt +++ b/docs/man/huawei-ups2000.txt @@ -342,20 +342,23 @@ Guide. AUTHOR ------ + Yifeng Li SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -Huawei UPS2000-A (1 kVA-3 kVA) User Manual: https://support.huawei.com/enterprise/en/doc/EDOC1000084260 - -Huawei UPS2000 (1 kVA-3 kVA) Modbus Protocol Development Guide: https://support.huawei.com/enterprise/en/doc/EDOC1000110696 -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* Huawei UPS2000-A (1 kVA-3 kVA) User Manual: + https://support.huawei.com/enterprise/en/doc/EDOC1000084260 +* Huawei UPS2000 (1 kVA-3 kVA) Modbus Protocol Development Guide: + https://support.huawei.com/enterprise/en/doc/EDOC1000110696 +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/index.txt b/docs/man/index.txt index 61b802caa9..65a5caf286 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -47,65 +47,16 @@ CGI programs - linkman:upsstats.cgi[8] [[Drivers]] -Drivers -~~~~~~~ +Driver control: +~~~~~~~~~~~~~~~ + +include::{builddir}/linkman-drivertool-names.txt[] + +Drivers: +~~~~~~~~ -- linkman:upsdrvctl[8] -- linkman:upsdrvsvcctl[8] -- linkman:nut-driver-enumerator[8] - -- linkman:al175[8] -- linkman:apcsmart[8] -- linkman:apcupsd-ups[8] -- linkman:asem[8] -- linkman:bcmxcp[8] -- linkman:bcmxcp_usb[8] -- linkman:belkin[8] -- linkman:belkinunv[8] -- linkman:bestfcom[8] -- linkman:bestfortress[8] -- linkman:bestuferrups[8] -- linkman:bestups[8] -- linkman:blazer_ser[8] -- linkman:blazer_usb[8] -- linkman:clone[8] -- linkman:dummy-ups[8] -- linkman:etapro[8] -- linkman:everups[8] -- linkman:gamatronic[8] -- linkman:genericups[8] -- linkman:isbmex[8] -- linkman:ivtscd[8] -- linkman:liebert[8] -- linkman:liebert-esp2[8] -- linkman:macosx-ups[8] -- linkman:masterguard[8] -- linkman:metasys[8] -- linkman:mge-shut[8] -- linkman:mge-utalk[8] -- linkman:microdowell[8] -- linkman:netxml-ups[8] -- linkman:nutdrv_atcl_usb[8] -- linkman:nutdrv_qx[8] - linkman:nutupsdrv[8] -- linkman:oneac[8] -- linkman:optiups[8] -- linkman:phoenixcontact_modbus[8] -- linkman:pijuice[8] -- linkman:powercom[8] -- linkman:powerman-pdu[8] -- linkman:powerpanel[8] -- linkman:rhino[8] -- linkman:richcomm_usb[8] -- linkman:safenet[8] -- linkman:snmp-ups[8] -- linkman:solis[8] -- linkman:tripplite[8] -- linkman:tripplitesu[8] -- linkman:tripplite_usb[8] -- linkman:usbhid-ups[8] -- linkman:upscode2[8] -- linkman:victronups[8] +include::{builddir}/linkman-driver-names.txt[] [[Developer_man]] Developer manual pages @@ -149,7 +100,7 @@ Device discovery library - linkman:nutscan[3] - linkman:nutscan_scan_usb[3] - linkman:nutscan_scan_snmp[3] -- linkman:nutscan_scan_xml_http[3] +- linkman:nutscan_scan_xml_http_range[3] - linkman:nutscan_scan_nut[3] - linkman:nutscan_scan_avahi[3] - linkman:nutscan_scan_ipmi[3] diff --git a/docs/man/isbmex.txt b/docs/man/isbmex.txt index 9b97d1629a..985f0ff429 100644 --- a/docs/man/isbmex.txt +++ b/docs/man/isbmex.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports SOLA/BASIC Mexico ISBMEX protocol UPS equipment. EXTRA ARGUMENTS @@ -25,6 +26,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Edscott Wilson Garcia SEE ALSO @@ -32,8 +34,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/ivtscd.txt b/docs/man/ivtscd.txt index 6a2c7c4beb..0696c87847 100644 --- a/docs/man/ivtscd.txt +++ b/docs/man/ivtscd.txt @@ -3,32 +3,40 @@ IVTSCD(8) NAME ---- + ivtscd - driver for the IVT Solar Controller Device NOTE ---- + This man page only documents the hardware-specific features of the *ivtscd* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This driver allows to access the IVT SCD-series devices. EXTRA ARGUMENTS --------------- + This driver does not support any extra argument. AUTHOR ------ + Arjen de Korte SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/libnutclient.txt b/docs/man/libnutclient.txt index d23b705210..668eb8349a 100644 --- a/docs/man/libnutclient.txt +++ b/docs/man/libnutclient.txt @@ -39,10 +39,12 @@ See the `nutclient.h` header for more information. ERROR HANDLING -------------- + There is currently no specific mechanism around error handling. SEE ALSO -------- + linkman:libnutclient_devices[3] linkman:libnutclient_commands[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_commands.txt b/docs/man/libnutclient_commands.txt index e3826dc33a..33a90ca559 100644 --- a/docs/man/libnutclient_commands.txt +++ b/docs/man/libnutclient_commands.txt @@ -4,9 +4,11 @@ LIBNUTCLIENT_COMMANDS(3) NAME ---- -libnutclient_commands, nutclient_get_device_commands, nutclient_has_device_command, -nutclient_get_device_command_description, nutclient_execute_device_command - -Instant command related functions in Network UPS Tools high-level client access library +libnutclient_commands, nutclient_get_device_commands, +nutclient_has_device_command, nutclient_get_device_command_description, +nutclient_execute_device_command - +Instant command related functions in Network UPS Tools high-level client +access library SYNOPSIS -------- @@ -15,33 +17,57 @@ SYNOPSIS typedef void* NUTCLIENT_t; - strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev); - int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); - char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd); - void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); + typedef char** strarr; + + strarr nutclient_get_device_commands( + NUTCLIENT_t client, + const char* dev); + + int nutclient_has_device_command( + NUTCLIENT_t client, + const char* dev, const char* cmd); + + char* nutclient_get_device_command_description( + NUTCLIENT_t client, + const char* dev, const char* cmd); + + void nutclient_execute_device_command( + NUTCLIENT_t client, + const char* dev, const char* cmd, + const char* param=""); DESCRIPTION ----------- These functions allow to manage instant commands of devices. -The *nutclient_get_device_commands()* function retrieve the list of command names for a device. +* The *nutclient_get_device_commands()* function retrieves + the list of command names for a device. ++ The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device_command* function test if the specified command is supported by the device. +* The *nutclient_has_device_command* function tests if the + specified command is supported by the device. ++ Return 1 is supported and 0 if not. -The *nutclient_get_device_command_description* function retrieve the command description, if any. +* The *nutclient_get_device_command_description* function + retrieves the command description, if any. ++ The returned string must be freed. -The *nutclient_execute_device_command* intend to execute the instant command. +* The *nutclient_execute_device_command* intends to execute + the instant command, with an optional parameter. + +Common arguments: -'dev' is the device name. +* 'dev' is the device name. -'cmd' is the instant command name. +* 'cmd' is the instant command name. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_devices.txt b/docs/man/libnutclient_devices.txt index dd69101a12..700673f04a 100644 --- a/docs/man/libnutclient_devices.txt +++ b/docs/man/libnutclient_devices.txt @@ -4,8 +4,10 @@ LIBNUTCLIENT_DEVICES(3) NAME ---- -libnutclient_devices, nutclient_get_devices, nutclient_has_device, nutclient_get_device_description - -Device related functions in Network UPS Tools high-level client access library +libnutclient_devices, nutclient_get_devices, nutclient_has_device, +nutclient_get_device_description - +Device related functions in Network UPS Tools high-level client access +library SYNOPSIS -------- @@ -14,8 +16,12 @@ SYNOPSIS typedef void* NUTCLIENT_t; + typedef char** strarr; + strarr nutclient_get_devices(NUTCLIENT_t client); + int nutclient_has_device(NUTCLIENT_t client, const char* dev); + char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev); DESCRIPTION @@ -23,21 +29,28 @@ DESCRIPTION These functions allow to manage devices. -The *nutclient_get_devices()* function retrieve the list of devices monitored by a client. +* The *nutclient_get_devices()* function retrieves the list of devices + monitored by a client. ++ The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device()* function test if a device is monitored by a client. +* The *nutclient_has_device()* function tests if a device is monitored + by a client. -The *nutclient_get_device_description()* function retrieve the device description. +* The *nutclient_get_device_description()* function retrieves the device + description. ++ The returned description string must be freed. -'dev' is the device name. +Common arguments: + +* 'dev' is the device name. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_commands[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] linkman:libnutclient_variables[3] - diff --git a/docs/man/libnutclient_general.txt b/docs/man/libnutclient_general.txt index 909549af72..7d41acf136 100644 --- a/docs/man/libnutclient_general.txt +++ b/docs/man/libnutclient_general.txt @@ -5,7 +5,8 @@ NAME ---- libnutclient_general, nutclient_destroy, strarr_alloc, strarr_free - -General and utility functions in Network UPS Tools high-level client access library +General and utility functions in Network UPS Tools high-level client +access library SYNOPSIS -------- @@ -19,6 +20,7 @@ SYNOPSIS typedef char** strarr; strarr strarr_alloc(unsigned short count); + void strarr_free(strarr arr); DESCRIPTION @@ -42,4 +44,5 @@ It also frees all pointed strings. SEE ALSO -------- + linkman:libnutclient[3] diff --git a/docs/man/libnutclient_misc.txt b/docs/man/libnutclient_misc.txt index 54624065a6..2635db4722 100644 --- a/docs/man/libnutclient_misc.txt +++ b/docs/man/libnutclient_misc.txt @@ -16,11 +16,20 @@ SYNOPSIS typedef void* NUTCLIENT_t; - void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd); + void nutclient_authenticate( + NUTCLIENT_t client, + const char* login, const char* passwd); + void nutclient_logout(NUTCLIENT_t client); + void nutclient_device_login(NUTCLIENT_t client, const char* dev); + int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); + + void nutclient_device_primary(NUTCLIENT_t client, const char* dev); + /* OBSOLETED name: */ void nutclient_device_master(NUTCLIENT_t client, const char* dev); + void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); DESCRIPTION @@ -28,9 +37,9 @@ DESCRIPTION The *nutclient_authenticate()* function authenticates the user. -'login' is the user name. +* 'login' is the user name. -'passwd' is the user password. +* 'passwd' is the user password. The *nutclient_logout()* function disconnects gracefully from the server. @@ -40,9 +49,9 @@ is drawing power from this UPS. The *nutclient_get_device_num_logins()* function retrieves the number of clients which have been logged for this device. -The *nutclient_device_master()* function makes sure that primary-mode -functions like FSD are available if necessary. (Note: API change may be -pending to rename/alias the deprecated function name) +The *nutclient_device_master()* and *nutclient_device_primary()* (note: +the former is obsoleted since NUT v2.8.0 in favor of the latter) functions +make sure that primary-mode functions like FSD are available if necessary. The *nutclient_device_forced_shutdown()* function sets the "forced shutdown" flag on the device. @@ -51,4 +60,5 @@ flag on the device. SEE ALSO -------- + linkman:libnutclient[3] diff --git a/docs/man/libnutclient_tcp.txt b/docs/man/libnutclient_tcp.txt index 86e6ba5798..299fce22b9 100644 --- a/docs/man/libnutclient_tcp.txt +++ b/docs/man/libnutclient_tcp.txt @@ -7,48 +7,64 @@ NAME libnutclient_tcp, nutclient_tcp_create_client, nutclient_tcp_is_connected, nutclient_tcp_disconnect, nutclient_tcp_reconnect, nutclient_tcp_set_timeout, nutclient_tcp_get_timeout - -TCP protocol related function for Network UPS Tools high-level client access library +TCP protocol related function for Network UPS Tools high-level client +access library SYNOPSIS -------- #include + #include /* uint16_t */ + #include /* time_t */ typedef NUTCLIENT_t NUTCLIENT_TCP_t; - NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); + NUTCLIENT_TCP_t nutclient_tcp_create_client( + const char* host, uint16_t port); + int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); + void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client); + int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); - void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); - long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); + + void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout); + + time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); DESCRIPTION ----------- -These functions allow to manage connections to linkman:upsd[8] using NUT TCP protocol. +These functions allow to manage connections to linkman:upsd[8] +using NUT TCP protocol. -The *nutclient_tcp_create_client()* function create the 'NUTCLIENT_TCP_t' context and -intend to connect to upsd at 'host' and 'port'. The context must be freed by 'nutclient_destroy()' +The *nutclient_tcp_create_client()* function create the 'NUTCLIENT_TCP_t' +context and intend to connect to upsd at 'host' and 'port'. +The context must be freed by 'nutclient_destroy()' -'host' can be a sever name or a valid IPv4 or IPv6 address like "localhost", "127.0.0.1" or "::1". +* 'host' can be a sever name or a valid IPv4 or IPv6 address like +"localhost", "127.0.0.1" or "::1". -'port' is a valid TCP port, generally 3493. +* 'port' is a valid TCP port, generally 3493. The *nutclient_tcp_is_connected()* function test if the connection is valid. -The *nutclient_tcp_disconnect()* function force to disconnect the specified connection. +The *nutclient_tcp_disconnect()* function force to disconnect the specified +connection. The *nutclient_tcp_reconnect()* function force to reconnect a connection, disconnecting it if needed. -The *nutclient_tcp_set_timeout()* function set the timeout duration for I/O operations. +The *nutclient_tcp_set_timeout()* function set the timeout duration +for I/O operations. -The *nutclient_tcp_get_timeout()* function retrieve the timeout duration for I/O operations. +The *nutclient_tcp_get_timeout()* function retrieve the timeout duration +for I/O operations. 'timeout' values are specified in seconds, negatives values for blocking. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_variables.txt b/docs/man/libnutclient_variables.txt index 0ca5e4968e..fbeeb3d981 100644 --- a/docs/man/libnutclient_variables.txt +++ b/docs/man/libnutclient_variables.txt @@ -6,9 +6,11 @@ NAME libnutclient_variables, nutclient_get_device_variables, nutclient_get_device_rw_variables, nutclient_has_device_variable, -nutclient_get_device_variable_description, nutclient_get_device_variable_values, +nutclient_get_device_variable_description, +nutclient_get_device_variable_values, nutclient_set_device_variable_value, nutclient_set_device_variable_values - -Variable related functions in Network UPS Tools high-level client access library +Variable related functions in Network UPS Tools high-level client access +library SYNOPSIS -------- @@ -17,48 +19,73 @@ SYNOPSIS typedef void* NUTCLIENT_t; - strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev); - strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev); - int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var); - char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var); - strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var); - void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value); - void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values); + typedef char** strarr; + + strarr nutclient_get_device_variables(NUTCLIENT_t client, + const char* dev); + + strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, + const char* dev); + + int nutclient_has_device_variable(NUTCLIENT_t client, + const char* dev, const char* var); + + char* nutclient_get_device_variable_description(NUTCLIENT_t client, + const char* dev, const char* var); + + strarr nutclient_get_device_variable_values(NUTCLIENT_t client, + const char* dev, const char* var); + + void nutclient_set_device_variable_value(NUTCLIENT_t client, + const char* dev, const char* var, const char* value); + + void nutclient_set_device_variable_values(NUTCLIENT_t client, + const char* dev, const char* var, const strarr values); DESCRIPTION ----------- These functions allow to manage variables of devices. -The *nutclient_get_device_variables()* function retrieve the list of variables names for a device. +The *nutclient_get_device_variables()* function retrieves the list +of variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_get_device_rw_variables* function retrieve the list of read-write variables names for a device. +The *nutclient_get_device_rw_variables* function retrieves the list +of read-write variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device_variable* function test if the specified variable is supported by the device. +The *nutclient_has_device_variable* function tests if the specified +variable is supported by the device. Return 1 is supported and 0 if not. -The *nutclient_get_device_variable_description* function retrieve the variable description, if any. +The *nutclient_get_device_variable_description* function retrieves +the variable description, if any. The returned string must be freed. -The *nutclient_get_device_variable_values* returns variable values (generally only one). +The *nutclient_get_device_variable_values* returns variable values +(generally only one). The returned strarr must be freed by 'strarr_free'. -The *nutclient_set_device_variable_value* intend to set the value of the specified variable. +The *nutclient_set_device_variable_value* intends to set the value +of the specified variable. -The *nutclient_set_device_variable_values* intend to set multiple values of the specified variable. +The *nutclient_set_device_variable_values* intends to set multiple +values of the specified variable. -'dev' is the device name. +Common arguments: -'var' is the variable name. +* 'dev' is the device name. -'value' is the variable value. +* 'var' is the variable name. -'values' is the variable array of values. +* 'value' is the variable value. + +* 'values' is the variable array of values. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] diff --git a/docs/man/libupsclient-config.txt b/docs/man/libupsclient-config.txt index 938f0faf40..526276c301 100644 --- a/docs/man/libupsclient-config.txt +++ b/docs/man/libupsclient-config.txt @@ -4,10 +4,12 @@ LIBUPSCLIENT-CONFIG(1) NAME ---- -libupsclient-config - script to get information about the installed version of libupsclient +libupsclient-config - script to get information about the installed +version of libupsclient SYNOPSIS -------- + *libupsclient-config* [--version] [--libs] [--cflags] DESCRIPTION @@ -33,6 +35,7 @@ Print the compiler flags that are necessary to compile a *libupsclient* program. AUTHORS ------- + This manual page was written by Arnaud Quette . SEE ALSO @@ -42,5 +45,5 @@ linkman:upsclient[3] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/liebert-esp2.txt b/docs/man/liebert-esp2.txt index 7b71425131..e71fb72c39 100644 --- a/docs/man/liebert-esp2.txt +++ b/docs/man/liebert-esp2.txt @@ -8,12 +8,14 @@ liebert-esp2 - Driver for Liebert UPS, using the ESP-II serial protocol NOTE ---- + This man page only documents the hardware-specific features of the liebert-esp2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SPECIAL CABLING NOTE -------------------- + Be aware that an RS-232 cable with ONLY the RX, TX and ground pin must be used when interfacing with GXT2 series UPS units (and possibly others), since the handshaking lines are used for purposes other than RS-232 flow control. @@ -23,8 +25,10 @@ the proper cable/wiring with the diagram provided in the manual with your UPS. SUPPORTED HARDWARE ------------------ + Tested to work on the following units: -Liebert GXT2-6000RT208 + +* Liebert GXT2-6000RT208 This is an experimental driver. You have been warned. @@ -39,17 +43,22 @@ This driver supports the following optional settings in linkman:ups.conf[5]: *baudrate=*'num':: Set the speed of the serial connection - 1200, 2400 (default), 4800, 9600 or 19200. -AUTHOR ------- -Richard Gregory , Arjen de Korte , Nash Kaminski +AUTHORS +------- + +* Richard Gregory +* Arjen de Korte +* Nash Kaminski SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/liebert.txt b/docs/man/liebert.txt index dfaa795838..b91365989b 100644 --- a/docs/man/liebert.txt +++ b/docs/man/liebert.txt @@ -8,12 +8,14 @@ liebert - Driver for Liebert contact-closure UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the liebert driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports some Liebert UPS equipment with a contact-closure interface. This includes the UPStation GXT2 with their contact-closure cable. The smart mode ("Multilink") cable is not supported by this @@ -42,8 +44,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/macosx-ups.txt b/docs/man/macosx-ups.txt index ee30671fd1..2338be2722 100644 --- a/docs/man/macosx-ups.txt +++ b/docs/man/macosx-ups.txt @@ -3,16 +3,19 @@ MACOSX-UPS(8) NAME ---- + macosx-ups - monitor for Mac OS X built-in UPS and battery driver NOTE ---- + This man page only documents the hardware-specific features of the *macosx-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + *macosx-ups* supports any USB HID Power Device Class (PDC) UPS which is matched by the Mac OS X built-in drivers. It also can monitor a laptop internal battery as though it were an UPS. @@ -23,6 +26,7 @@ Preferences, this driver should be able to monitor it. EXTRA ARGUMENTS ---------------- + *port*=auto:: Due to changes in the way that Mac OS X lists power sources, the *port* parameter no longer has any effect. The rest of NUT still requires a value here, @@ -60,8 +64,9 @@ the battery voltage, as well as current and frequency, are typically not shown. It may be possible to monitor these values with *apcupsd* (for APC hardware only) or linkman:usbhid-ups[8]. -AUTHORS -------- +AUTHOR +------ + Charles Lepple SEE ALSO @@ -71,10 +76,11 @@ linkman:usbhid-ups[8], *pmset*(8), *regex*(3) The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The apcupsd home page: http://www.apcupsd.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The apcupsd home page: http://www.apcupsd.org/ diff --git a/docs/man/masterguard.txt b/docs/man/masterguard.txt index 3a4982316e..373fc2f450 100644 --- a/docs/man/masterguard.txt +++ b/docs/man/masterguard.txt @@ -3,19 +3,32 @@ MASTERGUARD(8) NAME ---- + masterguard - Driver for Masterguard UPS equipment -NOTES ------ +NOTE +---- + This man page only documents the hardware-specific features of the masterguard driver. For information about the core driver, see linkman:nutupsdrv[8]. -There's a much newer and more comprehensive driver based on the Q* -framework that also supports USB, see linkman:nutdrv_qx[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. + +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! SUPPORTED HARDWARE ------------------ + This driver supports Masterguard UPS equipment (serial connection only). EXTRA ARGUMENTS @@ -26,6 +39,7 @@ Cancel the shutdown procedure. AUTHOR ------ + Michael Spanier SEE ALSO @@ -33,12 +47,15 @@ SEE ALSO Newer driver: ~~~~~~~~~~~~~ + linkman:nutdrv_qx[8] The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/metasys.txt b/docs/man/metasys.txt index 77f9b36e1c..d134fe5fc8 100644 --- a/docs/man/metasys.txt +++ b/docs/man/metasys.txt @@ -32,15 +32,18 @@ The driver should support all the common features of the ups models: CABLING ------- + The needed cable is a standard pin-to-pin serial cable with at least pins 2, 3, and 5 (on DB9 connector) connected. EXTRA ARGUMENTS --------------- + This driver supports no extra arguments from linkman:ups.conf[5]. BUGS ---- + This driver has been tested on Meta System HF Millennium 820 and ally HF 1000 only. @@ -49,6 +52,7 @@ UPS are really welcome. AUTHOR ------ + Fabio Di Niro SEE ALSO @@ -56,8 +60,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/mge-shut.txt b/docs/man/mge-shut.txt index 097d4cce98..91d96b7fa8 100644 --- a/docs/man/mge-shut.txt +++ b/docs/man/mge-shut.txt @@ -90,8 +90,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/mge-utalk.txt b/docs/man/mge-utalk.txt index 049e4c7582..4dd0384b02 100644 --- a/docs/man/mge-utalk.txt +++ b/docs/man/mge-utalk.txt @@ -22,14 +22,14 @@ SUPPORTED HARDWARE mge-utalk supports the following legacy units, using the MGE UTalk protocol: - Pulsar ESV+, - Pulsar ES+, - Pulsar EL, - Pulsar EX, - Pulsar EXtreme, - Comet EXtreme, - Comet (Utalk Serial Card, ref 66060), - Galaxy (Utalk Serial Card, ref 66060). +* Pulsar ESV+ +* Pulsar ES+ +* Pulsar EL +* Pulsar EX +* Pulsar EXtreme +* Comet EXtreme +* Comet (Utalk Serial Card, ref 66060) +* Galaxy (Utalk Serial Card, ref 66060) This driver also support some newer models with backward UTalk compatibility, such as Pulsar Evolution and Pulsar EXtreme C. As these models also support @@ -75,24 +75,26 @@ This is due to the fact that these models don't support too much polling. To solve this problem, add "pollinterval=20" in ups.conf, and change the value of MAXAGE to 25 in upsd.conf, and DEADTIME to 25 in upsmon.conf. -AUTHOR ------- +AUTHORS +------- -Hans Ekkehard Plesser, -Arnaud Quette, -Martin Loyer, -Patrick Agrain, -Nicholas Reilly, -Dave Abbott, -Marek Kralewski +* Hans Ekkehard Plesser +* Arnaud Quette +* Martin Loyer +* Patrick Agrain +* Nicholas Reilly +* Dave Abbott +* Marek Kralewski SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/microdowell.txt b/docs/man/microdowell.txt index 9277379486..a23ead2aff 100644 --- a/docs/man/microdowell.txt +++ b/docs/man/microdowell.txt @@ -15,8 +15,9 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver was developed for the Enterprise Nxx and Bxx models. Other -Microdowell models may work, too. + +This driver was developed for the Enterprise Nxx and Bxx models. +Other Microdowell models may work, too. EXTRA ARGUMENTS --------------- @@ -26,6 +27,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Elio Corbolante SEE ALSO @@ -33,8 +35,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/microsol-apc.txt b/docs/man/microsol-apc.txt index 9bc3756199..74e3225445 100644 --- a/docs/man/microsol-apc.txt +++ b/docs/man/microsol-apc.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports the following UPS models: * APC Back-UPS BZ1500-BR @@ -51,19 +52,22 @@ real values, needing model-specific post-processing by the driver. Monitoring of UPS state (on-battery/on-line, critical battery) should work for other models, but is untested. -AUTHOR ------- -Ygor A. S. Regados , -Roberto P. Velloso , -Silvino B. Magalhães +AUTHORS +------- + +* Ygor A. S. Regados +* Roberto P. Velloso +* Silvino B. Magalhães SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/netxml-ups.txt b/docs/man/netxml-ups.txt index 2517cd7a83..acc672ee08 100644 --- a/docs/man/netxml-ups.txt +++ b/docs/man/netxml-ups.txt @@ -3,18 +3,21 @@ netxml-ups(8) NAME ---- + netxml-ups - Driver for Eaton / MGE Network Management Card / Proxy (XML/HTTP Protocol) equipment NOTE ---- + This man page only documents the hardware-specific features of the netxml-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -netxml-ups support all recent Eaton / MGE models which use a Network + +netxml-ups supports all recent Eaton / MGE models which use a Network Management Card or Proxy (MGE XML/HTTP protocol based). This applies to both Eaton (previously MGE Office Protection Systems) and to MGE UPS SYSTEMS. Supported card and proxy models are: @@ -32,6 +35,7 @@ parameter. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -77,6 +81,7 @@ particular driver instance restores the old behavior for those measurements. IMPLEMENTATION -------------- + The hostname of the UPS is specified with the "port" value in *ups.conf*, i.e.: @@ -94,6 +99,7 @@ linkman:ups.conf[5]) to at least 5 seconds. KNOWN ISSUES ------------ + Don't connect to the UPS through a proxy. Although it would be trivial to add support for proxies, this is not recommended and don't ask for it. Not only because it will prevent the driver to make a persistent connection to the UPS, @@ -102,6 +108,7 @@ whatever reason), the driver will no longer be able to reach the UPS. AUTHORS ------- + Arjen de Korte SEE ALSO @@ -109,8 +116,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt index 39fef960fa..6fe92dbafa 100644 --- a/docs/man/nut-driver-enumerator.txt +++ b/docs/man/nut-driver-enumerator.txt @@ -8,6 +8,7 @@ nut-driver-enumerator - tool to map NUT device entries to service instances SYNOPSIS -------- + *nut-driver-enumerator.sh* -h *nut-driver-enumerator.sh* (no args) @@ -133,10 +134,17 @@ Bad inputs, e.g. unrecognized service management framework *2*:: Absent or unreadable `ups.conf` file +AUTHOR +------ + +Jim Klimov + SEE ALSO -------- + linkman:upsdrvsvcctl[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-ipmipsu.txt b/docs/man/nut-ipmipsu.txt index b5f3ba40e6..5191ea28d9 100644 --- a/docs/man/nut-ipmipsu.txt +++ b/docs/man/nut-ipmipsu.txt @@ -100,16 +100,19 @@ Here is an example output for a Dell r610 server: AUTHOR ------ + Arnaud Quette SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -GNU FreeIPMI home page: http://www.gnu.org/software/freeipmi/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* GNU FreeIPMI home page: http://www.gnu.org/software/freeipmi/ diff --git a/docs/man/nut-recorder.txt b/docs/man/nut-recorder.txt index e20223942c..edde4ae0bb 100644 --- a/docs/man/nut-recorder.txt +++ b/docs/man/nut-recorder.txt @@ -1,17 +1,19 @@ NUT-RECORDER(8) =============== - NAME ---- + nut-recorder - utility to record device status and values changes SYNOPSIS -------- + *nut-recorder* 'device-name' [output-file] [interval] DESCRIPTION ----------- + *nut-recorder* is an utility to record sequences from running devices (such as power failures, or any other value changes) from upsd, and dump it in a .seq format. @@ -21,6 +23,7 @@ to replay the sequence. OPTIONS ------- + 'device-name':: Record the changes of this device. The format for this option is @@ -60,14 +63,18 @@ You can then define a dummy device in linkman:ups.conf[5]: AUTHOR ------ + Arnaud Quette SEE ALSO -------- +The dummy-ups driver: +~~~~~~~~~~~~~~~~~~~~~ + linkman:dummy-ups[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index 1a8796d096..e7f6a438f6 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -1,13 +1,14 @@ NUT-SCANNER(8) ============== - NAME ---- + nut-scanner - scan communication buses for NUT devices SYNOPSIS -------- + *nut-scanner* -h *nut-scanner* ['OPTIONS'] @@ -29,6 +30,7 @@ both during compilation and runtime, then SNMP discovery will be available. OPTIONS ------- + *-h*:: Display the help text. @@ -220,7 +222,7 @@ SEE ALSO linkman:ups.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index 8a0bf7ad68..90f7106a41 100644 --- a/docs/man/nut.conf.txt +++ b/docs/man/nut.conf.txt @@ -3,6 +3,7 @@ NUT.CONF(5) NAME ---- + nut.conf - UPS definitions for Network UPS Tools DESCRIPTION @@ -82,6 +83,7 @@ Please read http://bugs.debian.org/358696 for more details. EXAMPLE ------- +------ # /etc/nut/nut.conf. See nut.conf(5) MODE=none @@ -91,12 +93,13 @@ EXAMPLE UPSMON_OPTIONS="" # POWEROFF_WAIT=15m +------ INTEGRATION ----------- An init script, such as /etc/init.d/nut, is expected to source this -file in order to determine which component(s) has to be started. +file in order to determine which components have to be started. SEE ALSO -------- @@ -104,6 +107,7 @@ SEE ALSO linkman:ups.conf[5], linkman:upsd.conf[5], linkman:upsd.users[5], linkman:upsmon.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutdrv_atcl_usb.txt b/docs/man/nutdrv_atcl_usb.txt index cd99e2b77f..e955a01068 100644 --- a/docs/man/nutdrv_atcl_usb.txt +++ b/docs/man/nutdrv_atcl_usb.txt @@ -3,24 +3,31 @@ NUTDRV_ATCL_USB(8) NAME ---- + nutdrv_atcl_usb - Driver for 'ATCL FOR UPS' equipment NOTE ---- + This man page only documents the specific features of the nutdrv_atcl_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver is for UPS hardware which identifies itself as USB idVendor 0001 and idProduct 0000, and iManufacturer +ATCL FOR UPS+. Known manufacturers -include Kanji and Plexus. The UPS interface seems to be a generic USB-to-serial -chip, and for hardware manufactured by Kanji and Plexus, the microcontroller +include Kanji and Plexus. + +The UPS interface seems to be a generic USB-to-serial chip, and for +hardware manufactured by Kanji and Plexus, the microcontroller appears to emulate a traditional contact-closure interface. This translates into only three states in ups.status: *OL*, *OB* and *OB LB* (similar to -linkman:genericups[8]), with no other dynamic status values reported. Note that -these USB identifiers (including the iManufacturer string) have also been seen -on devices that are supported by the `fuji` subdriver of linkman:nutdrv_qx[8]. +linkman:genericups[8]), with no other dynamic status values reported. + +Note that these USB identifiers (including the iManufacturer string) +have also been seen on devices that are supported by the `fuji` +subdriver of linkman:nutdrv_qx[8]. EXTRA ARGUMENTS --------------- @@ -35,6 +42,7 @@ devices, such as linkman:nutdrv_qx[8]. BUGS ---- + The UPS returns the same code for "load power is off" as for "on line power". This condition will not be observed if the NUT `upsmon` in primary mode runs on the box powered by the UPS, but may be an issue if the UPS is monitored @@ -51,8 +59,9 @@ systems. See the linkman:upsmon[8] man page for more information. The solution to this problem is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command. -AUTHORS -------- +AUTHOR +------ + Charles Lepple SEE ALSO @@ -60,16 +69,20 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:genericups[8] The Qx driver: ~~~~~~~~~~~~~~ + linkman:nutdrv_qx[8] (`fuji` subdriver) Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutdrv_qx.txt b/docs/man/nutdrv_qx.txt index b2b8a6da1f..905b5615f8 100644 --- a/docs/man/nutdrv_qx.txt +++ b/docs/man/nutdrv_qx.txt @@ -21,6 +21,7 @@ The *nutdrv_qx* driver is known to work with various UPSes from 'Armac', 'Blazer', 'Energy Sistem', 'Fenton Technologies', 'General Electric', 'Hunnox', 'Masterguard', 'Mustek', 'Powercool', 'Voltronic Power' (rebranded by many, many - have I said many? - others... + Long story short: if your UPS came with a software called 'Viewpower', chances are high that it works with this driver with one of the <<_extra_arguments,'voltronic*' protocols or with the 'mecer' one>>), @@ -794,13 +795,13 @@ If you want to know the explanation of that bit you can either watch the log or AUTHORS ------- -Daniele Pezzini , -Arnaud Quette , -John Stamp , -Peter Selinger , -Arjen de Korte , -Alexander Gordeev , -Edgar Fuß +* Daniele Pezzini +* Arnaud Quette +* John Stamp +* Peter Selinger +* Arjen de Korte +* Alexander Gordeev +* Edgar Fuß SEE ALSO @@ -820,7 +821,5 @@ linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -The NUT HCL: http://www.networkupstools.org/stable-hcl.html - +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT HCL: http://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/nutdrv_siemens_sitop.txt b/docs/man/nutdrv_siemens_sitop.txt index 291096130b..193f99df44 100644 --- a/docs/man/nutdrv_siemens_sitop.txt +++ b/docs/man/nutdrv_siemens_sitop.txt @@ -3,16 +3,19 @@ NUTDRV_SIEMENS_SITOP(8) NAME ---- + nutdrv_siemens_sitop - driver for the Siemens SITOP UPS500 series UPS NOTE ---- + This man page only documents the hardware-specific features of the *nutdrv_siemens_sitop* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + *nutdrv_siemens_sitop* supports Siemens UPS models from the SITOP UPS500 series. Some models have a serial port, others have a USB port. The models with USB port actually contain a serial-over-USB chip, @@ -26,6 +29,7 @@ with USB port (Siemens product number 6EP1933-2EC41). DEVICE SETTINGS --------------- + The UPS is configured via DIP-switches. For correct functioning in combination with NUT, set the DIP-switches to the following: @@ -54,6 +58,7 @@ supply power from its batteries. USB driver ---------- + The USB-versions of the UPS contain an FTDI USB-to-serial converter chip. It is programmed with a non-standard product ID (for example _0403:e0e3_), but can still be used with the normal ftdi_sio driver. @@ -85,6 +90,7 @@ SUBSYSTEM=="tty" ATTRS{idVendor}=="0403", ATTRS{idProduct}=="e0e3" SYMLINK+="tty POLLING ------- + The UPS does not have a special 'get status' command. Instead, it continuously sends out status update messages (tens of messages per second). Every *pollinterval*, these messages are read from the serial port buffer. @@ -94,6 +100,7 @@ value is 1 (second). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings: *max_polls_without_data*='num':: @@ -126,6 +133,7 @@ has been removed for at least 1 second, and has been re-applied. INSTALLATION ------------ + Make sure that your operating system has created a serial device for the UPS. See the section *USB driver* for more information. @@ -136,6 +144,7 @@ or by adding the NUT user to a user group that can access serial devices DIAGNOSTICS ----------- + You can verify the correct functioning of the hardware, by monitoring the serial port with a terminal program, for example picocom: @@ -156,6 +165,7 @@ To exit picocom, use Ctrl-A Ctrl-X. KNOWN ISSUES AND BUGS --------------------- + *Untested models*:: As mentioned under *Supported hardware*, this driver has not been tested with all models in the SITOP UPS500 series. @@ -172,8 +182,9 @@ During normal operation, no commands are sent to the UPS at all usability. It is not sure if the serial models are affected by this issue as well. -AUTHORS -------- +AUTHOR +------ + Matthijs H. ten Berge SEE ALSO @@ -181,8 +192,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index 90ac366813..99cb2471e2 100644 --- a/docs/man/nutscan.txt +++ b/docs/man/nutscan.txt @@ -13,7 +13,8 @@ The Network UPS Tools (NUT) *nutscan* library provides the same discovery related features that are also offered by linkman:nut-scanner[8]. It enables the discovery of supported NUT devices (USB, SNMP, Eaton XML/HTTP -and IPMI) and NUT servers (using Avahi, or the classic connection method). +and IPMI) and NUT servers (either using Avahi, or the classic connection +method). DISCOVERY FUNCTIONS @@ -27,14 +28,17 @@ Then, to discover new devices, use the appropriate function: - linkman:nutscan_scan_usb[3] for supported USB devices, - linkman:nutscan_scan_snmp[3] for supported SNMP agents, -- linkman:nutscan_scan_xml_http[3] for Eaton Network Management Card, -- linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic method, -- linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS (Avahi) method, +- linkman:nutscan_scan_xml_http_range[3] for Eaton Network Management Card, +- linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic + method (search for port), +- linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS + (Avahi) method, - linkman:nutscan_scan_ipmi[3] for supported IPMI PSU. -All of these functions return a list of devices found, using the nutscan_device_t -structure. This structure is described in linkman:nutscan_add_device_to_device[3]. +All of these functions return a list of devices found, using the +`nutscan_device_t` structure. This structure is described in +linkman:nutscan_add_device_to_device[3]. Helper functions are also provided to output data using standard formats: @@ -44,17 +48,24 @@ Helper functions are also provided to output data using standard formats: ERROR HANDLING -------------- + There is currently no specific mechanism for error handling. SEE ALSO -------- + linkman:nut-scanner[8], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], -linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], +linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_add_device_to_device[3], +linkman:nutscan_add_option_to_device[3], +linkman:nutscan_cidr_to_ip[3] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + http://avahi.org/ diff --git a/docs/man/nutscan_add_device_to_device.txt b/docs/man/nutscan_add_device_to_device.txt index 5ce06c2f06..6f5aed610a 100644 --- a/docs/man/nutscan_add_device_to_device.txt +++ b/docs/man/nutscan_add_device_to_device.txt @@ -11,7 +11,9 @@ SYNOPSIS #include - nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); + nutscan_device_t * nutscan_add_device_to_device( + nutscan_device_t * first, + nutscan_device_t * second); DESCRIPTION @@ -20,24 +22,36 @@ DESCRIPTION The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; - char * driver; - char * port; - nutscan_options_t opt; - struct nutscan_device * prev; - struct nutscan_device * next; + char * driver; + char * port; + nutscan_options_t opt; + struct nutscan_device * prev; + struct nutscan_device * next; -This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. +This is a double linked list of device. Each device is described by its +`type`, its `driver` name, its `port` and any number of optional data. -The *nutscan_add_device_to_device()* concatenates 'first' and 'second' devices to a unique device. No new device is created, the two linked list are simply linked to each other. So 'first' and 'second' devices are likely to be modified by this function. +The *nutscan_add_device_to_device()* concatenates 'first' and 'second' +devices to a unique device. No new device is created, the two linked +lists are simply linked to each other. So 'first' and 'second' devices +are likely to be modified by this function. RETURN VALUE ------------ -The *nutscan_add_device_to_device()* functions returns a pointer to a device containing both passed devices. Note that it's not a new device, so it is either 'first' or 'second' which is returned. +The *nutscan_add_device_to_device()* functions returns a pointer to a +device containing both passed devices. Note that it's not a new device, +so it is either 'first' or 'second' which is returned. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_add_option_to_device.txt b/docs/man/nutscan_add_option_to_device.txt index 6cb5cc1fec..2dbf6690c0 100644 --- a/docs/man/nutscan_add_option_to_device.txt +++ b/docs/man/nutscan_add_option_to_device.txt @@ -11,7 +11,10 @@ SYNOPSIS #include - void nutscan_add_option_to_device(nutscan_device_t * device,char * option_name, char * value); + void nutscan_add_option_to_device( + nutscan_device_t * device, + char * option_name, + char * value); DESCRIPTION @@ -20,19 +23,30 @@ DESCRIPTION The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; - char * driver; - char * port; - nutscan_options_t opt; - struct nutscan_device * prev; - struct nutscan_device * next; + char * driver; + char * port; + nutscan_options_t opt; + struct nutscan_device * prev; + struct nutscan_device * next; -This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. +This is a double linked list of device. Each device is described by +its `type`, its `driver` name, its `port` and any number of optional data. -The *nutscan_add_option_to_device()* adds an optional data in the given device. Optional data are made of an 'option_name' and an associated 'value'. Copies of 'option_name' and 'value' are stored in the device, so the caller can safely free both of them. +The *nutscan_add_option_to_device()* adds an optional data in the +given device. Optional data are made of an 'option_name' and an +associated 'value'. Copies of 'option_name' and 'value' are stored +in the device, so the caller can safely free both of the original +strings used as arguments. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_cidr_to_ip.txt b/docs/man/nutscan_cidr_to_ip.txt index 99d83327d1..5f18213eeb 100644 --- a/docs/man/nutscan_cidr_to_ip.txt +++ b/docs/man/nutscan_cidr_to_ip.txt @@ -16,16 +16,28 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_cidr_to_ip()* function converts a range of IP address in the CIDR format given as a string in 'cidr', to two IPs in strings pointed by 'start_ip' and 'stop_ip' which can be used as input parameters in the scanning functions of the libnutscan API. It is the caller's responsibility to free 'start_ip' and 'stop_ip' strings. +The *nutscan_cidr_to_ip()* function converts a range of IP address in +the CIDR format given as a string in 'cidr', to two IPs in strings +pointed by 'start_ip' and 'stop_ip' which can be used as input +parameters in the scanning functions of the libnutscan API. + +It is the caller's responsibility to free 'start_ip' and 'stop_ip' strings. RETURN VALUE ------------ -The *nutscan_cidr_to_ip()* function returns 0 if an error occurred (invalid 'cidr' address) or 1 if successful. +The *nutscan_cidr_to_ip()* function returns 0 if an error occurred +(invalid 'cidr' address) or 1 if successful. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3] diff --git a/docs/man/nutscan_display_parsable.txt b/docs/man/nutscan_display_parsable.txt index 6e2cdb8fe2..9e275153be 100644 --- a/docs/man/nutscan_display_parsable.txt +++ b/docs/man/nutscan_display_parsable.txt @@ -4,7 +4,8 @@ NUTSCAN_DISPLAY_PARSABLE(3) NAME ---- -nutscan_display_parsable - Display the specified `nutscan_device_t` structure on stdout. +nutscan_display_parsable - Display the specified `nutscan_device_t` +structure on stdout. SYNOPSIS -------- @@ -16,17 +17,22 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_display_parsable()* function displays all NUT devices in 'device' to stdout. It displays them in a way that can be easily parsed which is: +The *nutscan_display_parsable()* function displays all NUT devices in +'device' to stdout. It displays them in a way that can be easily parsed +which is: -:driver="",port=""[,="",="",...] + :driver="",port=""[,="",="",...] - may be one of USB, SNMP, XML, NUT, IPMI or AVAHI. - is the name of the driver's binary corresponding to this device. - and depend on , see the corresponding driver's man page. +* may be one of USB, SNMP, XML, NUT, IPMI or AVAHI. +* is the name of the driver's binary corresponding + to this device. +* and depend on , + see the corresponding driver's man page. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_display_ups_conf.txt b/docs/man/nutscan_display_ups_conf.txt index 4d714c9309..b79c90fa0c 100644 --- a/docs/man/nutscan_display_ups_conf.txt +++ b/docs/man/nutscan_display_ups_conf.txt @@ -4,7 +4,8 @@ NUTSCAN_DISPLAY_UPS_CONF(3) NAME ---- -nutscan_display_ups_conf - Display the specified `nutscan_device_t` structure on stdout. +nutscan_display_ups_conf - Display the specified `nutscan_device_t` +structure on stdout. SYNOPSIS -------- @@ -16,11 +17,14 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_display_ups_conf()* function displays all NUT devices in 'device' to stdout. It displays them in a way that it can be directly copied into the ups.conf file. +The *nutscan_display_ups_conf()* function displays all NUT devices in +'device' to stdout. It displays them in a way that it can be directly +copied into the 'ups.conf' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index 1479f385a0..4420f362e2 100644 --- a/docs/man/nutscan_free_device.txt +++ b/docs/man/nutscan_free_device.txt @@ -4,7 +4,8 @@ NUTSCAN_FREE_DEVICE(3) NAME ---- -nutscan_free_device - Free a nutscan_device_t structure created by nutscan_new_device. +nutscan_free_device - Free a `nutscan_device_t` structure created by +`nutscan_new_device`. SYNOPSIS -------- @@ -16,11 +17,19 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_free_device()* function free a `nutscan_device_type_t` structure. Doing so, it free the whole linked list, not only the given device. +The *nutscan_free_device()* function can free a `nutscan_device_type_t` +structure. Doing so, it frees the whole linked list, not only the given +device. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index d13923d34f..8b38bedbbf 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -16,24 +16,43 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_get_serial_ports_list()* function returns a null terminated array of string generated from a port range. +The *nutscan_get_serial_ports_list()* function returns a null +terminated array of strings generated from a port range. 'ports_range' may be one of: - - a single character from 0 to 9 or a to z representing a serial communication port depending on the operating system. For instance "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; "1" is converted to COM1 on Windows; "c" is converted to /dev/ttyc on Solaris... - - a range of character in the form "X-Y". For instance "0-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 and /dev/ttyUSB1 on Linux; "1-3" will be converted to COM1, COM2 and COM3 on Windows; "a-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris. + - a single character from 0 to 9 or a to z representing a serial + communication port depending on the operating system. For instance + "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; + "1" is converted to COM1 on Windows; + "c" is converted to /dev/ttyc on Solaris... + - a range of character in the form "X-Y". For instance + "0-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 + and /dev/ttyUSB1 on Linux; + "1-3" will be converted to COM1, COM2 and COM3 on Windows; + "a-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris. - a single port name (/dev/ttyS5, COM4...). - - a list of port names separated with comas : "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"... + - a list of port names separated with commas: + "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"... -The returned array can be used in a call to *nutscan_scan_eaton_serial* to get the serial device on a system. +The returned array can be used in a call to *nutscan_scan_eaton_serial* +to get the serial device on a system. RETURN VALUE ------------ -The *nutscan_get_serial_ports_list()* function returns NULL if an error occurred (invalid port range) or a pointer to a null terminated array of string on success. +The *nutscan_get_serial_ports_list()* function returns NULL if an error +occurred (invalid port range) or a pointer to a null terminated array +of strings on success. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-serial.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_eaton_serial[3], diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index f23fe0b9ce..17e6ddf91d 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -11,14 +11,18 @@ SYNOPSIS #include - void nutscan_init(); + void nutscan_init(void); DESCRIPTION ----------- -The *nutscan_init()* function must be called at least once before using any other function of the nutscan library. +The *nutscan_init()* function must be called at least once before using +any other function of the nutscan library. -It updates the following global variables which can be used by nutscan library user to know which scan methods are available at run-time. This depends on the libraries installed on the system: +It updates the following global variables which can be used by nutscan +library user to know which scan methods are available at run-time. + +This depends on the libraries installed on the system: nutscan_avail_avahi = 1 : AVAHI scan is available nutscan_avail_ipmi = 1 : IPMI scan is available @@ -27,12 +31,19 @@ It updates the following global variables which can be used by nutscan library u nutscan_avail_usb = 1 : USB method is available nutscan_avail_xml_http = 1 : XML HTTP method is available -Note that if a method is reported as unavailable by those variables, the call to the corresponding nutscan_scan_* function will always return NULL. +Note that if a method is reported as unavailable by those variables, the +call to the corresponding `nutscan_scan_*` function will always return NULL. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-init.h' file. SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], -linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index 81ab4dbf18..81cf26d164 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -11,21 +11,29 @@ SYNOPSIS #include - nutscan_device_t * nutscan_new_device(); + nutscan_device_t * nutscan_new_device(void); DESCRIPTION ----------- -The *nutscan_new_device()* function allocates a new `nutscan_device_type_t` structure. +The *nutscan_new_device()* function allocates a new `nutscan_device_type_t` +structure. RETURN VALUE ------------ -The *nutscan_new_device()* function returns the newly allocated `nutscan_device_type_t` structure +The *nutscan_new_device()* function returns the newly allocated +`nutscan_device_type_t` structure. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], + +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3] linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3] diff --git a/docs/man/nutscan_scan_avahi.txt b/docs/man/nutscan_scan_avahi.txt index 2c830981a7..01f02e9e92 100644 --- a/docs/man/nutscan_scan_avahi.txt +++ b/docs/man/nutscan_scan_avahi.txt @@ -10,35 +10,43 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - nutscan_device_t * nutscan_scan_avahi(long usec_timeout); + nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout); DESCRIPTION ----------- -The *nutscan_scan_avahi()* function tries to detect the NUT service via mDNS, -and its associated devices. It uses the Avahi library to do so. +The *nutscan_scan_avahi()* function tries to detect the NUT service via +mDNS, and its associated devices. It uses the Avahi library to do so. You MUST call linkman:nutscan_init[3] before using this function. -This function waits up to 'usec_timeout' microseconds before considering an IP -address to be unresponsive. +This function can query perspective IP addresses using NUT protocol, and +in this case it waits up to 'usec_timeout' microseconds before considering +an IP address to be unresponsive. RETURN VALUE ------------ -The *nutscan_scan_avahi()* function returns a pointer to a `nutscan_device_t` -structure containing all found devices. It returns NULL if an error occurs, or -if no device is found. +The *nutscan_scan_avahi()* function returns a pointer to a +`nutscan_device_t` structure containing all found devices. + +It returns NULL if an error occurs, or if no device is found. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_cidr_to_ip[3], -linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_scan_eaton_serial[3] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + http://avahi.org/ diff --git a/docs/man/nutscan_scan_eaton_serial.txt b/docs/man/nutscan_scan_eaton_serial.txt index cc7c8e2e3f..068db12877 100644 --- a/docs/man/nutscan_scan_eaton_serial.txt +++ b/docs/man/nutscan_scan_eaton_serial.txt @@ -4,7 +4,8 @@ NUTSCAN_SCAN_EATON_SERIAL(3) NAME ---- -nutscan_scan_eaton_serial - Scan serial ports for Eaton devices (XCP, SHUT and Q1). +nutscan_scan_eaton_serial - Scan serial ports for Eaton devices (XCP, SHUT +and Q1). SYNOPSIS -------- @@ -16,8 +17,10 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_scan_eaton_serial()* function tries to detect NUT devices which are -compatible with Eaton's serial device protocols (SHUT, XCP and Q1 (aka blazer)). +The *nutscan_scan_eaton_serial()* function tries to detect NUT devices +which are compatible with Eaton's serial device protocols (SHUT, XCP +and Q1 (aka blazer)). + 'ports_list' is a NULL terminated array of pointers to strings containing serial device name (/dev/ttyS0, COM1, /dev/ttya...) @@ -26,12 +29,15 @@ You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ -The *nutscan_scan_eaton_serial()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_eaton_serial()* function returns a pointer to +a `nutscan_device_t` structure containing all found devices or +NULL if an error occurs or no device is found. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index 5999152fbb..9d463f477d 100644 --- a/docs/man/nutscan_scan_ipmi.txt +++ b/docs/man/nutscan_scan_ipmi.txt @@ -11,15 +11,34 @@ SYNOPSIS #include - nutscan_device_t * nutscan_scan_ipmi(void); + nutscan_device_t * nutscan_scan_ipmi( + const char * startIP, + const char * stopIP, + nutscan_ipmi_t * sec); DESCRIPTION ----------- -The *nutscan_scan_ipmi()* function is not implemented yet. +The *nutscan_scan_ipmi()* function tries to detect IPMI manageable devices. + +If 'start_ip' is NULL, the function searches for a local PSU. + +Otherwise, it searches for remote hosts that would serve IPMI protocols, +and would try to authenticate using the data in 'sec' structure. +It issues a NUT request on every IP ranging from 'startIP' to 'stopIP', +where 'stopIP' is optional. Those IP arguments may be either IPv4 or IPv6 +addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. +BUGS +---- + +Design or implementation of this function may be incomplete: until +recently, this man page stated that it was not implemented yet. +Source code is present, although some blocks are commented away +or hidden with `#if 0`. + RETURN VALUE ------------ @@ -27,8 +46,9 @@ The *nutscan_scan_ipmi()* function is not implemented yet. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_nut.txt b/docs/man/nutscan_scan_nut.txt index d0b93b700d..7d0b543c84 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -10,29 +10,41 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - 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); DESCRIPTION ----------- -The *nutscan_scan_nut()* function try to detect available NUT services and their associated devices. It issues a NUT request on every IP ranging from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. Those IP may be either IPv4 or IPv6 addresses or host names. +The *nutscan_scan_nut()* function try to detect available NUT services +and their associated devices. It issues a NUT request on every IP ranging +from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. +Those IP arguments may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. A specific 'port' number may be passed, or NULL to use the default NUT port. -This function waits up to 'usec_timeout' microseconds before considering an IP address does not respond to NUT queries. +This function waits up to 'usec_timeout' microseconds before considering +an IP address does not respond to NUT queries. RETURN VALUE ------------ -The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` +structure containing all found devices or NULL if an error occurs or no +device is found. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index 8f91f85911..ecc893ffb9 100644 --- a/docs/man/nutscan_scan_snmp.txt +++ b/docs/man/nutscan_scan_snmp.txt @@ -10,21 +10,31 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - nutscan_device_t * nutscan_scan_snmp(const char * start_ip,const char * stop_ip,long timeout, nutscan_snmp_t * sec); + nutscan_device_t * nutscan_scan_snmp( + const char * start_ip, + const char * stop_ip, + useconds_t timeout, + nutscan_snmp_t * sec); DESCRIPTION ----------- -The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP devices. It tries SNMP queries on every IP ranging from 'start_ip' to 'stop_ip'. Those IP may be either IPv4 or IPv6 addresses or host names. +The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP +devices. It tries SNMP queries on every IP ranging from 'start_ip' to +'stop_ip'. Those IP arguments may be either IPv4 or IPv6 addresses or +host names. You MUST call linkman:nutscan_init[3] before using this function. -This function waits up to 'timeout' microseconds before considering an IP address does not respond to SNMP queries. +This function waits up to 'timeout' microseconds before considering +an IP address does not respond to SNMP queries. A valid `nutscan_snmp_t` structure must be passed to this function. -The `nutscan_snmp_t` structure contains the following members which must be filled as described below: +The `nutscan_snmp_t` structure contains the following members which +must be filled as described below: char * 'community'; char * 'secLevel'; @@ -36,29 +46,41 @@ The `nutscan_snmp_t` structure contains the following members which must be fill If 'community' is not NULL, SNMP v1 request are sent using this 'community'. -If 'community' is NULL and 'secLevel' is NULL, SNMP v1 is selected and 'community' is set to "public". +If 'community' is NULL and 'secLevel' is NULL, SNMP v1 is selected +and 'community' is set to "public". -In the other cases, SNMP v3 is used. 'secLevel' may be one of `SNMP_SEC_LEVEL_NOAUTH`, `SNMP_SEC_LEVEL_AUTHNOPRIV` or `SNMP_SEC_LEVEL_AUTHPRIV`. 'secName' is the security name and must be non NULL. +In the other cases, SNMP v3 is used. 'secLevel' may be one +of `SNMP_SEC_LEVEL_NOAUTH`, `SNMP_SEC_LEVEL_AUTHNOPRIV` +or `SNMP_SEC_LEVEL_AUTHPRIV`. +'secName' is the security name and must be non NULL. -If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHNOPRIV`, 'authPassword' must be non NULL. +If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHNOPRIV`, 'authPassword' +must be non NULL. -If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHPRIV`, 'authPassword' and 'privPassword' must be non NULL. +If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHPRIV`, 'authPassword' +and 'privPassword' must be non NULL. -If 'authProtocol' is NULL, MD5 protocol is used. Else you can set 'authProtocol' to either "MD5" or "SHA". +If 'authProtocol' is NULL, MD5 protocol is used. +Else you can set 'authProtocol' to either "MD5" or "SHA". -If 'privProtocol' is NULL, DES protocol is used. Else you can set 'privProtocol' to either "AES" or "DES". +If 'privProtocol' is NULL, DES protocol is used. +Else you can set 'privProtocol' to either "AES" or "DES". -'peername' and 'handle' are used internally and do not need any initialization. +'peername' and 'handle' are used internally and do not need any +initialization. RETURN VALUE ------------ -The *nutscan_scan_snmp()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_snmp()* function returns a pointer to a +`nutscan_device_t` structure containing all found devices +or NULL if an error occurs or no device is found. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_usb.txt b/docs/man/nutscan_scan_usb.txt index dfeb0d4468..878c712a92 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - nutscan_device_t * nutscan_scan_usb(); + nutscan_device_t * nutscan_scan_usb(void); DESCRIPTION ----------- @@ -23,12 +23,15 @@ You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ -The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` +structure containing all found devices or NULL if an error occurs or no +device is found. SEE ALSO -------- + linkman:nutscan_init[3], -linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_xml_http.txt b/docs/man/nutscan_scan_xml_http.txt deleted file mode 100644 index 59e421e732..0000000000 --- a/docs/man/nutscan_scan_xml_http.txt +++ /dev/null @@ -1,36 +0,0 @@ -NUTSCAN_SCAN_XML_HTTP(3) -======================== - -NAME ----- - -nutscan_scan_xml_http - Scan network for XML/HTTP devices. - -SYNOPSIS --------- - - #include - - nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); - -DESCRIPTION ------------ - -The *nutscan_scan_xml_http()* function try to detect NUT compatible XML/HTTP devices. It does this by issuing a broadcast message on currently configured network interfaces. It waits up to 'usec_timeout' microseconds for a response from potential devices. - -You MUST call linkman:nutscan_init[3] before using this function. - -RETURN VALUE ------------- - -The *nutscan_scan_xml_http()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. - -SEE ALSO --------- -linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], -linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], -linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], -linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], -linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] diff --git a/docs/man/nutscan_scan_xml_http_range.txt b/docs/man/nutscan_scan_xml_http_range.txt new file mode 100644 index 0000000000..15cb2af5c1 --- /dev/null +++ b/docs/man/nutscan_scan_xml_http_range.txt @@ -0,0 +1,54 @@ +NUTSCAN_SCAN_XML_HTTP_RANGE(3) +============================== + +NAME +---- + +nutscan_scan_xml_http_range - Scan network for XML/HTTP devices. + +SYNOPSIS +-------- + + #include + #include /* useconds_t */ + + nutscan_device_t * nutscan_scan_xml_http_range( + const char * start_ip, + const char * end_ip, + useconds_t usec_timeout, + nutscan_xml_t * sec) + +DESCRIPTION +----------- + +The *nutscan_scan_xml_http_range()* function tries to detect NUT compatible +XML/HTTP devices. + +If 'start_ip' is NULL, the function does this by issuing a broadcast message +on currently configured network interfaces. + +Otherwise, it queries every IP ranging from 'start_ip' to 'stop_ip'. +Those IP arguments may be either IPv4 or IPv6 addresses or host names. + +It waits up to 'usec_timeout' microseconds for a response from potential +devices. + +You MUST call linkman:nutscan_init[3] before using this function. + +RETURN VALUE +------------ + +The *nutscan_scan_xml_http_range()* function returns a pointer to +a `nutscan_device_t` structure containing all found devices +or NULL if an error occurs or no device is found. + +SEE ALSO +-------- + +linkman:nutscan_init[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], +linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], +linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], +linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], +linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], +linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 015dba5f5b..01ddbf57a7 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -50,6 +50,7 @@ options and parameters that generally are not needed by normal users. OPTIONS ------- + *-h*:: Display a help message without doing anything else. This will also list possible values for '-x' in that driver, and other help text that the @@ -69,8 +70,8 @@ this option should be used for specific needs only. *-D*:: Raise the debugging level. Use this multiple times to see more details. -Running a driver in debug mode will prevent it from backgrounding after -startup. It will keep on logging information to the console until it +Running a driver in debug mode will (by default) prevent it from backgrounding +after startup. It will keep on logging information to the console until it receives a SIGINT (usually Ctrl-C) or SIGTERM signal. + The level of debugging needed depends both on the driver and the @@ -82,6 +83,7 @@ either too limited or too verbose to be of any use. *-d* 'update_count':: Dump the data tree (in upsc-like format) to stdout after running the driver update loop for 'update_count' times and exit. +By default this prevents the driver process from backgrounding after startup. Note that the driver banner will be printed too, so when using this option in scripts, don't forget to trim the first line. @@ -90,6 +92,14 @@ Raise log level threshold. Use this multiple times to log more details. + The debugging comment above also applies here. +*-F*:: +Enforce running the driver as a foreground process, regardless of debugging +or data-dumping settings. + +*-B*:: +Enforce running the driver as a background process, regardless of debugging +or data-dumping settings. + *-i* 'interval':: Set the poll interval for the device. The default value is 2 (in seconds). @@ -132,6 +142,11 @@ USB-based drivers. However, you will want to remove this option later in order to avoid permission conflicts between the driver and the unprivileged copy of linkman:upsd[8]. +*-g* 'groupname':: +Override the unprivileged group name that the driver may use after startup +to set permissions for the filesystem socket so `upsd` may still access it +if the run-time `user` of the driver normally would deny that access. + *-x* 'var'='val':: Define a variable called 'var' with the value of 'var' in the driver. This varies from driver to driver - see the specific man pages @@ -157,6 +172,7 @@ production use. FILES ----- + ups.conf:: Required configuration file. This contains all details on which drivers to start and where the hardware is attached. @@ -188,61 +204,37 @@ SEE ALSO -------- Server: -linkman:upsd[8] +~~~~~~~ + +- linkman:upsd[8] Clients: -linkman:upsc[8], linkman:upscmd[8], -linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] +~~~~~~~~ + +- linkman:upsc[8] +- linkman:upscmd[8] +- linkman:upsrw[8] +- linkman:upslog[8] +- linkman:upsmon[8] CGI programs: -linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] +~~~~~~~~~~~~~ + +- linkman:upsset.cgi[8] +- linkman:upsstats.cgi[8] +- linkman:upsimage.cgi[8] Driver control: -linkman:upsdrvctl[8] +~~~~~~~~~~~~~~~ + +include::{builddir}/linkman-drivertool-names.txt[] Drivers: -linkman:al175[8] -linkman:apcsmart[8], -linkman:bcmxcp[8], -linkman:bcmxcp_usb[8], -linkman:belkin[8], -linkman:belkinunv[8], -linkman:bestfcom[8], -linkman:bestuferrups[8], -linkman:bestups[8], -linkman:blazer_ser[8], -linkman:blazer_usb[8], -linkman:cyberpower[8], -linkman:dummy-ups[8], -linkman:etapro[8], -linkman:everups[8], -linkman:gamatronic[8], -linkman:genericups[8], -linkman:isbmex[8], -linkman:liebert[8], -linkman:masterguard[8], -linkman:metasys[8], -linkman:mge-shut[8], -linkman:mge-utalk[8], -linkman:mge-xml[8], -linkman:nitram[8], -linkman:nutdrv_qx[8], -linkman:oneac[8], -linkman:optiups[8], -linkman:powercom[8], -linkman:powerman-pdu[8], -linkman:powerpanel[8], -linkman:rhino[8], -linkman:richcomm_usb[8], -linkman:safenet[8], -linkman:snmp-ups[8], -linkman:solis[8], -linkman:tripplite[8], -linkman:tripplitesu[8], -linkman:tripplite_usb[8], -linkman:usbhid-ups[8], -linkman:upscode2[8], -linkman:victronups[8] +~~~~~~~~ + +include::{builddir}/linkman-driver-names.txt[] Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/oneac.txt b/docs/man/oneac.txt index dfa5b0852d..beb257ebc7 100644 --- a/docs/man/oneac.txt +++ b/docs/man/oneac.txt @@ -28,6 +28,7 @@ linkman:genericups[8] driver. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -39,6 +40,7 @@ Change shutdown delay time from 0 second default. INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands. (See linkman:upscmd[8]) @@ -97,6 +99,7 @@ Mutes the UPS beeper/buzzer for the current alarm condition(s). Writable Variables ------------------ + See linkman:upsrw[8] to see what variables are writable for the UPS. NOTE: If your UPS supports writing battery.runtime.low, the new set value @@ -108,18 +111,21 @@ those values are used to create an allowable output range. The UPS will do what it can to keep the output voltage value within the defined range (for example: tap change or switch to inverter). -AUTHOR ------- -Bill Elliot , -Eric Lawson +AUTHORS +------- + +* Bill Elliot +* Eric Lawson SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/optiups.txt b/docs/man/optiups.txt index 055d26ad1d..1e1d90417a 100644 --- a/docs/man/optiups.txt +++ b/docs/man/optiups.txt @@ -56,13 +56,14 @@ sent to your system logs each time NUT polls the UPS. If you specify *fake_lowbatt*:: -This forces the low battery flag true. Without it, if you want to test your -UPS, you will have to unplug it and wait until the battery drops to a low/critical -voltage level before NUT will respond and power down your system. With the flag, -NUT should power down the system soon after you pull the plug. When you are done -testing, you should remove this flag. +This forces the low battery flag true. Without it, if you want to test +your UPS, you will have to unplug it and wait until the battery drops +to a low/critical voltage level before NUT will respond and power down +your system. With the flag, NUT should power down the system soon after +you pull the plug. When you are done testing, you should remove this flag. + -For basic shutdown configuration testing, the command 'upsmon -c fsd' is preferred. +For basic shutdown configuration testing, the command 'upsmon -c fsd' is +preferred. *powerup*:: @@ -73,20 +74,26 @@ This will also power-up your equipment connected to the UPS! BUGS ---- -On the 420E, `ups.serial` and `ups.temperature` are unsupported features. This -is not a bug in NUT or the NUT driver, just the way things are with this UPS. +On the 420E, `ups.serial` and `ups.temperature` are unsupported features. +This is not a bug in NUT or the NUT driver, just the way things are with +this UPS. -AUTHOR ------- -Russell Kroll, Scott Heavner, Matthias Goebl +AUTHORS +------- + +* Russell Kroll +* Scott Heavner +* Matthias Goebl SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/phoenixcontact_modbus.txt b/docs/man/phoenixcontact_modbus.txt index 9711937d46..e173089d61 100644 --- a/docs/man/phoenixcontact_modbus.txt +++ b/docs/man/phoenixcontact_modbus.txt @@ -20,8 +20,10 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver should support the PhoenixContact QUINT-UPS industrial DC UPS, model 2320461 and all compatible models. -More information about this UPS can be found here: https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 +This driver should support the PhoenixContact QUINT-UPS industrial DC UPS, +model 2320461 and all compatible models. More information about this UPS +can be found here: +https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 phoenixcontact_modbus uses the libmodbus project, for Modbus implementation. @@ -30,28 +32,40 @@ How to configure the UPS Note: this UPS and its manual refers to Low-Batt as "Shutdown Event". -You need the "IFS-RS232-DATACABLE" to communicate with the UPS in linux as the IFS-USB cable doesn't seem to be supported. FYI communication parameters are: 115200,E,8,1. -You also need the UPS-CONF Windows software (free; download from their site), to configure the UPS signals and timers. +You need the "IFS-RS232-DATACABLE" to communicate with the UPS in Linux +as the IFS-USB cable doesn't seem to be supported. FYI communication +parameters are: 115200,E,8,1. + +You also need the UPS-CONF Windows software (free; download from their +site), to configure the UPS signals and timers. 1. Run the UPS-CONF 2. Go to Settings->Time Setting 3. Choose "state of charge shutdown delay" 4. Choose Remote starts PC-Shutdown in Mains and Battery mode 5. On the PC-Shutdown enter the maximum value (5 minutes) -6. On the PC-Restart delay enter the time you want the UPS to leave the output power off before restarting (e.g. 60 seconds), after mains power is restored. +6. On the PC-Restart delay enter the time you want the UPS to leave + the output power off before restarting (e.g. 60 seconds), after + mains power is restored. 7. On the UPS, turn the screw to the "PC-MODE" position Configuring the above way ensures that: * When power is lost, UPS constantly calculates remaining battery time - * When remaining battery time is less than 5 minutes (PC-Shutdown setting), it will raise the "Shutdown" event (seen as LOW-BATT in NUT) - * From that moment even if input power is restored, the UPS will cut the output power to its load after 5 minutes - * When the input power is restored, the UPS will restore output power after 60 seconds (PC-RESTART delay setting). + * When remaining battery time is less than 5 minutes (PC-Shutdown setting), + it will raise the "Shutdown" event (seen as LOW-BATT in NUT) + * From that moment even if input power is restored, the UPS will cut the + output power to its load after 5 minutes + * When the input power is restored, the UPS will restore output power + after 60 seconds (PC-RESTART delay setting). Meaning of settings: - * PC-Shutdown: How long before output cutoff the UPS will raise the "shutdown event" signal. Max value for this is 5 minutes. So PC should be able to shutdown within 5 minutes. - * PC-Restart: How long to delay output power after power is restored. Max is 60 seconds. + * PC-Shutdown: How long before output cutoff the UPS will raise + the "shutdown event" signal. Max value for this is 5 minutes. + So PC should be able to shutdown within 5 minutes. + * PC-Restart: How long to delay output power after power is restored. + Max is 60 seconds. EXTRA ARGUMENTS @@ -62,8 +76,8 @@ This driver doesn't support any optional settings. INSTALLATION ------------ -This driver is not built by default. You can build it by installing libmodbus -and running `configure --with-modbus=yes`. +This driver is not built by default. You can build it by installing +libmodbus and running `configure --with-modbus=yes`. You also need to give proper permissions on the local serial device file (/dev/ttyS0 for example) to allow the NUT user to access it. @@ -75,16 +89,19 @@ This driver doesn't support any instant commands. AUTHOR ------ + Spiros Ioannou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/pijuice.txt b/docs/man/pijuice.txt index 1643208f34..801646af60 100644 --- a/docs/man/pijuice.txt +++ b/docs/man/pijuice.txt @@ -3,10 +3,12 @@ PIJUICE(8) NAME ---- + pijuice - driver for UPS in PiJuice HAT NOTE ---- + This man page only documents the hardware-specific features of the *pijuice* driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -17,6 +19,7 @@ may not fully apply to PiJuice HAT, patches from experts are welcome. SUPPORTED HARDWARE ------------------ + The *pijuice* driver supports the portable PiJuice HAT UPS for Raspberry Pi embedded PCs. @@ -30,6 +33,7 @@ On the PiJuice HAT, this should be `/dev/i2c-1`. INSTALLATION ------------ + NOTE: This section was copied from `asem` driver manpage and may not fully apply to PiJuice HAT, patches are welcome. @@ -52,6 +56,7 @@ DIAGNOSTICS KNOWN ISSUES AND BUGS --------------------- + NOTE: This section was copied from `asem` driver manpage and may not fully apply to PiJuice HAT, patches are welcome. @@ -60,6 +65,7 @@ made to turn off the UPS. AUTHORS ------- + Andrew Anderson SEE ALSO @@ -67,15 +73,16 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -Initial pull requests adding this driver: -* https://github.com/networkupstools/nut/pull/730 -* https://github.com/PiSupply/PiJuice/issues/124 +* Initial pull requests adding this driver: +** https://github.com/networkupstools/nut/pull/730 +** https://github.com/PiSupply/PiJuice/issues/124 -Product home page: https://uk.pi-supply.com/products/pijuice-standard +* Product home page: https://uk.pi-supply.com/products/pijuice-standard -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/powercom.txt b/docs/man/powercom.txt index ce4dbd73d0..db7e936f7e 100644 --- a/docs/man/powercom.txt +++ b/docs/man/powercom.txt @@ -8,12 +8,14 @@ powercom - UPS driver for serial Powercom/Trust/Advice UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the powercom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports many similar kinds of serial UPS hardware (as well as a few USB UPS models with USB-to-serial adapters). The most common ones are the Trust 425/625, Powercom, and Advice Partner/King PR750. Others using the same @@ -24,6 +26,7 @@ see the NUT Hardware Compatibility List (HCL). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -116,6 +119,7 @@ system startup -- just when the power consumption tends to be high. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + linevoltage = 230 manufacturer = PowerCom modelname = Unknown @@ -128,6 +132,7 @@ values for ALL models. Trust ~~~~~ + numOfBytesFromUPS = 11 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0},{7,0},{8,0}} @@ -139,6 +144,7 @@ Trust KP625AP ~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0x80},{7,0},{8,0}} @@ -150,6 +156,7 @@ KP625AP Egys ~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0x80},{7,0},{8,0}} @@ -161,6 +168,7 @@ Egys IMP ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} @@ -168,6 +176,7 @@ IMP KIN ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x4b},{8,0},{8,0}} @@ -175,6 +184,7 @@ KIN BNT ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x42},{8,0},{8,0}} @@ -182,6 +192,7 @@ BNT BNT-other ~~~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{8,0},{8,0},{8,0}} @@ -193,25 +204,29 @@ BNT-other OPTI ~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} -AUTHOR ------- -Peter Bieringer , -Alexey Sidorov , -Keven L. Ates , -Rouben Tchakhmakhtchian +AUTHORS +------- + +* Peter Bieringer +* Alexey Sidorov +* Keven L. Ates +* Rouben Tchakhmakhtchian SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/powerman-pdu.txt b/docs/man/powerman-pdu.txt index 0feec852e7..0332ea64d9 100644 --- a/docs/man/powerman-pdu.txt +++ b/docs/man/powerman-pdu.txt @@ -38,6 +38,7 @@ This driver is not built by default. You can build it by using UPS COMMANDS ------------ + The following instant commands (see linkman:upscmd[8]) are available for each outlet of the PDU, with *X* standing for the outlet number: @@ -55,6 +56,7 @@ Cycle the outlet (power off then power on, possibly with a delay). IMPLEMENTATION -------------- + The hostname of the Powerman server is specified using the "port" value in *ups.conf*, i.e.: @@ -66,22 +68,26 @@ The port used to reach 'powermand' is optional if the default port is used. KNOWN ISSUES ------------ + In the current NUT version (2.4.1), ups.status is still exposed, with the value "WAIT". Some other values from the ups collection are also exposed. AUTHOR ------ + Arnaud Quette SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The PowerMan home page: http://powerman.sourceforge.net/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The PowerMan home page: https://github.com/chaos/powerman diff --git a/docs/man/powerpanel.txt b/docs/man/powerpanel.txt index 1cfe18cdb9..4e8450d0bc 100644 --- a/docs/man/powerpanel.txt +++ b/docs/man/powerpanel.txt @@ -8,12 +8,14 @@ powerpanel - Driver for serial PowerPanel Plus compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the powerpanel driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports CyberPower BC1200, PR2200 and many other similar devices, both for the text and binary protocols. The driver will autodetect which protocol is used. @@ -24,6 +26,7 @@ network cards via SNMP. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in linkman:ups.conf[5]: *protocol=*['text,binary']:: @@ -51,6 +54,7 @@ seconds will be truncated to 6 seconds intervals, values above 60 seconds to VARIABLES --------- + Depending on the type of your UPS unit, some of the following variables may be changed with linkman:upsrw[8]. If the driver can't read a variable from the UPS, it will not be made available. @@ -72,6 +76,7 @@ writable: allow cold start from battery COMMANDS -------- + Depending on the type of your UPS unit, some of the following commands may be available. @@ -88,6 +93,7 @@ must verify that these work as expected (see <<_shutdown_issues,Shutdown Issues> SUPPORT STATUS -------------- + Vendor support is absent for this driver, so if you need some features that are currently not available, provide ample documentation on what the driver should sent to the UPS in order to make this work. If more information @@ -98,6 +104,7 @@ isn't willing to share this with us. SHUTDOWN ISSUES --------------- + If the *shutdown.return* command on your UPS doesn't seem to work, chances are that your UPS is an older model. Try a couple of different settings for 'offdelay'. If no value in the range 6..600 works, your @@ -114,6 +121,7 @@ supported through the text protocol are affected by this. KNOWN PROBLEMS -------------- + The CyberPower OP series don't offer direct voltage, charge, frequency and temperature readings. Instead, they will return a binary value that needs conversion to the actual value. @@ -128,6 +136,7 @@ instrument. AUTHORS ------- + Arjen de Korte , Doug Reynolds SEE ALSO @@ -135,13 +144,16 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers: ~~~~~~~~~~~~~~ + linkman:usbhid-ups[8], linkman:snmp-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/rhino.txt b/docs/man/rhino.txt index 443dec85e7..bdb30f892a 100644 --- a/docs/man/rhino.txt +++ b/docs/man/rhino.txt @@ -3,17 +3,20 @@ RHINO(8) NAME ---- + rhino - Driver for Brazilian Microsol RHINO UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the rhino driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver has been tested with : + +This driver has been tested with: * Rhino 6000 VA * Rhino 7500 VA @@ -45,6 +48,7 @@ COMMANDS AUTHOR ------ + Silvino B. Magalhães SEE ALSO @@ -52,8 +56,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/richcomm_usb.txt b/docs/man/richcomm_usb.txt index 69578e74d5..fe53b14920 100644 --- a/docs/man/richcomm_usb.txt +++ b/docs/man/richcomm_usb.txt @@ -3,16 +3,19 @@ RICHCOMM_USB(8) NAME ---- + richcomm_usb - Driver UPS equipment using Richcomm dry-contact to USB solution NOTE ---- + This man page only documents the specific features of the richcomm_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The Richcomm dry-contact to USB solution is a generic interface that is used to upgrade an existing (RS-232) contact closure UPS interface to USB. As such, all the limitations of the underlying contact closure interface @@ -21,6 +24,7 @@ OB, and LB. See also linkman:genericups[8]. BUGS ---- + Most contact-closure UPSes will not power down the load if the line power is present. This can create a race when using secondary linkman:upsmon[8] systems. See the linkman:upsmon[8] man page for more information. @@ -31,20 +35,23 @@ UPS of some kind that allows detection and proper load cycling on command. AUTHORS ------- -Peter van Valderen , -Dirk Teurlings +* Peter van Valderen +* Dirk Teurlings SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:genericups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/riello_ser.txt b/docs/man/riello_ser.txt index 45a399b902..cbc326b0a6 100644 --- a/docs/man/riello_ser.txt +++ b/docs/man/riello_ser.txt @@ -1,5 +1,5 @@ RIELLO_SER(8) -=========== +============= NAME ---- @@ -36,8 +36,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/riello_usb.txt b/docs/man/riello_usb.txt index d4afbfedb7..1c4ffd6e43 100644 --- a/docs/man/riello_usb.txt +++ b/docs/man/riello_usb.txt @@ -1,5 +1,5 @@ RIELLO_USB(8) -=========== +============= NAME ---- @@ -35,8 +35,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/safenet.txt b/docs/man/safenet.txt index f6f5a5a34c..00aa02f929 100644 --- a/docs/man/safenet.txt +++ b/docs/man/safenet.txt @@ -3,21 +3,25 @@ SAFENET(8) NAME ---- + safenet - Driver for SafeNet compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the safenet driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports UPS equipment which can be controlled via SafeNet v1.0 for Windows (serial interface only). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -38,8 +42,9 @@ Time to wait before switching on the UPS (minutes). Defaults to 1 minute. *offdelay=*'value':: Time to wait before shutting down the UPS (seconds). Defaults to 30 seconds. -UPSCMD ------- +INSTANT COMMANDS +---------------- + This driver supports some instant commands (see linkman:upscmd[8]): *test.battery.start*:: @@ -73,6 +78,7 @@ and *ondelay*. KNOWN PROBLEMS -------------- + If you run the *shutdown.return* command with mains present, the output may stay on or switch off and not back on again. The *shutdown.reboot* command will unconditionally switch on the load again (with or without mains @@ -87,6 +93,7 @@ return when the power comes back. AUTHOR ------ + Arjen de Korte SEE ALSO @@ -94,8 +101,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/skel.txt b/docs/man/skel.txt index 7cae6669c9..8e310943e0 100644 --- a/docs/man/skel.txt +++ b/docs/man/skel.txt @@ -3,10 +3,12 @@ SKEL(8) NAME ---- + skel - skeleton driver man page NOTE ---- + This man page only documents the hardware-specific features of the *skel* driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -31,6 +33,7 @@ apropos(8) database is properly rebuilt. SUPPORTED HARDWARE ------------------ + *skel* supports ... ////////////////////////////////////////// @@ -44,6 +47,7 @@ CABLING EXTRA ARGUMENTS --------------- + This driver also supports the following optional settings: *option1*='num':: @@ -86,6 +90,7 @@ encountered when implementing the protocol for your UPS. KNOWN ISSUES AND BUGS --------------------- + *Got "EPERM: Operation not permitted" upon driver startup*:: You have forgotten to install the udev files, as explained @@ -100,6 +105,7 @@ some form of contact information so that users can report bugs. AUTHORS ------- + J Random User ////////////////////////////////////////// @@ -114,8 +120,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 23781be617..f45462b3e3 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -8,6 +8,7 @@ snmp-ups - Multi-MIB Driver for SNMP UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the snmp-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -15,16 +16,20 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -The snmp-ups driver automatically detects and supports a wide range of devices by loading various MIBS, such as: +The snmp-ups driver automatically detects and supports a wide range of +devices by loading various MIBS, such as: *ietf*:: -UPS that is RFC 1628 (UPS MIB) compliant, e.g. MGE UPS SYSTEMS, Liebert, perhaps others (default) +UPS that is RFC 1628 (UPS MIB) compliant, e.g. MGE UPS SYSTEMS, Liebert, +perhaps others (default) *mge*:: -MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards (ref 66062, 66045, 66074 and 66244) +MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards +(ref 66062, 66045, 66074 and 66244) *apcc*:: -APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, as well as any others supporting the APC POWERNET MIB +APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, +as well as any others supporting the APC POWERNET MIB *netvision*:: Socomec Sicon UPS with Netvision Web/SNMP management card/external box @@ -54,7 +59,8 @@ Various BayTech PDUs HP/Compaq AF401A management card, perhaps others *cyberpower*:: -Cyberpower RMCARD201. Should also support RMCARD100 (net version), RMCARD202 and RMCARD301 +Cyberpower RMCARD201. Should also support RMCARD100 (net version), +RMCARD202 and RMCARD301 *huawei*:: Huawei UPS5000-E, perhaps others @@ -85,15 +91,20 @@ $ snmp-ups -a snmp-test -x mibs=--list ---- *mibs*='name':: -Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED HARDWARE above). +Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED +HARDWARE above). ++ With "auto", the driver will try a select set of SNMP objects until it finds -one that the device responds to. Note that since NUT 2.6.2, snmp-ups has a new -method that uses sysObjectID (which is a pointer to the preferred MIB of the -device) to detect supported devices. This renders void the use of "mibs" option. +one that the device responds to. ++ +Note that since NUT 2.6.2, snmp-ups has a new method that uses sysObjectID +(which is a pointer to the preferred MIB of the device) to detect supported +devices. This renders void the *requirement* to use the "mibs" option. *community*='name':: Set community name (default = public). -Note that a RW community name is required to change UPS settings (as for a powerdown). +Note that a RW community name is required to change UPS settings and +send commands (as for a powerdown). *snmp_version*='version':: Set SNMP version (default = v1, allowed: v2c, v3) @@ -112,8 +123,8 @@ Convert from three phase line-to-line voltage to line-to-neutral voltage *pollfreq*='num':: Set polling interval for full updates, in seconds, to reduce SNMP network traffic relative to the quick updates performed every "pollinterval" (the -latter option is described in linkman:ups.conf[5]). The default value is 30 (in -seconds). +latter option is described in linkman:ups.conf[5]). +The default value is 30 (in seconds). *notransferoids*:: Disable the monitoring of the low and high voltage transfer OIDs in @@ -149,6 +160,7 @@ run-time supported list. REQUIREMENTS ------------ + You will need to install the Net-SNMP package from http://www.net-snmp.org/ before building this driver. @@ -156,6 +168,7 @@ SNMP v3 also requires OpenSSL support from http://www.openssl.org. LIMITATIONS ----------- + Shutdown ~~~~~~~~ @@ -168,15 +181,18 @@ supported command. INSTALLATION ------------ + This driver is only built if the Net-SNMP development files are present at configuration time. You can also force it to be built by using +configure --with-snmp=yes+ before calling make. EXAMPLES -------- + The hostname of the UPS is specified with the "port" value in `ups.conf`, and may include a non-standard (161) remote peer port: +------ [snmpv1] driver = snmp-ups port = snmp-ups.example.com @@ -194,23 +210,29 @@ The hostname of the UPS is specified with the "port" value in authPassword = myauthenticationpassphrase privPassword = myprivatepassphrase desc = "Example SNMP v3 device, with the highest security level" +------ AUTHORS ------- -Arnaud Quette, Dmitry Frolov +* Arnaud Quette +* Dmitry Frolov +* Jim Klimov SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] NUT SNMP Protocols Library ~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available at: http://www.networkupstools.org/ups-protocols.html#_snmp Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt new file mode 100644 index 0000000000..bc192d634e --- /dev/null +++ b/docs/man/socomec_jbus.txt @@ -0,0 +1,166 @@ +SOCOMEC_JBUS(8) +=============== + +NAME +---- + +socomec_jbus - Driver for Socomec JBUS UPS with +RS-232 serial Modbus connection. + +SYNOPSIS +-------- + +*socomec_jbus* -h + +*socomec_jbus* -a 'DEVICE_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the +socomec_jbus driver. For information about the core driver, see +linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +This driver supports Socomec JBUS series, online (double conversion) +UPS with the following characteristics. + +1. Single phase and 3-phase UPS + +2. Connection: RS-232 + +These are typically provided with a Netvision WEB/SNMP management +card / external box that would be better served by the linkman:snmp-ups[8] +driver. In case netvision isn't available, you can hook up the UPS directly +via the serial port and use this driver. + +Currently, it has only been tested on the following model. + +* DIGYS 3/3 15kVA + +In theory, any Socomec JBUS model should work. It should be discovered +as ``Unknown Socomec JBUS'' with a numeric id that I'll need to add it +to the list of supported UPSs. + +Sadly, Socomec document only mentions DELPHYS MX and DELPHYS MX elite and +I have the id of my own DIGYS, so chances are, your model will not be +recognized but will probably mostly work. Please report successful +or unsuccessful results to the bug tracker or the mailing list. +Make sure to include the full model number of your UPS manually +in your report. + +socomec_jbus uses the libmodbus project, for Modbus implementation. + +CABLING +------- + +The UPS has an RS-232 port which should be connected with a NULL-modem +cable to a PC serial port. The UPS tested has a female DB9 connector, +so if you construct the cable yourself, make note of the connector type to +avoid using gender changers. + +RS-232 is supported on all operating systems, either via a built-in serial +port on your computer, or by using an external USB-to-RS-232 converter. If +you plan to use an USB-to-RS-232 converter, make sure it's supported by your +operating system. + + +INSTALLATION +------------ + +This driver should be built by default if libmodbus and development headers +are available. You can force the `configure` script to build it with the +following arguments: + + configure --with-serial=yes --with-modbus=yes + +You also need to give proper (R/W) permissions on the local serial device +file to allow the NUT driver run-time user to access it. This may need +additional setup for start-up scripting, udev or upower rules, to apply +the rights on every boot -- especially if your device nodes are tracked +by a virtual filesystem. + +For example, a USB-to-serial converter can be identified as `/dev/ttyACM0` +or `/dev/ttyUSB0` on Linux, or `/dev/ttyU0` on FreeBSD (note the capital "U"). +A built-in serial port can be identified as `/dev/ttyS0` on Linux or one of +`/dev/cua*` names on FreeBSD. + +INSTANT COMMANDS +---------------- + +This driver does not (yet?) support sending commands to the UPS. + +VARIABLES +--------- + +This driver does not support writable runtime variables (see linkman:upsrw[8]): +for the same reasons. +Both should be trivial to implement, but since I've already found one or two +inconsistencies in the documentation, I'm withholding from trying them. + +KNOWN ISSUES AND BUGS +--------------------- + +Well, it is an alpha release at best, but so far appears to report the UPS +status reliably. Mostly based on the work of Yifeng Li on +the huawei-ups2000 in that very same source tree. + +Read failure on some JBUS addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The driver polls all documented JBUS addresses and it is quite possible +that your UPS model does not support one of them (e.g. the Digys does not +support address 0x1020 which should provide the current UPS status). +This should be logged with LOG_ERR from modbus_read_input_registers() +along with the address that produced the error. + +Data stale +~~~~~~~~~~~ + +Under certain circumstances, some registers can return invalid values +and trigger a "data stale" error. Once a data stale error has occurred, +you should see error messages similar to the example below in the system +log. + + socomec_jbus: modbus_read_input_registers(addr:XXXX, count:Z): + Illegal data address + upsd: Data for UPS [socomec] is stale - check driver + upsd: UPS [socomec] data is no longer stale + +So far all known problems have been fixed by the author, but an unknown one +cannot be ruled out. If you have encountered "data stale" problems during +normal uses, please file a bug report with full logs attached. + +Before troubleshooting or reporting a problem, it's important to check +your *dmesg* log for USB connect and disconnect events to avoid wasting +time on the NUT driver when the actual problem is USB. For example, if +someone yanks the cable out of the USB port, or if a new USB device is +plugged into a USB host/hub that is struggling to power its ports +(common on single-board computers like Raspberry Pi), or if you have +flaky cabling or EMI noise, the serial converter can get disconnected +from USB, at least briefly. This creates a permanent data stale, the driver +must be restarted (plugging the USB back won't fix it, since the driver +is still using the nonexistent serial device). These USB problems usually +have nothing to do with NUT. If it's the case, you should solve the +underlying USB problem - check the cable, check the converter, try a +powered USB hub, try a full-speed USB isolator, etc. + +AUTHOR +------ + +Thanos Chatziathanassiou + +SEE ALSO +-------- + +The core driver: +~~~~~~~~~~~~~~~~ + +linkman:nutupsdrv[8] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* Socomec JBUS/Modbus Reference Guide: + https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/solis.txt b/docs/man/solis.txt index eb732439de..532112b25a 100644 --- a/docs/man/solis.txt +++ b/docs/man/solis.txt @@ -15,7 +15,8 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver has been tested with : + +This driver has been tested with: * Solis 1000 VA * Solis 1500 VA @@ -55,6 +56,7 @@ connect to the UPS, but some values are read incorrectly. AUTHOR ------ + Silvino B. Magalhães SEE ALSO @@ -62,8 +64,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/tripplite.txt b/docs/man/tripplite.txt index 9305551eae..9ec37f295d 100644 --- a/docs/man/tripplite.txt +++ b/docs/man/tripplite.txt @@ -3,22 +3,26 @@ TRIPPLITE(8) NAME ---- + tripplite - Driver for Tripp-Lite SmartPro UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the tripplite driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should work on the SmartPro line, including the SMART700 and SMART700SER. It only supports SmartPro models that communicate using the serial port. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -36,25 +40,31 @@ reboot command. The default value is 60 (in seconds). KNOWN ISSUES AND BUGS --------------------- + Battery charge information may not be correct for all UPSes. It is tuned to be correct for a SMART700SER. Other models may not provide correct information. Information from the manufacturer would be helpful. AUTHORS ------- -Rickard E. (Rik) Faith, Nicholas Kain + +* Rickard E. (Rik) Faith +* Nicholas Kain SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplitesu[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/tripplite_usb.txt b/docs/man/tripplite_usb.txt index d71537f676..cad5a80f22 100644 --- a/docs/man/tripplite_usb.txt +++ b/docs/man/tripplite_usb.txt @@ -1,29 +1,34 @@ TRIPPLITE_USB(8) ================ - NAME ---- + tripplite_usb - Driver for older Tripp Lite USB UPSes (not PDC HID) SYNOPSIS -------- + *tripplite_usb* -h *tripplite_usb* -a 'UPS_NAME' ['OPTIONS'] 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 linkman:usbhid-ups[8] 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.firmware.aux`. Also, be sure to turn on debugging ('-DDD') -for more informative log messages. If your Tripp Lite UPS uses a serial port, -you may wish to investigate the linkman:tripplite[8] or linkman:tripplitesu[8] -driver. + +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 linkman:usbhid-ups[8] 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.firmware.aux`. Also, be sure to turn on debugging ('-DDD') +for more informative log messages. + +If your Tripp Lite UPS uses a serial port, you may wish to investigate +the linkman:tripplite[8] or linkman:tripplitesu[8] drivers. This driver has been tested with the following models: @@ -152,11 +157,13 @@ 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. The UPS would need to be claimed by the driver in order to read this ID. -AUTHOR ------- -Written by Charles Lepple, based on the linkman:tripplite[8] driver by Rickard E. (Rik) -Faith and Nicholas Kain. Please do not email the authors directly - use the -nut-upsdev mailing list. +AUTHORS +------- + +Written by Charles Lepple, based on the linkman:tripplite[8] driver +by Rickard E. (Rik) Faith and Nicholas Kain. + +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 Bradley Feldman (http://www.bradleyloritheo.com) @@ -166,19 +173,20 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplite[8], linkman:tripplitesu[8], linkman:usbhid-ups[8] Other tools: ~~~~~~~~~~~~ -regex(7), lsusb(8) +regex(7), lsusb(8) -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - diff --git a/docs/man/tripplitesu.txt b/docs/man/tripplitesu.txt index 6416699a1c..9194a1229d 100644 --- a/docs/man/tripplitesu.txt +++ b/docs/man/tripplitesu.txt @@ -3,20 +3,24 @@ TRIPPLITESU(8) NAME ---- + tripplitesu - Driver for Tripp-Lite SmartOnline (SU) UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the tripplitesu driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports the Tripp Lite SmartOnline family (via the serial port). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -27,6 +31,7 @@ until there are only a few seconds left. Common values are around 25--30. AUTHOR ------ + Allan N. Hessenflow SEE ALSO @@ -34,12 +39,15 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplite[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 41a194c306..10c060754c 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -3,6 +3,7 @@ UPS.CONF(5) NAME ---- + ups.conf - UPS definitions for Network UPS Tools DESCRIPTION @@ -93,8 +94,9 @@ provided in the respective driver man pages. *synchronous*:: -Optional. The driver work by default in asynchronous mode (i.e -*synchronous=no*). This means that all data are pushed by the driver +Optional. The drivers work by default in asynchronous mode initially +but can fall back to synchronous mode if writes to server socket failed +(i.e *synchronous=auto*). This means that all data are pushed by the driver on the communication socket to upsd (Unix socket on Unix, Named pipe on Windows) without waiting for these data to be actually consumed. With some HW, such as ePDUs, that can produce a lot of data, @@ -108,16 +110,38 @@ By enabling the 'synchronous' flag (value = 'yes'), the driver will wait for data to be consumed by upsd, prior to publishing more. This can be enabled either globally or per driver. + -The default is 'no' (i.e. asynchronous mode) for backward compatibility -of the driver behavior. +The default of 'auto' acts like 'no' (i.e. asynchronous mode) for backward +compatibility of the driver behavior, until communications fail with a +"Resource temporarily unavailable" condition, which happens when the +driver has many data points to send in a burst, and the server can not +handle that quickly enough so the buffer fills up. *user*:: -Optional. Overrides the compiled-in default unprivileged username. See the -discussion of the `-u` option in linkman:nutupsdrv[8] for details. +Optional. Overrides the compiled-in default unprivileged username for +all NUT device drivers. See the discussion of the `-u` option in +linkman:nutupsdrv[8] for details. + +*group*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged group name for all NUT device drivers, used for the +socket file access. See the discussion of the `-g` option in +linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + +*debug_min* 'INTEGER':: + +Optional. Specify a minimum debug level for all driver daemons, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. UPS FIELDS ---------- + *driver*:: Required. This specifies which program will be monitoring this UPS. You @@ -131,6 +155,25 @@ Required. This is the serial port where the UPS is connected. On a Linux system, the first serial port usually is '/dev/ttyS0'. On FreeBSD and similar systems, it probably will be '/dev/cuaa0'. +*user*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged username for a particular NUT device driver. See the +discussion of the `-u` option in linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + +*group*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged group name for a particular NUT device driver, used for +the socket file access. See the discussion of the `-g` option in +linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + *sdorder*:: Optional. When you have multiple UPSes on your system, you usually need to @@ -225,6 +268,14 @@ the outside world, internally in the UPS the original value is used. All other fields are passed through to the hardware-specific part of the driver. See those manuals for the list of what is allowed. +*debug_min* 'INTEGER':: + +Optional. Specify a minimum debug level for this driver daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. If the global `debug_min` is also set, this +driver-level setting overrides it. Command-line option `-D` can only +increase this verbosity level. + INTEGRATION ----------- @@ -240,8 +291,11 @@ from linkman:upsc[8] or similar as "snoopy@doghouse". SEE ALSO -------- -linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsdrvctl[8] + +linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsdrvctl[8], +linkman:upsdrvsvcctl[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsc.txt b/docs/man/upsc.txt index 11628867a9..2e89c5071a 100644 --- a/docs/man/upsc.txt +++ b/docs/man/upsc.txt @@ -1,13 +1,14 @@ UPSC(8) ======= - NAME ---- + upsc - example lightweight UPS client SYNOPSIS -------- + *upsc* -l | -L ['host'] *upsc* 'ups' ['variable'] @@ -23,6 +24,7 @@ want to include the full interface. OPTIONS ------- + *-l* 'host':: List all UPS names configured at 'host', one name per line. The hostname @@ -113,7 +115,7 @@ SEE ALSO linkman:upsd[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upscli_add_host_cert.txt b/docs/man/upscli_add_host_cert.txt index 87684df732..6d9050d3c1 100644 --- a/docs/man/upscli_add_host_cert.txt +++ b/docs/man/upscli_add_host_cert.txt @@ -16,6 +16,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_add_host_cert()* function register a security rule associated to the 'hostname'. All connections to this host use this rule. @@ -23,9 +24,9 @@ The rule is composed of the certificate name 'certname 'expected for the host, 'certverify' if the certificate must be validated for the host and 'forcessl' if a secured connection must be used to connect to the host. -Note: This call only functions if upsclient has been compiled with NSS support. -If instead it was compiled with OpenSSL support, this function contains an empty -definition and will take no action when called. +Note: This call only functions if upsclient has been compiled with NSS +support. If instead it was compiled with OpenSSL support, this function +contains an empty definition and will take no action when called. RETURN VALUE ------------ @@ -34,5 +35,6 @@ RETURN VALUE SEE ALSO -------- + linkman:upscli_init[3], linkman:upscli_connect[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_cleanup.txt b/docs/man/upscli_cleanup.txt index 611c3de051..60431ec30c 100644 --- a/docs/man/upscli_cleanup.txt +++ b/docs/man/upscli_cleanup.txt @@ -15,15 +15,18 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_cleanup()* function flushes SSL caches and frees memory used internally in upsclient module. RETURN VALUE ------------ -The *upscli_cleanup()* function returns 1 on success, or -1 if an error occurs. +The *upscli_cleanup()* function returns 1 on success, or -1 if an error +occurs. SEE ALSO -------- + linkman:upscli_init[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_connect.txt b/docs/man/upscli_connect.txt index 6662ae00fa..c2e3ddef27 100644 --- a/docs/man/upscli_connect.txt +++ b/docs/man/upscli_connect.txt @@ -11,10 +11,11 @@ SYNOPSIS #include - int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); + int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags); DESCRIPTION ----------- + The *upscli_connect()* function takes the pointer 'ups' to a `UPSCONN_t` state structure and opens a TCP connection to the 'host' on the given 'port'. @@ -45,6 +46,7 @@ returns 0 on success, or -1 if an error occurs. SEE ALSO -------- + linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], diff --git a/docs/man/upscli_disconnect.txt b/docs/man/upscli_disconnect.txt index 9ce36b458c..76a8e61100 100644 --- a/docs/man/upscli_disconnect.txt +++ b/docs/man/upscli_disconnect.txt @@ -15,10 +15,12 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_disconnect()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure, shuts down the connection to the server, and -frees dynamic memory used by the state structure. The `UPSCONN_t` structure -is no longer valid after this function is called. +`UPSCONN_t` state structure, shuts down the connection to the server, +and frees dynamic memory used by the state structure. + +The `UPSCONN_t` structure is no longer valid after this function is called. This function must be called, or your program will leak memory and file descriptors. @@ -31,5 +33,6 @@ error occurs. SEE ALSO -------- + linkman:upscli_connect[3], linkman:upscli_fd[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_fd.txt b/docs/man/upscli_fd.txt index c42b098d1a..1fd2971c08 100644 --- a/docs/man/upscli_fd.txt +++ b/docs/man/upscli_fd.txt @@ -16,9 +16,9 @@ SYNOPSIS DESCRIPTION ----------- -The *upscli_fd()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure and returns the value of the file descriptor -for that connection, if any. +The *upscli_fd()* function takes the pointer 'ups' to a `UPSCONN_t` +state structure and returns the value of the file descriptor for +that connection, if any. This may be useful for determining if the connection to linkman:upsd[8] has been lost. @@ -27,7 +27,9 @@ RETURN VALUE ------------ The *upscli_fd()* function returns the file descriptor, which -may be any non-negative number. It returns -1 if an error occurs. +may be any non-negative number. + +It returns -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_get.txt b/docs/man/upscli_get.txt index ff453fccf4..0d524fe9ba 100644 --- a/docs/man/upscli_get.txt +++ b/docs/man/upscli_get.txt @@ -3,7 +3,8 @@ UPSCLI_GET(3) NAME ---- -upscli_get - retrieve data from a UPS + +upscli_get - retrieve data from an UPS SYNOPSIS -------- @@ -15,20 +16,23 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_get()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, and the pointer 'query' to an array of 'numq' query elements. It builds a properly-formatted request from those elements and transmits it to linkman:upsd[8]. -Upon success, the response will be split into separate components. A -pointer to those components will be returned in 'answer'. The -number of usable answer components will be returned in 'numa'. +Upon success, the response will be split into separate components. +A pointer to those components will be returned in 'answer'. +The number of usable answer components will be returned in 'numa'. USES ---- -This function implements the "GET" command in the protocol. As a -result, you can use it to request many different things from the server. +This function implements the "GET" command in the protocol. +As a result, you can use it to request many different things +from the server. + Some examples are: * GET NUMLOGINS @@ -40,38 +44,46 @@ Some examples are: QUERY FORMATTING ---------------- + To generate a request for `GET NUMLOGINS su700`, you would populate query and numq as follows: +------ size_t numq; const char *query[2]; query[0] = "NUMLOGINS"; query[1] = "su700"; numq = 2; +------ All escaping of special characters and quoting of elements with spaces is handled for you inside this function. ANSWER FORMATTING ----------------- -The raw response from upsd to the above query would be `NUMLOGINS su700 1`. + +The raw response from `upsd` to the above query would be `NUMLOGINS su700 1`. + Since this is split up for you, the values work out like this: +------ size_t numa; numa = 3; answer[0] = "NUMLOGINS" answer[1] = "su700" answer[2] = "1" +------ -Notice that the value which you seek typically starts at answer[numq]. +Notice that the value which you seek typically starts at `answer[numq]`. ERROR CHECKING -------------- + This function will check your query against the response from -linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", it -will expect to see those at the beginning of the response. +linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", +it will expect to see those at the beginning of the response. If the results from *upsd* do not pass this case-insensitive test against your request, this function will return an error. When this @@ -79,6 +91,7 @@ happens, linkman:upscli_upserror[3] will return 'UPSCLI_ERR_PROTOCOL'. ANSWER ARRAY LIFETIME --------------------- + The pointers contained within the 'answer' array are only valid until the next call to a 'upsclient' function which references them. If you need to use data from multiple calls, you must copy it somewhere @@ -90,18 +103,21 @@ which were returned by the most recent call. You also must not attempt to use more than 'numa' elements in 'answer'. Such behavior is undefined, and may yield bogus data or a crash. -The array will be deleted after calling linkman:upscli_disconnect[3]. Any -access after that point is also undefined. +The array will be deleted after calling linkman:upscli_disconnect[3]. +Any access after that point is also undefined. RETURN VALUE ------------ + The *upscli_get()* function returns 0 on success, or -1 if an error occurs. -If *upsd* disconnects, you may need to handle or ignore `SIGPIPE` in order to -prevent your program from terminating the next time that the library writes to -the disconnected socket. The following code in your initialization function -will allow the *upscli_get()* call to return an error in that case: +If *upsd* disconnects, you may need to handle or ignore `SIGPIPE` +in order to prevent your program from terminating the next time that +the library writes to the disconnected socket. + +The following code in your initialization function will allow the +*upscli_get()* call to return an error in that case: #include ... @@ -110,5 +126,6 @@ will allow the *upscli_get()* call to return an error in that case: SEE ALSO -------- + linkman:upscli_list_start[3], linkman:upscli_list_next[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_init.txt b/docs/man/upscli_init.txt index 07c8b5f13f..16cd0e9690 100644 --- a/docs/man/upscli_init.txt +++ b/docs/man/upscli_init.txt @@ -16,6 +16,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_init()* function initialize upsclient module and set many SSL-related properties: 'certverify' to 1 makes certificate verification required for all SSL connections and 'certpath' is the location of @@ -28,14 +29,14 @@ the same hash value (thus file name), the ".0" can be incremented to ".1" and so on, as needed. The bash command for creating links in this manner would be: -ln -s ca.pem ./$(openssl x509 -hash -noout -in ca.pem).0 + ln -s ca.pem ./$(openssl x509 -hash -noout -in ca.pem).0 Alternatively, the c_rehash utility (provided by openssl-perl) can take a directory and iterate it to link all certificates found in that directory, in the manner described above. -If compiled with NSS, certpath refers to a directory -containing database files. +If compiled with NSS, certpath refers to a directory containing database +files. If compiled with NSS and using SSL, you can specify 'certname' the name of the certificate to send to upsd and 'certpasswd' the password used @@ -53,6 +54,7 @@ The *upscli_init()* function returns 1 on success, or -1 if an error occurs. SEE ALSO -------- + linkman:upscli_add_host_cert[3], linkman:upscli_cleanup[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], diff --git a/docs/man/upscli_list_next.txt b/docs/man/upscli_list_next.txt index b44d5efdf4..9612136cde 100644 --- a/docs/man/upscli_list_next.txt +++ b/docs/man/upscli_list_next.txt @@ -42,8 +42,8 @@ ANSWER FORMATTING ----------------- The contents of 'numa' and 'answer' work just like a call to -linkman:upscli_get[3]. The values returned by linkman:upsd[8] are identical to -a single item request, so this is not surprising. +linkman:upscli_get[3]. The values returned by linkman:upsd[8] are +identical to a single item request, so this is not surprising. ERROR CHECKING -------------- @@ -66,5 +66,6 @@ its first call in that case. SEE ALSO -------- + linkman:upscli_list_start[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_list_start.txt b/docs/man/upscli_list_start.txt index b5d7dd9f5f..c564c36e0e 100644 --- a/docs/man/upscli_list_start.txt +++ b/docs/man/upscli_list_start.txt @@ -47,12 +47,14 @@ To see the list of variables on a UPS called 'su700', the protocol command would be `LIST VAR su700`. To start that list with this function, you would populate query and numq as follows: +------ size_t numq; const char *query[2]; query[0] = "VAR"; query[1] = "su700"; numq = 2; +------ All escaping of special characters and quoting of elements with spaces are handled for you inside this function. @@ -69,6 +71,7 @@ When this happens, linkman:upscli_upserror[3] will return RETURN VALUE ------------ + The *upscli_list_start()* function returns 0 on success, or -1 if an error occurs. @@ -79,4 +82,3 @@ linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] - diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index 6c4db6ccf2..120ad21b78 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -10,17 +10,20 @@ SYNOPSIS -------- #include + #include /* or on some platforms */ int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, - const long timeout); + const time_t timeout); DESCRIPTION ----------- -The *upscli_readline()* and *upscli_readline_timeout()* functions take the -pointer 'ups' to a `UPSCONN_t` state structure, receive a single line from the -server, and copy up to 'buflen' bytes of the response into the buffer 'buf'. + +The *upscli_readline()* and *upscli_readline_timeout()* functions take +the pointer 'ups' to a `UPSCONN_t` state structure, receive a single +line from the server, and copy up to 'buflen' bytes of the response +into the buffer 'buf'. Some parsing of the string occurs during reception. In particular, ERR messages from linkman:upsd[8] are detected and will cause this @@ -34,8 +37,8 @@ freedom, and uses NUT default network timeout (5 seconds). RETURN VALUE ------------ -The *upscli_readline()* and *upscli_readline_timeout()* functions return 0 on -success, or -1 if an error occurs. +The *upscli_readline()* and *upscli_readline_timeout()* functions +return 0 on success, or -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index 5e4e7edb6d..895f0baed0 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -9,20 +9,20 @@ upscli_sendline, upscli_sendline_timeout - send a single command to a UPS SYNOPSIS -------- - #include + #include /* or on some platforms */ int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, - const long timeout); + const time_t timeout); DESCRIPTION ----------- The *upscli_sendline()* and *upscli_sendline_timeout()* functions take the -pointer 'ups' to a `UPSCONN_t` state structure and transmit a buffer 'buf' of -size 'buflen' to the server. +pointer 'ups' to a `UPSCONN_t` state structure and transmit a buffer 'buf' +of size 'buflen' to the server. The data in 'buf' must be a fully formatted protocol command as no parsing of the buffer occurs within this function. @@ -35,8 +35,8 @@ freedom, and uses an immediate timeout (0 second). RETURN VALUE ------------ -The *upscli_sendline()* and *upscli_sendline_timeout()* functions return 0 on -success, or -1 if an error occurs. +The *upscli_sendline()* and *upscli_sendline_timeout()* functions +return 0 on success, or -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_splitaddr.txt b/docs/man/upscli_splitaddr.txt index 03fd9ff253..b7054939f6 100644 --- a/docs/man/upscli_splitaddr.txt +++ b/docs/man/upscli_splitaddr.txt @@ -9,7 +9,6 @@ upscli_splitaddr - split a listening address into its components SYNOPSIS -------- - #include int upscli_splitaddr(const char *buf, char **hostname, diff --git a/docs/man/upscli_splitname.txt b/docs/man/upscli_splitname.txt index 8990f0cc0c..d40e642982 100644 --- a/docs/man/upscli_splitname.txt +++ b/docs/man/upscli_splitname.txt @@ -37,6 +37,7 @@ Definitions without an explicit port value receive the default value of MEMORY USAGE ------------ + You must *free*(3) the pointers to 'upsname' and 'hostname' when you are done with them to avoid memory leaks. diff --git a/docs/man/upscli_strerror.txt b/docs/man/upscli_strerror.txt index fa6da66e93..b9b8462a1f 100644 --- a/docs/man/upscli_strerror.txt +++ b/docs/man/upscli_strerror.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - char *upscli_strerror(UPSCONN_t *ups); + const char *upscli_strerror(UPSCONN_t *ups); DESCRIPTION ----------- diff --git a/docs/man/upsclient.txt b/docs/man/upsclient.txt index 54210d7f79..0c2ac15ad2 100644 --- a/docs/man/upsclient.txt +++ b/docs/man/upsclient.txt @@ -60,6 +60,7 @@ in memory and file descriptor leaks in your program. ERROR HANDLING -------------- + In the event of an error, linkman:upscli_strerror[3] will provide human-readable details on what happened. linkman:upscli_upserror[3] may also be used to retrieve the error number. These numbers are defined in @@ -67,6 +68,7 @@ also be used to retrieve the error number. These numbers are defined in SEE ALSO -------- + linkman:libupsclient-config[1], linkman:upscli_init[3], linkman:upscli_cleanup[3], linkman:upscli_add_host_cert[3], linkman:upscli_connect[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index c6c48992c8..b8c80d8274 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -3,10 +3,12 @@ UPSCMD(8) NAME ---- + upscmd - UPS administration program for instant commands SYNOPSIS -------- + *upscmd* -h *upscmd* -l 'ups' @@ -45,7 +47,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of command execution by the driver and return its actual result from the device. Note that this feature requires that both upsd -and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +and the driver support TRACKING (NUT version 2.8.0 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). @@ -102,8 +104,10 @@ It involves magic cookies. SEE ALSO -------- + linkman:upsd[8], linkman:upsrw[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upscode2.txt b/docs/man/upscode2.txt index b03763a84b..21c7b02492 100644 --- a/docs/man/upscode2.txt +++ b/docs/man/upscode2.txt @@ -3,22 +3,26 @@ UPSCODE2(8) NAME ---- + upscode2 - Driver for UPScode II compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the upscode2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports UPS equipment which can be controlled via the UPScode II protocol. This is mainly Fiskars, Powerware equipment, but also some (probably OEM'ed) products from Compaq. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -82,18 +86,21 @@ have not returned a value for 'battery.charge'. Therefore, the driver will guestimate a value based on the nominal battery min/max and the current battery voltage. -AUTHOR ------- -HÃ¥vard Lygre , -Niels Baggesen +AUTHORS +------- + +* HÃ¥vard Lygre +* Niels Baggesen SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 412d21ea1f..410910e99e 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -121,11 +121,38 @@ Possible values are : - '1' to require to all clients a certificate - '2' to require to all clients a valid certificate +"DISABLE_WEAK_SSL 'BOOLEAN'":: + +Tell upsd to disable older/weak SSL/TLS protocols and ciphers. +With relatively recent versions of OpenSSL or NSS it will be restricted +to TLSv1.2 or better. +Unless you have really ancient clients, you probably want to enable this. +Currently disabled by default to ensure compatibility with existing setups. + +"DEBUG_MIN 'INTEGER'":: + +Optionally specify a minimum debug level for `upsd` data daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. + SEE ALSO -------- linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsd.users[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 331a2fd066..25d92ca171 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -8,6 +8,7 @@ upsd - UPS information server SYNOPSIS -------- + *upsd* -h *upsd* ['OPTIONS'] @@ -43,8 +44,23 @@ are: *reload*;; reread configuration files *stop*;; stop process and exit +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start `upsd` as a foreground process so it does not create a PID file. +See also `-FF` option as an alternative. + *-D*:: -Raise the debug level. Use this multiple times for additional details. +Raise the debugging level. upsd will run in the foreground by default, +and will print information on stdout about the monitoring process. +Use this option multiple times for more details. + +*-F*:: +upsd will run in the foreground, regardless of debugging settings. +Specify twice (`-FF` or `-F -F`) to save the PID file even in this mode. + +*-B*:: +upsd will run in the background, regardless of debugging settings. *-h*:: Display the help text. @@ -141,25 +157,32 @@ SEE ALSO Clients: ~~~~~~~~ -linkman:upsc[8], linkman:upscmd[8], -linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] + +- linkman:upsc[8] +- linkman:upscmd[8] +- linkman:upsrw[8] +- linkman:upslog[8] +- linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ -linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] + +- linkman:upsset.cgi[8] +- linkman:upsstats.cgi[8] +- linkman:upsimage.cgi[8] + +Driver control: +~~~~~~~~~~~~~~~ + +include::{builddir}/linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ -linkman:nutupsdrv[8], -linkman:apcsmart[8], linkman:belkin[8], linkman:belkinunv[8], -linkman:bestuferrups[8], linkman:bestups[8], -linkman:cyberpower[8], linkman:energizerups[8], linkman:etapro[8], -linkman:everups[8], linkman:genericups[8], -linkman:isbmex[8], linkman:liebert[8], linkman:masterguard[8], -linkman:mge-shut[8], linkman:mge-utalk[8], linkman:oneac[8], -linkman:powercom[8], linkman:safenet[8], linkman:snmp-ups[8], -linkman:tripplite[8], linkman:tripplitesu[8], linkman:victronups[8], + +- linkman:nutupsdrv[8] +include::{builddir}/linkman-driver-names.txt[] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.users.txt b/docs/man/upsd.users.txt index 10bbdba262..6b287d02e5 100644 --- a/docs/man/upsd.users.txt +++ b/docs/man/upsd.users.txt @@ -3,6 +3,7 @@ UPSD.USERS(5) NAME ---- + upsd.users - Administrative user definitions for NUT upsd DESCRIPTION @@ -84,7 +85,8 @@ SEE ALSO linkman:upsd[8], linkman:upsd.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index da0593142d..46ec658d7c 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -8,6 +8,7 @@ upsdrvctl - UPS driver controller SYNOPSIS -------- + *upsdrvctl* -h *upsdrvctl* ['OPTIONS'] {start | stop | shutdown} ['ups'] @@ -57,6 +58,9 @@ This may be set in ups.conf with "user" in the global section. *-D*:: Raise the debug level. Use this multiple times for additional details. +*-d*:: +Pass the selected debug level from `upsdrvctl` to launched drivers. + COMMANDS -------- @@ -100,8 +104,10 @@ background. SEE ALSO -------- + linkman:upsdrvsvcctl[8], linkman:nutupsdrv[8], linkman:upsd[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index 9ad3eeaf94..e0d912dc80 100644 --- a/docs/man/upsdrvsvcctl.txt +++ b/docs/man/upsdrvsvcctl.txt @@ -8,6 +8,7 @@ upsdrvsvcctl - UPS driver service instance controller SYNOPSIS -------- + *upsdrvsvcctl* -h *upsdrvsvcctl* ['OPTIONS'] {start | stop } ['ups'] @@ -193,11 +194,18 @@ copy of the journals stored in the filesystem. *Solaris SMF*:: Look for `/var/svc/log/system-power-nut-driver:instance-name.log` file. +AUTHOR +------ + +Jim Klimov + SEE ALSO -------- + linkman:upsdrvctl[8], linkman:nutupsdrv[8], linkman:upsd[8], linkman:nut-driver-enumerator[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsimage.cgi.txt b/docs/man/upsimage.cgi.txt index 9ede74a723..bdf23b83f6 100644 --- a/docs/man/upsimage.cgi.txt +++ b/docs/man/upsimage.cgi.txt @@ -45,6 +45,5 @@ linkman:upsd[8], linkman:upsstats.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The gd home page: http://libgd.bitbucket.org - -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The gd home page: http://libgd.bitbucket.org diff --git a/docs/man/upslog.txt b/docs/man/upslog.txt index 4eaa572503..f405104888 100644 --- a/docs/man/upslog.txt +++ b/docs/man/upslog.txt @@ -65,7 +65,14 @@ the linkman:upsclient[3] library. Store the results in this file. + -You can use "-" for stdout, but upslog will remain in the foreground. +You can use "-" for stdout, but upslog will remain in the foreground +by default. + +*-F*:: +upslog will run in the foreground, regardless of logging target. + +*-B*:: +upslog will run in the background, regardless of logging target. *-s* 'ups':: Monitor this UPS. The format for this option is @@ -73,7 +80,7 @@ Monitor this UPS. The format for this option is *-u* 'username':: -If started as root, upsmon will *setuid*(2) to the user id +If started as root, upslog will *setuid*(2) to the user id associated with 'username' for security. + If 'username' is not defined, it will use the value that was compiled into the @@ -108,13 +115,16 @@ SEE ALSO Server: ~~~~~~~ + linkman:upsd[8] Clients: ~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8], linkman:upssched[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 4551c95628..1bd18ab242 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -226,7 +226,7 @@ REPLBATT;; The UPS battery is bad and needs to be replaced NOCOMM;; A UPS is unavailable (can't be contacted for monitoring) -*NOTIFYFLAG* 'type' 'flag'[\+'flag'][+'flag']...:: +*NOTIFYFLAG* 'type' 'flag'[+'flag']...:: By default, upsmon sends walls global messages to all logged in users) via /bin/wall and writes to the syslog when things happen. You can @@ -393,10 +393,30 @@ support SSL, so don't use it unless all of them have it running. When compiled with NSS support of SSL, can be overridden for host specified with a CERTHOST directive. +*DEBUG_MIN* 'INTEGER':: + +Optionally specify a minimum debug level for `upsmon` daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. + SEE ALSO -------- + linkman:upsmon[8], linkman:upsd[8], linkman:nutupsdrv[8]. Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 1478275fdd..cb202fcc33 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -11,9 +11,9 @@ SYNOPSIS *upsmon* -h -*upsmon* -c 'command' +*upsmon* -c 'command' [-P 'pid'] -*upsmon* [-D] [-K] [-p] [-u 'user'] +*upsmon* [-D] [-F | -B] [-K] [-p] [-u 'user'] DESCRIPTION ----------- @@ -44,10 +44,22 @@ commands are: *reload*;; reread linkman:upsmon.conf[5] configuration file. See "reloading nuances" below if this doesn't work. +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start main `upsmon` as a foreground process so it does not have to +rely on a PID file. + *-D*:: -Raise the debugging level. upsmon will run in the foreground and prints -information on stdout about the monitoring process. Use this multiple -times for more details. +Raise the debugging level. upsmon will run in the foreground by default, +and will print information on stdout about the monitoring process. +Use this option multiple times for more details. + +*-F*:: +upsmon will run in the foreground, regardless of debugging settings. + +*-B*:: +upsmon will run in the background, regardless of debugging settings. *-K*:: Test for the shutdown flag. If it exists and contains the magic string @@ -163,7 +175,7 @@ also receives the notification message (see below) as the first (and only) argument, so you can deliver a pre-formatted message too. Note that the NOTIFYCMD will only be called for a given event when you set -the EXEC flag by using the notify flags, below: +the EXEC flag by using the notify flags, as detailed below. NOTIFY FLAGS ------------ @@ -438,17 +450,21 @@ SEE ALSO Server: ~~~~~~~ + linkman:upsd[8] Clients: ~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ + linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 54a79f4e92..ebcd2056d1 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -9,7 +9,7 @@ upsrw - UPS variable administration tool SYNOPSIS -------- -*upsrw* 'ups' +*upsrw* [-l] 'ups' *upsrw* -h @@ -48,6 +48,12 @@ length limit. Others are enumerated types and can only be set to one of those values. Others may be within an allowed range of values. Refer to the list to know what's available in your hardware. +*-l*:: +Just display the list of the variables and their possible values. ++ +Same as default activity without '-s' argument, provided for CLI similarity +with other tools. + *-u* 'username':: Set the NUT username for the connection to the server. This is optional, and you will be prompted for this when using the -s option if you don't @@ -61,7 +67,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of setting execution by the driver and return its actual result from the device. Note that this feature requires that both upsd -and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +and the driver support TRACKING (NUT version 2.8.0 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). @@ -112,8 +118,10 @@ confusing. SEE ALSO -------- + linkman:upsd[8], linkman:upscmd[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upssched.conf.txt b/docs/man/upssched.conf.txt index baa7f4f43b..52a028c77d 100644 --- a/docs/man/upssched.conf.txt +++ b/docs/man/upssched.conf.txt @@ -107,4 +107,5 @@ linkman:upssched[8], linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upssched.txt b/docs/man/upssched.txt index f15581e9ab..0fb033b973 100644 --- a/docs/man/upssched.txt +++ b/docs/man/upssched.txt @@ -8,6 +8,7 @@ upssched - Timer helper for scheduling events from upsmon SYNOPSIS -------- + *upssched* NOTE: *upssched* should be run from linkman:upsmon[8] via the NOTIFYCMD. @@ -112,4 +113,5 @@ linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsset.cgi.txt b/docs/man/upsset.cgi.txt index 79f5786d15..78912dda12 100644 --- a/docs/man/upsset.cgi.txt +++ b/docs/man/upsset.cgi.txt @@ -94,4 +94,5 @@ SEE ALSO Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsset.conf.txt b/docs/man/upsset.conf.txt index 2058a24907..02cd996714 100644 --- a/docs/man/upsset.conf.txt +++ b/docs/man/upsset.conf.txt @@ -55,8 +55,10 @@ web server, don't blame me. SEE ALSO -------- + linkman:upsset.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsstats.cgi.txt b/docs/man/upsstats.cgi.txt index 141cfefb24..e79e5395ca 100644 --- a/docs/man/upsstats.cgi.txt +++ b/docs/man/upsstats.cgi.txt @@ -59,4 +59,5 @@ linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsstats.html.txt b/docs/man/upsstats.html.txt index 9d30841f10..ec869f480a 100644 --- a/docs/man/upsstats.html.txt +++ b/docs/man/upsstats.html.txt @@ -232,4 +232,5 @@ linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 0677b6784a..bd5f7d1c26 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -31,6 +31,7 @@ At the present time, usbhid-ups supports: - some Belkin models, - some Cyber Power Systems models, - some Powercom models, + - some PowerWalker models, - some TrippLite models. For a more complete list, refer to the NUT hardware compatibility list, @@ -89,6 +90,12 @@ If this flag is set, the driver will not use Interrupt In transfers during the shorter "pollinterval" cycles (not recommended, but needed if these reports are broken on your UPS). +*onlinedischarge*:: +If this flag is set, the driver will treat `OL+DISCHRG` status as offline. +For most devices this combination means calibration or similar maintenance; +however some UPS models (e.g. CyberPower UT series) emit `OL+DISCHRG` when +wall power is lost -- and need this option to handle shutdowns. + *vendor*='regex':: *product*='regex':: *serial*='regex':: @@ -236,6 +243,33 @@ If you do the same without mains present, it should do the same, but in this case, the outputs shall remain off until mains power is applied again. +UPS cuts power too soon +~~~~~~~~~~~~~~~~~~~~~~~ + +Note that many Cyber Power Systems (CPS) models tend to divide `offdelay` +by 60 and round down, so the minimum advisable value is 60 (seconds) to avoid +powering off immediately after NUT sends the shutdown command to the UPS. + +UPS does not set battery.charge.low but says OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Note that many Cyber Power Systems (CPS) models tend to allow only certain +values for `battery.charge.low` and anything outside of the set of allowed +values are rounded or ignored. + +A shell loop like this can help you map out the allowed values: + +==== + for i in `seq 90 -1 0`; do echo "set to $i"; \ + upsrw -s battery.charge.low=$i -u * -p * cps-big; \ + sleep 1; upsc cps-big battery.charge.low; echo ""; \ + done +==== + +For example, for CPS PR1000LCDRTXL2U model, the only allowed values are +`[60,55,50,45,40,35,30,25,20]` and in some cases, your UPS may effectively +not support a value of 10 for the `battery.charge.low` setting. + HISTORY ------- @@ -246,16 +280,22 @@ AUTHORS ------- Originally sponsored by MGE UPS SYSTEMS. + Now sponsored by Eaton -Arnaud Quette, Peter Selinger, Arjen de Korte + +* Arnaud Quette +* Peter Selinger +* Arjen de Korte SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/victronups.txt b/docs/man/victronups.txt index 05be10c1b6..17be94d7aa 100644 --- a/docs/man/victronups.txt +++ b/docs/man/victronups.txt @@ -3,16 +3,19 @@ VICTRONUPS(8) NAME ---- + victronups - Driver for IMV/Victron UPS unit Match, Match Lite, NetUps NOTE ---- + This man page only documents the hardware-specific features of the the victronups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The victronups driver should recognize all Victron models that use a serial protocol at 1200 bps. These include Match Lite, Match and the NetUps line. @@ -25,10 +28,12 @@ CABLING If your Victron cable is broken or missing, use this diagram to build a clone: -docs/cables/victron.txt +* https://github.com/networkupstools/nut/blob/master/docs/cables/ge-imv-victron.txt +* link:docs/cables/ge-imv-victron.txt[] EXTRA ARGUMENTS --------------- + This driver supports the following optional setting in the linkman:ups.conf[5]: @@ -41,20 +46,24 @@ Set delay before shutdown on UPS BUGS ---- + The protocol for this UPS is not officially documented. -AUTHOR ------- -Radek Benedikt , -Daniel Prynych +AUTHORS +------- + +* Radek Benedikt +* Daniel Prynych SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 6186b6e1bb..0af943381d 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -44,12 +44,16 @@ NUT network protocol, over the time: |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables -.2+|1.3 .2+|>= 2.7.5 |Add "cmdparam" to "INSTCMD" +.4+|1.3 .4+|>= 2.8.0 |Add "cmdparam" to "INSTCMD" |Add "TRACKING" commands (GET, SET) + |Add "PRIMARY" as alias to older "MASTER" + (implementation tested to be backwards + compatible in `upsd` and `upsmon`) + |Add "PROTVER" as alias to older "NETVER" |=============================================================================== -NOTE: any new version of the protocol implies an update of NUT_NETVERSION -in *configure.ac*. +NOTE: Any new version of the protocol implies an update of `NUT_NETVERSION` +in 'configure.ac' file. GET @@ -489,8 +493,11 @@ NOTE: You probably shouldn't send this command unless you are upsmon, or a upsmon replacement. -MASTER ------- +PRIMARY (since NUT 2.8.0) or MASTER (deprecated) +------------------------------------------------ + +NOTE: This command was renamed in NUT 2.8.0 to "PRIMARY" with the older +name "MASTER" kept as deprecated alias for compatibility. Form: @@ -498,19 +505,26 @@ Form: Response: - OK (upon success) + OK MASTER-GRANTED (upon success) + +Form: + + PRIMARY + +Response: + + OK PRIMARY-GRANTED (upon success) or <> NOTE: This requires "upsmon primary" in upsd.users +NOTE: Previously documented response was just `OK` -- clients checking +that server reply *starts with* that keyword would handle all cases. + This function doesn't do much by itself. It is used by upsmon to make sure that primary-mode functions like FSD are available if necessary. -NOTE: This command will be eventually renamed to "PRIMARY" with the older -name "MASTER" kept as deprecated alias for compatibility; a protocol bump -or special backwards-compatibility handling may be needed for that however. - FSD --- @@ -614,6 +628,7 @@ Other commands - HELP: lists the commands supported by this server - VER: shows the version of the server currently in use - NETVER: shows the version of the network protocol currently in use + (aliased as PROTVER since NUT v2.8.0, or formal protocol version 1.3) These three are not intended to be used directly by programs. Humans can make use of this program by using telnet or netcat. If you use diff --git a/docs/new-clients.txt b/docs/new-clients.txt index 6c4c073346..1b07900071 100644 --- a/docs/new-clients.txt +++ b/docs/new-clients.txt @@ -51,6 +51,7 @@ and commands with an object-oriented API in C++ and C. For more information, refer to the linkman:libnutclient[3] manual page. +------ #include #include #include @@ -128,6 +129,7 @@ For more information, refer to the linkman:libnutclient[3] manual page. delete client; return 0; } +------ Configuration helpers @@ -144,7 +146,8 @@ Python The PyNUT module, contributed by David Goncalves, can be used for connecting a Python script to `upsd`. Note that this code (and the accompanying NUT-Monitor -application) is licensed under the GPL v3. +application, later separated into NUT-Monitor-py2gtk2 and NUT-Monitor-py3qt5, +suitable for two generations of Python ecosystem) is licensed under the GPL v3. The `PyNUTClient` class abstracts the connection to the server. In order to list the status variables for `ups1` on the local `upsd`, the following @@ -181,6 +184,7 @@ of the results. The Perl class instance encapsulates a single UPS, where the Python class instance represents a connection to the server (which may service multiple UPS units). +------ use UPS::Nut; $ups = new UPS::Nut( NAME => "myups", @@ -203,10 +207,10 @@ multiple UPS units). ; print $other_ups{MFR}, " ", $other_ups{MODEL}, "\n"; +------ Java ---- The NUT Java support has been externalized. It is available at https://github.com/networkupstools/jnut - diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 8b4bd252d4..4f97ab5612 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -87,11 +87,14 @@ during a transition period. The ups.* data will then be removed. | device.macaddr | Physical network address of the device | 68:b5:99:f5:89:27 | device.uptime | Device uptime in seconds | 1782 | device.count | Total number of daisychained devices | 1 -|===================================================================================== +|==================================================================================== -NOTE: When present, device.count implies daisychain support. For more +[NOTE] +====== +When present, `device.count` implies daisychain support. For more information, refer to the <> chapter of the user manual and developer guide. +====== ups: General unit information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -172,10 +175,11 @@ ups: General unit information shutdown ability (poweroff) | enabled |=============================================================================== -NOTE: When present, the value of *ups.start.auto* has an impact on shutdown.* -commands. For the sake of coherence, shutdown commands will set *ups.start.auto* -to the right value before issuing the command. That is, shutdown.stayoff will first -set *ups.start.auto* to *no*, while shutdown.return will set it to *yes*. +NOTE: When present, the value of `ups.start.auto` has an impact on +`shutdown.*` commands. For the sake of coherence, shutdown commands +will set `ups.start.auto` to the right value before issuing the command. +That is, `shutdown.stayoff` will first set `ups.start.auto` to `no`, +while `shutdown.return` will set it to `yes`. NOTE: When possible, time-stamps and dates should be expressed as detailed above in the Time and Date format chapter. @@ -244,8 +248,8 @@ input: Incoming line/power information of full) | 25 | input.realpower | Current sum value of all (ePDU) phases real power (W) | 300 -| input.realpower.nominal | Typical power capacity of the - ePDU input (W) | 2000 +| input.realpower.nominal | Nominal sum value of all (ePDU) + phases real power (W) | 850 | input.power | Current sum value of all (ePDU) phases apparent power (VA) | 500 | input.source | The current input power source | 1 @@ -277,21 +281,24 @@ broken down to their base components. Phase Count Determination ^^^^^^^^^^^^^^^^^^^^^^^^^ -input.phases (3 for three-phase, absent or 1 for 1phase) -output.phases (as for input.phases) + +`input.phases` (3 for three-phase, absent or 1 for 1phase) + +`output.phases` (as for `input.phases`) DOMAINs ^^^^^^^ + Any input or output is considered a valid DOMAIN. -input (should really be called input.mains, but keep this for compat) -input.bypass -input.servicebypass +* input (should really be called input.mains, but keep this for compat) +** input.bypass +** input.servicebypass -output (should really be called output.load, but keep this for compat) -output.bypass -output.inverter -output.servicebypass +* output (should really be called output.load, but keep this for compat) +** output.bypass +** output.inverter +** output.servicebypass Specification (SPEC) ^^^^^^^^^^^^^^^^^^^^ @@ -300,6 +307,7 @@ Voltage, current, frequency, etc are considered to be a specification of the measurement. With this notation, the old 1phase naming scheme becomes DOMAIN.SPEC + Example: `input.current` CONTEXT @@ -310,6 +318,7 @@ measurements in more detail. We call this the CONTEXT. With this notation, the naming scheme becomes DOMAIN.CONTEXT.SPEC when in three-phase mode. + Example: `input.L1.current` Valid CONTEXTs @@ -470,15 +479,15 @@ battery: Any battery details |=============================================================================== NOTE: -battery.charger.status replaces the historic flags CHRG and DISCHRG that were -exposed through ups.status. battery.charger.status can have one of the -following value: +`battery.charger.status` replaces the historic flags `CHRG` and `DISCHRG` +that were exposed through `ups.status`. +The `battery.charger.status` can have one of the following values: -- charging: battery is charging, -- discharging: battery is discharging, -- floating: battery has completed its charge cycle, and waiting to go to resting -mode, -- resting: the battery is fully charged, and not charging nor discharging. +- `charging`: battery is charging, +- `discharging`: battery is discharging, +- `floating`: battery has completed its charge cycle, + and waiting to go to resting mode, +- `resting`: the battery is fully charged, and not charging nor discharging. NOTE: When possible, time-stamps and dates should be expressed as detailed above in the Time and Date format chapter. @@ -658,6 +667,8 @@ Some specific data to outlet groups exists: |================================================================================= | Name | Description | Example value | outlet.group.n.type | Type of outlet group (OPAQUE) | outlet-section +| outlet.group.n.color | Color-coding of the outlets + in this group (OPAQUE) | yellow | outlet.group.n.count | Number of outlets in the group | 12 | outlet.group.n.phase | Electrical phase to which the physical outlet group (Gang) is diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index 8d237d182d..ad50881fe8 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -11,7 +11,7 @@ Documentation ------------- The documentation toolchain uses -link:http://www.methods.co.nz/asciidoc/[AsciiDoc] to output both HTML pages and +link:https://asciidoc.org/[AsciiDoc] to output both HTML pages and manual pages (troff). This single point of control fills many gaps, suppresses many redundancies, and optimizes documentation management in general. @@ -30,8 +30,9 @@ link:http://aspell.net[Aspell], both interactively (using 'make spellcheck-interactive') and automatically in Buildbot (using 'make spellcheck'). -NOTE: A NUT dictionary is also available (docs/nut.dict), providing a glossary -of terms related to power devices and management. +NOTE: A NUT dictionary is also available (docs/nut.dict), providing a +glossary of terms related to power devices and management, as well as +partial terms, technical jargon and author names. Source code ----------- @@ -67,7 +68,9 @@ mailing list. //////////////////////////////////////////////////////////////////////////////// - link:http://buildbot.networkupstools.org/public/nut/[Buildbot] - and the new dedicated Jenkins incarnation of the NUT CI Farm with "legacy UI" + for older take on multi-platform builds, and the + +- new dedicated Jenkins incarnation of the NUT CI Farm with "legacy UI" for link:https://ci.networkupstools.org/job/nut/job/nut/job/master/[main branch] and link:https://ci.networkupstools.org/job/nut/job/nut/view/change-requests/[PRs], also accessible at the slower but slicker-looking Blue Ocean user interface for @@ -89,9 +92,12 @@ mailing list, and fixed quickly. - a project portal with trackers for bugs, feature request, patches and tasks +- LGTM.COM automatically checking C/C++ and Python code + - Static code analysis: * link:https://scan.coverity.com/projects/networkupstools-nut[Coverity Scan overview of NUT] * status: image:https://scan.coverity.com/projects/8829/badge.svg[Coverity Scan Build Status] + * cppcheck as part of NUT CI farm builds and reports NUT QA also relies on external tools and trackers, like: @@ -112,16 +118,26 @@ FIXME (POST): the installation, upgrade and removal testing processes. - a runtime testing suite, which automates the inter-layer communication testing -(driver -- upsd -- upsmon / clients), that is part of Ubuntu. -link:http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/view/head:/scripts/test-nut.py[The NUT testing script] -is available in the link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite]. -It installs NUT, configures it with the dummy-ups driver, changes a few data and -checks that these are well propagated with upsc. + (driver -- upsd -- upsmon / clients), that is part of Ubuntu. + link:https://git.launchpad.net/ubuntu/+source/nut/tree/debian/tests/test-nut.py[The NUT testing script] + is available in the link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite]. ++ + It installs NUT packages, configures it with the dummy-ups driver, changes + a few data points and checks that these are well propagated with upsc. + +- similar approach is explored in NIT (NUT Integration Testing) suite, + which is part of the codebase and automated with `make check-NIT`; + it can also be added to default `make check` activities by running + `configure --enable-check-NIT` + + * Note that developers updating components which directly impact NIT runs + may benefit from `make check-NIT-devel` target, to rebuild the `upsd`, + `dummy-ups`, `cppnit` and other programs used in the test as they iterate. - link:https://bugzilla.redhat.com/buglist.cgi?component=nut[Redhat / Fedora Bug tracker] - link:https://www.openhub.net/p/nut[Black Duck Open Hub] (formerly Ohloh.net) -provides metrics on NUT source code base and activity. + provides metrics on NUT source code base and activity. Runtime quality ~~~~~~~~~~~~~~~ diff --git a/docs/nut.dict b/docs/nut.dict index eab32f0839..71773839fe 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,5 +1,6 @@ -personal_ws-1.1 en 2918 utf-8 +personal_ws-1.1 en 3008 utf-8 AAS +ABI ACFAIL ACFREQ ACK @@ -10,6 +11,7 @@ ACrms ADDR ADDRCONFIG ADDRINFO +ADELSYSTEM ADK ADKK AEC @@ -35,6 +37,8 @@ ATT ATTRS ATX ATs +AUTOCOMMIT +AUTOPUSH AVL AVR AVRLCD @@ -56,7 +60,6 @@ AlmCPol AlmEnbl Amplon Ampère -Amplon Andreas Andreassen Andrzej @@ -154,6 +157,7 @@ ByPass CA's CABAC CAs +CBI CBLimit CCC CCCC @@ -238,11 +242,13 @@ DEBUGOUT DELCMD DELENUM DELINFO +DELPHYS DELRANGE DES DESTDIR DF DHEA +DIGYS DISCHRG DMF DMFs @@ -274,6 +280,7 @@ DiSplay Diehl Dietze Digitus +Digys Dimitris Dly Dmitry @@ -288,6 +295,7 @@ EG EL ELCD EMI +EMP EMPDT ENDFOR ENV @@ -452,6 +460,7 @@ ID's IDEN IDentifiers IFBETWEEN +IFF IFSUPP IGN IMG @@ -491,10 +500,12 @@ Invter IoT Ioannou JAWAN +JBUS JBus JKL JRE JSON +JVM JW Jageson Jarosch @@ -559,8 +570,10 @@ LINEV LISTINSTCMD LISTRW LISTVARS +LLNC LOADPCT LOCKFN +LOCKNAME LOTRANS LUA LVM @@ -574,8 +587,10 @@ Laventhol Legrand Lepple Levente +LibGD LibLTDL LibNEON +LibUSB LineA LineB LineSens @@ -600,6 +615,7 @@ MAXPARMAKES MBATTCHG MCOL MCU +MEC MEGATAEC MH MIBs @@ -617,6 +633,7 @@ MQ MSI MSII MSIII +MX MacKenzie's MacOS Maccelari @@ -661,6 +678,7 @@ MiniGuard Minislot Moar Modbus +ModemManager MonAMI MonUPS Monett @@ -696,6 +714,7 @@ NOAUTH NOCOMM NOCOMMWARNTIME NOCONF +NOGET NOMBATTV NOMINV NOMOUTV @@ -752,8 +771,10 @@ OMNIVSINT ONF ONV OOM +OSABI OSF OSs +OUTDIR OUTPUTV OUTVOLT OV @@ -791,6 +812,7 @@ PBTn PBTnn PC PC's +PCI PDC PDUs PDX @@ -821,6 +843,7 @@ PPPPPPPPPP PR PR'ed PROGS +PROTVER PRs PSA PSD @@ -835,6 +858,7 @@ PULS PV PWLv PWR +PXG PaaS Pac Parisi @@ -896,7 +920,9 @@ PresentStatus Priv Procomm ProductID +Proxmox Prynych +Pulizzi PwrOut PyGTK PyNUT @@ -935,8 +961,10 @@ RDNT RDWR README REDi +REFREPO REPLBATT REQSSL +RESPIN RETPCT RK RMCARD @@ -1076,6 +1104,7 @@ SendEnv Senoidal Sep Sequentializing +SerialNumber Serv Shara Shaul @@ -1142,6 +1171,7 @@ TEMPC TEMPF TESTEDFILE TESTEDINDEX +TGS TIMELEFT TIOCM TIOCMBIC @@ -1157,6 +1187,7 @@ TST TT TTT TXF +TXG TXV TXVxx TapSwDly @@ -1199,6 +1230,7 @@ UID UIDA UINT UNKCOMMAND +UNSTASH UNV UPGUARDS UPM @@ -1215,6 +1247,7 @@ UPSTEMP UPScode UPSes UPSilon +UPSmart UPSmon UPSonic UPSs @@ -1246,6 +1279,7 @@ VER VERFW VFI VIB +VM VMIN VMM VMware @@ -1345,6 +1379,7 @@ addenum addinfo addr addrange +adelsystem adkorte adm admin's @@ -1401,6 +1436,7 @@ ascii asciidoc asem aspell +ast async atcl ats @@ -1507,6 +1543,7 @@ buflen bugfix bugfixes buildbots +builddir bullseye busybox bv @@ -1516,6 +1553,7 @@ cablepower calloc cb cbe +cbi cbl cblimit ccache @@ -1610,6 +1648,7 @@ coverity cp cpp cppcheck +cppnit cppunit cpqpower cpsups @@ -1623,6 +1662,9 @@ crw csh cshdelay css +cstdint +ctime +ctrl cts ctypes cua @@ -1656,6 +1698,7 @@ decrement decrypt dedb dedup +deduplication defun dep dephasing @@ -1674,9 +1717,11 @@ devscan dfl dhcp dialout +difftool dipsw dir dirpath +discardable disp distcheck distclean @@ -1685,6 +1730,7 @@ distros dl dll dlopen +dmake dmesg dmf dmfdir @@ -1706,6 +1752,7 @@ dq driverexec drivername driverpath +drivertool drv drvctl drvpath @@ -1842,6 +1889,7 @@ getent getenv getopt getvar +gitcache github gitignore gitk @@ -1856,6 +1904,7 @@ gpg graphviz groupadd groupname +gtk guardpend guardpstart guestimate @@ -1868,12 +1917,15 @@ hal hardcoded hasFeature hb +hcd hcl hg hh hibernate's hiddev hidparser +hidraw +hidtypes hidups highbattery highfrequency @@ -1892,6 +1944,8 @@ httpd https huawei hunnox +hypervisor +hypervisors iBox iDowell iManufacturer @@ -1954,6 +2008,7 @@ io ioLogik ioLogikE ioLogikR +iocage iostream ip ipE @@ -1971,6 +2026,7 @@ ivtscd jNUT jNut jNutWebAPI +jbus jdk jenkins jessie @@ -2023,6 +2079,7 @@ libneon libnss libnut libnutclient +libnutclientsub libnutconfig libnutscan libpng @@ -2089,6 +2146,7 @@ lxcbr lxccontainer lxml lxyz +lz mA mDNS mS @@ -2122,6 +2180,7 @@ mecer megatec memset merchantability +mergetool metadata metasys methodOfFlowControl @@ -2224,6 +2283,7 @@ nobody's nobt nodtk noflag +nogroup nohang noimp noinst @@ -2277,6 +2337,7 @@ oftd oid oids ok +oksh ol oldmac oldmge @@ -2287,25 +2348,31 @@ onclick ondelay oneac online +onlinedischarge ont ontd ontimedays ontiniedays ooce openSUSE +openipmi openjdk openlog +openmp opensolaris openssh openssl +optimizations optiups oq os ostream otherprotocols +otheruser outliers pF pacman +param paramkeywords parsability parsable @@ -2343,6 +2410,7 @@ pinouts pkg pkgconf pkgconfig +pkgin plaintext plugin plugnplay @@ -2400,6 +2468,7 @@ prog prtconf psu pty +pulizzi pw pwl pwmib @@ -2428,6 +2497,7 @@ raritan ratedva ratedwatts rb +rcctl readline readonly realpower @@ -2492,6 +2562,7 @@ sbin sbindir scd schemas +scm screenshot screenshots scriptname @@ -2514,6 +2585,7 @@ selftest sendback sendline sendmail +sequentialized ser seria serialno @@ -2533,6 +2605,7 @@ setvar's sfr sgml sgs +shm shutdownArguments shutdowncmd shutdowndebounce @@ -2542,6 +2615,7 @@ shutdowntime shutup si siemens +sig sigaction sigmask signedness @@ -2569,6 +2643,7 @@ snprintf snprintfcat snr sockdebug +socomec solaris solis somepass @@ -2688,11 +2763,13 @@ th timehead timeline timername +timestamp timeticks tiocm tios tmp tmpfiles +tmpfs tonumber toolchain toolkits @@ -2719,6 +2796,7 @@ tuple turnon tw tx +txg txt typedef uA @@ -2744,12 +2822,14 @@ undervoltage unescaped uninstall uninterruptible +uniq unistd unitidentify unmapped unmounts unpowered unshutup +unstash updateinfo upexia upower @@ -2800,6 +2880,7 @@ upstype upsuser upswired uptime +urb urpmi usb usbconfig @@ -2814,6 +2895,7 @@ usec useconds useradd userid +userland usermap username usernames @@ -2852,6 +2934,7 @@ vin virsh virtualenv virtualization +vivo vo vod voltronic @@ -2859,19 +2942,24 @@ von wDescriptorLength wakeup wastePower +wc wchar webserver wf wget whitespace wiki +winnutclient +wmNUT wmnut wordformat workflow workspace +workspaces writability writeinfo writeups +ws xAAAA xCC xD @@ -2890,6 +2978,7 @@ xfff xffff xfmrresistance xh +xhci xhtml xmalloc xml @@ -2909,6 +2998,7 @@ xxxxAP youruid yyy zaac +zfs zinto zlib zsh diff --git a/docs/packager-guide.txt b/docs/packager-guide.txt index 4730dcb2bb..0a65fb46d6 100644 --- a/docs/packager-guide.txt +++ b/docs/packager-guide.txt @@ -106,7 +106,10 @@ Packagers involved The following packagers are working on this subject: - Debian (and derivatives): Arnaud Quette -- SUSE/Novell: Stanislav Brabec +- SUSE/Novell: Stanislav Brabec +- Solaris, OpenSolaris, OpenIndiana and related illumos distributions: + Jim Klimov +- MacOS: Charles Lepple NOTE: the people below should be contacted to (re)launch discussions! @@ -120,9 +123,7 @@ The following packagers should be interested in working on this subject: - OpenBSD: <> - PLD: Andrzej Zawadzki - E-Smith: Charlie Brady -- Solaris, OpenSolaris, OpenIndiana and related illumos distributions: Jim Klimov - Windows: check with WinNUT author?! -- MacOS: <> => Charles Lepple? <> - HP-UX: <> - IBM AIX: <> @@ -130,6 +131,7 @@ The following packagers should be interested in working on this subject: Possible use cases ------------------ + - standalone (1 system + 1-n UPS) - network server (same as standalone, but serving data to network clients) - network monitoring client @@ -162,6 +164,7 @@ This standard was created by: Overview of the package tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + FIXME: make a dependency graph - <> @@ -383,6 +386,7 @@ TO BE CONTINUED Configuration option ^^^^^^^^^^^^^^^^^^^^ + name= "ups" or "nut" ./configure \ --prefix=/ \ @@ -403,4 +407,3 @@ html-path ... ------------------------------------------------------------------------ - diff --git a/docs/snmp-subdrivers.txt b/docs/snmp-subdrivers.txt index f010ff8960..d71c35f628 100644 --- a/docs/snmp-subdrivers.txt +++ b/docs/snmp-subdrivers.txt @@ -226,7 +226,7 @@ displays the textual OID name. For example, the following entry: /* upsMIB.upsObjects.upsIdent.upsIdentModel = STRING: "Dell UPS Tower 1920W HV" */ { "unmapped.upsidentmodel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, -Many time, only the first field will need to be modified, to map to an actual +Many times, only the first field will need to be modified, to map to an actual NUT variable name. Check the <> section first diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index e4701e5234..32cd5bf2e7 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -185,6 +185,7 @@ INSTCMD INSTCMD load.on 10 TRACKING 1bd31808-cb49-4aec-9d75-d056e6f018d2 NOTE: + * is an additional and optional parameter for the command, * "TRACKING " can be provided to track commands execution status, if TRACKING was set to ON on upsd. In this case, driver will later return @@ -199,6 +200,7 @@ SET SET ups.id "Data room" TRACKING 2dedb58a-3b91-4fab-831f-c8af4b90760a NOTE: + * "TRACKING " can be provided to track commands execution status, if TRACKING was set to ON on upsd. In this case, driver will later return the execution status, using TRACKING. diff --git a/docs/solaris-usb.txt b/docs/solaris-usb.txt index ee658805b8..1eea303069 100644 --- a/docs/solaris-usb.txt +++ b/docs/solaris-usb.txt @@ -26,11 +26,14 @@ Connect the power device using its USB port to your computer. Run `prtconf -v | less` to see the details of device connections, and search for its probable strings (vendor, model, serial number). -Two examples follow: -In this example, no suitable driver was attached "out of the box": +Two examples follow: -```` +* In this example, no suitable driver was attached "out of the box": ++ +---- +:; prtconf -v +... input (driver not attached) Hardware properties: name='driver-minor' type=int items=1 @@ -64,15 +67,17 @@ In this example, no suitable driver was attached "out of the box": value=00000002 name='assigned-address' type=int items=1 value=00000003 -```` +---- -In the following example, a "hid power" driver was attached, giving +* In the following example, a "hid power" driver was attached, giving some usability to the device although not enough for NUT to interact well (at least, according to the helpful notes in the https://web.archive.org/web/20140126045707/http://barbz.com.au/blog/?p=407 blog entry): - -```` ++ +---- +:; prtconf -v +... input, instance #1 Driver properties: name='pm-components' type=string items=3 dev=none @@ -114,34 +119,34 @@ blog entry): dev_path=/pci@0,0/pci8086,7270@1d/hub@1/input@3:hid_0_1 spectype=chr type=minor dev_link=/dev/usb/hid0 -```` +---- You can also check with `cfgadm` if the device is at least somehow visible (if not, there can be hardware issues in play). For example, if there is a physical link but no recognized driver was attached, the device would show up as "unconfigured": -```` -# cfgadm | grep usb- +---- +:; cfgadm | grep usb- usb8/1 usb-input connected unconfigured ok -```` +---- If you conclude that a change is needed, you would need to unload the existing copy of the "ugen" driver and set it up to handle the -device patterns that you find in 'compatible' values from `prtconf`, -e.g. for monitoring the devices from listings above: +device patterns that you find in 'compatible' values from `prtconf`. +For example, to monitor the devices from listings above, you would run: -```` -rem_drv ugen -add_drv -i '"usb463,ffff.100"' -m '* 0666 root sys' ugen -```` +---- +:; rem_drv ugen +:; add_drv -i '"usb463,ffff.100"' -m '* 0666 root sys' ugen +---- or -```` -rem_drv ugen -add_drv -i '"usb665,5161.2"' -m '* 0666 root sys' ugen -```` +---- +:; rem_drv ugen +:; add_drv -i '"usb665,5161.2"' -m '* 0666 root sys' ugen +---- Note that there are many patterns in the 'compatible' line which allow for narrower or wider catchment. It is recommended to match @@ -158,19 +163,25 @@ this access to the account that the NUT driver would run as. After proper driver binding, `cfgadm` should expose the details: -```` +---- # cfgadm -lv ... usb8/1 connected configured ok Mfg: EATON Product: Eaton 9PX NConfigs: 1 Config: 0 unavailable usb-input n /devices/pci@0,0/pci103c,1309@1d,2:1 ... -```` +---- Usually the driver mapping should set up the "friendly" device nodes under `/dev/` tree as well (symlinks to real entries in `/devices/`) so for NUT drivers you would specify a `port=/dev/usb/463.ffff/0` for -your new `driver=usbhid-ups` section. +your new driver section in `ups.conf`. + +NOTE: As detailed in link:config-notes.txt[], the "natively USB" drivers +(including `usbhid-ups` and `nutdrv_qx`) match the device by ID and/or +strings it reports, and so effectively require but ignore the `port` +option -- so it is commonly configured as `port=auto`. +Drivers used for SHUT or serial protocols do need the device path. For some serial-to-USB converter chips however it was noted that while the device driver is attached, and the `/device/...` path is exposed @@ -178,10 +189,10 @@ in the `dmesg` output (saved to `/var/adm/messages`) the `/dev/...` symlinks are not created. In this case you can pass the low-level name of the character-device node as the "port" option, e.g.: -```` +---- ./mge-shut -s 9px-ser -DDDDD -d2 -u root \ -x port=/devices/pci@0,0/pci103c,1309@1a,2/device@1:0 -```` +---- libusb version and binary ------------------------- @@ -198,23 +209,16 @@ as an UPS), especially when using USB hubs and chips where hardware vendors had cut a few corners too many, which were addressed in a newer rewrite of the library as libusb-1.0. -Subsequently as at least the illumos-based distributions evolved to +Subsequently just as at least the illumos-based distributions evolved to include the new library and certain patches for it, and the library itself matured, the NUT project also added an ability to build with -libusb-1.0 either directly or using its 0.1-compat API. - -Currently this is not part of the common codebase and thus tagged -releases, but is experimented in several competing GitHub branches -until one gets chosen as the best to integrate: - -* https://github.com/networkupstools/nut/issues/300 - Please port to libusb 1.0 #300 -* https://github.com/networkupstools/nut/tree/libusb-1.0 -* https://github.com/networkupstools/nut/tree/libusb-1.0+0.1 -* https://github.com/networkupstools/nut/tree/libusb-compat-1.0 +libusb-1.0 either directly or using its 0.1-compat API (available since +NUT 2.8.0 release). -If your "standard" build of NUT has problems connecting to your -USB UPS, consider building one of those branches using the recent -library available for your distribution. +If your "standard" build of NUT has problems connecting to your USB UPS +(libusb binary variant should be visible in driver debug messages), +consider re-building NUT from source with the other option using the +recent library build as available for your distribution. In this context, note the OpenIndiana libusb-1 package pull requests with code which was successfully used when developing this documentation: @@ -222,5 +226,6 @@ with code which was successfully used when developing this documentation: * https://github.com/OpenIndiana/oi-userland/pull/5382 * (TO CHECK) https://github.com/OpenIndiana/oi-userland/pull/5277 -Binaries from builds made in OpenIndiana using the recipe from PR #5382 +Binaries from builds made in OpenIndiana using the recipe from +https://github.com/OpenIndiana/oi-userland/pull/5382[PR #5382] above were successfully directly used on contemporary OmniOS CE as well. diff --git a/docs/support.txt b/docs/support.txt index 0cb07afce2..c7d6165bcb 100644 --- a/docs/support.txt +++ b/docs/support.txt @@ -65,12 +65,13 @@ In this case, be sure to include the following information: - OS name and version, - exact NUT version, -- NUT installation method: from source tarball, package or Subversion, +- NUT installation method: package, or a custom build from source tarball + or GitHub (which fork, branch, PR), - exact device name and related information (manufacturing date, web -pointers, ...), + pointers, ...), - complete problem description, with any relevant traces, like system -log excerpts, and driver debug output. You can obtain the latter using -the following command, running as root and after having stopped NUT: + log excerpts, and driver debug output. You can obtain the latter using + the following command, running as root and after having stopped NUT: /path/to/driver -DD -a diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 67650a9f99..25b35ddb45 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -53,7 +53,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 +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 @@ -309,12 +309,19 @@ 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 @@ -365,9 +372,8 @@ dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.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 \ - apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.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 + 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, diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c new file mode 100644 index 0000000000..cda2696848 --- /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 0000000000..023586b7c9 --- /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/apc-ats-mib.c b/drivers/apc-ats-mib.c index 3679a67641..a3bb344c27 100644 --- a/drivers/apc-ats-mib.c +++ b/drivers/apc-ats-mib.c @@ -24,7 +24,7 @@ #include "apc-ats-mib.h" -#define APC_ATS_MIB_VERSION "0.5" +#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" @@ -119,6 +119,11 @@ static info_lkp_t apc_ats_outletgroups_status_info[] = { /* 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 */ diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index 4482216db7..36afaf12c2 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -27,11 +27,12 @@ */ #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.97" +#define APC_HID_VERSION "APC HID 0.98" /* APC */ #define APC_VENDORID 0x051d @@ -127,8 +128,24 @@ static const char *apc_date_conversion_fun(double value) return buf; } +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, NULL } + { 0, NULL, apc_date_conversion_fun, apc_date_conversion_reverse } }; /* This was determined empirically from observing a BackUPS LS 500 */ @@ -317,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. */ @@ -499,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, @@ -507,5 +591,5 @@ subdriver_t apc_subdriver = { apc_format_model, apc_format_mfr, apc_format_serial, - fix_report_desc, + apc_fix_report_desc, }; diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index 8e0d290990..199c25762c 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -26,7 +26,7 @@ #include "apc-mib.h" -#define APCC_MIB_VERSION "1.5" +#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? */ @@ -327,6 +327,11 @@ static info_lkp_t apcc_transfer_reasons[] = { 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 }, diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 34a82a56ec..0a2f451382 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -536,7 +536,7 @@ static void protocol_verify(unsigned char cmd) if (found) return; - if (isprint(cmd)) + if (isprint((size_t)cmd)) upsdebugx(1, "protocol_verify: 0x%02x [%c] unrecognized", cmd, cmd); else diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 0122c924b3..bb82e191dc 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -111,7 +111,7 @@ static const char *prtchr(char x) static char info[32]; curr = (curr + 8) & 0x1F; - snprintf(info + curr, 8, isprint(x) ? "%c" : "0x%02x", x); + snprintf(info + curr, 8, isprint((size_t)x) ? "%c" : "0x%02x", x); return info + curr; } @@ -1712,7 +1712,7 @@ void upsdrv_shutdown(void) ups_status = APC_STAT_LB | APC_STAT_OB; } - if (testvar("advorder") && toupper(*getval("advorder")) != 'N') + if (testvar("advorder") && toupper((size_t)*getval("advorder")) != 'N') upsdrv_shutdown_advanced(); else upsdrv_shutdown_simple(); @@ -2040,10 +2040,10 @@ static int instcmd(const char *cmd, const char *ext) if (!ext || !*ext) return sdcmd_S(0); - if (toupper(*ext) == 'A') + if (toupper((size_t)*ext) == 'A') return sdcmd_AT(ext + 3); - if (toupper(*ext) == 'C') + if (toupper((size_t)*ext) == 'C') return sdcmd_CS(0); } diff --git a/drivers/baytech-mib.c b/drivers/baytech-mib.c index ad6aae5ff5..f83553302d 100644 --- a/drivers/baytech-mib.c +++ b/drivers/baytech-mib.c @@ -24,7 +24,7 @@ #include "baytech-mib.h" /* NOTE: last badly versioned release was "4032" but should be "X.Y[Z]"! */ -#define BAYTECH_MIB_VERSION "0.4033" +#define BAYTECH_MIB_VERSION "0.4034" /* Baytech MIB */ #define BAYTECH_OID_MIB ".1.3.6.1.4.1.4779" @@ -60,6 +60,12 @@ static info_lkp_t baytech_outlet_status_info[] = { /* 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 }, diff --git a/drivers/bcmxcp_usb.c b/drivers/bcmxcp_usb.c index 49cf8ade58..e219714ce0 100644 --- a/drivers/bcmxcp_usb.c +++ b/drivers/bcmxcp_usb.c @@ -499,6 +499,7 @@ usb_dev_handle *nutusb_open(const char *port) int ret = 0; upsdebugx(1, "entering nutusb_open()"); + warn_if_bad_usb_port_filename(device_path); /* Initialize Libusb */ #if WITH_LIBUSB_1_0 diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 0a46be4951..c508563c13 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -1196,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")) { @@ -1236,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")) { diff --git a/drivers/bestpower-mib.c b/drivers/bestpower-mib.c index fd3263ef8c..fc53f9d3c8 100644 --- a/drivers/bestpower-mib.c +++ b/drivers/bestpower-mib.c @@ -22,7 +22,7 @@ #include "bestpower-mib.h" -#define BESTPOWER_MIB_VERSION "0.3" +#define BESTPOWER_MIB_VERSION "0.4" #define BESTPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.2947.1.1.2.0" /* @@ -54,6 +54,12 @@ static info_lkp_t bestpower_power_status[] = { /* 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 }, diff --git a/drivers/bestups.c b/drivers/bestups.c index c0d56d7b48..4f63aa5146 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -436,6 +436,15 @@ void upsdrv_makevartable(void) 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 6068bb4839..6e06b4236f 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -696,6 +696,15 @@ 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 = -1; diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index 78e271bf6d..fc3f78bc6b 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -584,9 +584,10 @@ void upsdrv_initups(void) int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ 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"); @@ -684,7 +685,7 @@ void upsdrv_initups(void) * 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 = 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); } } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 81374358ad..127909c564 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -20,6 +20,7 @@ #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include #include @@ -143,13 +144,27 @@ static int parse_args(size_t numargs, char **arg) static int sstate_connect(void) { ssize_t ret; - int fd; + 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); diff --git a/drivers/clone.c b/drivers/clone.c index ab0824d9da..00a95b4c6d 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -22,6 +22,7 @@ #include "main.h" #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" #include #include @@ -159,13 +160,27 @@ static int parse_args(size_t numargs, char **arg) static int sstate_connect(void) { ssize_t ret; - int fd; + 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); diff --git a/drivers/compaq-mib.c b/drivers/compaq-mib.c index ee618485ec..f33e3a227b 100644 --- a/drivers/compaq-mib.c +++ b/drivers/compaq-mib.c @@ -30,7 +30,7 @@ #include "compaq-mib.h" -#define CPQPOWER_MIB_VERSION "1.65" +#define CPQPOWER_MIB_VERSION "1.66" #define DEFAULT_ONDELAY "30" #define DEFAULT_OFFDELAY "20" @@ -382,6 +382,12 @@ static info_lkp_t cpqpower_outlet_switchability_info[] = { /* 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 }, diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c index 06fc595bd3..71f9a5bcbe 100644 --- a/drivers/cps-hid.c +++ b/drivers/cps-hid.c @@ -25,6 +25,7 @@ #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" @@ -276,29 +277,6 @@ static int cps_claim(HIDDevice_t *hd) { * voltage limits as being more appropriate. */ -static HIDData_t *FindReport(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; -} - static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { HIDData_t *pData; @@ -315,12 +293,12 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { * To fix it Set both the input and output voltages to pre-defined settings. */ - if ((pData=FindReport(pDesc_arg, 16, (PAGE_POWER_DEVICE<<16)+USAGE_HIGHVOLTAGETRANSFER))) { + 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=FindReport(pDesc_arg, 18, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) { + 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", @@ -331,7 +309,7 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { 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=FindReport(pDesc_arg, 15, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) { + 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", diff --git a/drivers/cyberpower-mib.c b/drivers/cyberpower-mib.c index 551746f019..f43e788170 100644 --- a/drivers/cyberpower-mib.c +++ b/drivers/cyberpower-mib.c @@ -24,44 +24,66 @@ #include "cyberpower-mib.h" -#define CYBERPOWER_MIB_VERSION "0.4" +#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" /* 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" + { 1, "NULL" /* unknown */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 3, "OB" + { 2, "OL" /* onLine */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 4, "OL BOOST" + { 3, "OB" /* onBattery */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 5, "OFF" + { 4, "OL BOOST" /* onBoost */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 7, "OL" + { 5, "OFF" /* onSleep */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 1, "NULL" + { 6, "OFF" /* off */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif }, - { 6, "OFF" + { 7, "OL" /* rebooting */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 8, "OL" /* onECO */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 9, "OL BYPASS" /* onBypass */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 10, "OL TRIM" /* onBuck */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 11, "OL OVER" /* onOverload */ #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif @@ -139,6 +161,12 @@ static info_lkp_t cyberpower_battrepl_status[] = { /* 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 }, diff --git a/drivers/delta_ups-mib.c b/drivers/delta_ups-mib.c index 9e408b749f..81b7e833f0 100644 --- a/drivers/delta_ups-mib.c +++ b/drivers/delta_ups-mib.c @@ -25,7 +25,7 @@ #include "delta_ups-mib.h" -#define DELTA_UPS_MIB_VERSION "0.4" +#define DELTA_UPS_MIB_VERSION "0.5" #define DELTA_UPS_SYSOID ".1.3.6.1.4.1.2254.2.4" @@ -161,6 +161,11 @@ static snmp_info_t delta_ups_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 }, + /* 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" */ diff --git a/drivers/dstate.c b/drivers/dstate.c index 50e4e2283b..52b5129dc8 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -109,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) { @@ -213,8 +215,26 @@ static void send_to_all(const char *fmt, ...) ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + 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); } } } @@ -258,12 +278,51 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if (ret <= INT_MAX) upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); - ret = write(conn->fd, buf, strlen(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(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + 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 */ @@ -287,8 +346,15 @@ static void sock_connect(int sock) return; } - /* enable nonblocking I/O */ - if (!do_synchronous) { + /* 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); if (ret < 0) { @@ -305,6 +371,9 @@ static void sock_connect(int sock) return; } } + else { + upsdebugx(0, "%s: keeping default synchronous mode", __func__); + } conn = (conn_t *)xcalloc(1, sizeof(*conn)); conn->fd = fd; @@ -414,6 +483,9 @@ static void send_tracking(conn_t *conn, const char *id, int 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; } @@ -602,7 +674,7 @@ 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]; @@ -625,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 */ diff --git a/drivers/dstate.h b/drivers/dstate.h index 76a645de3c..94829dfcd3 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -50,7 +50,7 @@ typedef struct conn_s { * Defaults to nonblocking, for backward compatibility */ extern int do_synchronous; -void dstate_init(const char *prog, const char *devname); +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))); diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 857f5d5e23..c75cc1e479 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -34,10 +34,12 @@ #include #include #include +#include #include #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include "upsclient.h" #include "dummy-ups.h" @@ -54,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, -static 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 */ static PCONF_CTX_t *ctx = NULL; static time_t next_update = -1; +static struct stat datafile_stat; #define MAX_STRING_SIZE 128 @@ -79,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 */ @@ -89,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++ ) { @@ -113,6 +140,7 @@ void upsdrv_initinfo(void) dstate_dataok(); break; + case MODE_META: case MODE_REPEATER: /* Obtain the target name */ @@ -141,9 +169,35 @@ void upsdrv_initinfo(void) } /* FIXME: commands and settable variable! */ 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: fatalx(EXIT_FAILURE, "no suitable definition found!"); +#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; @@ -158,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) @@ -183,9 +274,35 @@ 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 } } @@ -220,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"); @@ -234,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); + } } } diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c index 36b1da0e00..486d764ef1 100644 --- a/drivers/eaton-ats16-nm2-mib.c +++ b/drivers/eaton-ats16-nm2-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define EATON_ATS16_NM2_MIB_VERSION "0.20" +#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" @@ -440,9 +440,47 @@ static info_lkp_t eaton_ats16_nm2_threshold_humidity_alarms_info[] = { } }; +static info_lkp_t eaton_ats16_nm2_ambient_drycontacts_info[] = { + { -1, "unknown" +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 1, "opened" +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 2, "closed" +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 3, "opened" /* openWithNotice */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 4, "closed" /* closedWithNotice */ +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + }, + { 0, NULL +#if WITH_SNMP_LKP_FUN + , NULL, NULL, NULL, NULL +#endif + } +}; + /* 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 */ @@ -527,6 +565,15 @@ static snmp_info_t eaton_ats16_nm2_mib[] = { { "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_nm2_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_nm2_ambient_drycontacts_info[0] }, /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ /* Warning: indexes start at '1' not '0'! */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index 9d5403fed8..8ffb0a66c8 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nmc-mib.h" -#define EATON_ATS16_NMC_MIB_VERSION "0.19" +#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" @@ -233,6 +233,10 @@ static info_lkp_t eaton_ats16_nmc_ambient_drycontacts_info[] = { /* 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 }, diff --git a/drivers/eaton-pdu-genesis2-mib.c b/drivers/eaton-pdu-genesis2-mib.c index 24e881f1cf..22ff01e1fe 100644 --- a/drivers/eaton-pdu-genesis2-mib.c +++ b/drivers/eaton-pdu-genesis2-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-genesis2-mib.h" -#define EATON_APHEL_GENESIS2_MIB_VERSION "0.51" +#define EATON_APHEL_GENESIS2_MIB_VERSION "0.52" /* APHEL-GENESIS-II-MIB (monitored ePDU) * ************************************* @@ -48,6 +48,12 @@ /* 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 }, diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index a386e11095..421045f4c8 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -36,15 +36,16 @@ #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 */ @@ -55,6 +56,9 @@ long marlin_device_count_fun(const char *daisy_dev_list) /* 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; } diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 4ad9a2383c..c4c7bce75b 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.59" +#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" @@ -368,7 +368,7 @@ static info_lkp_t marlin_ambient_drycontacts_info[] = { , NULL, NULL, NULL, NULL #endif }, - { 0, "open" + { 0, "opened" #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif @@ -632,7 +632,7 @@ static const char *marlin_outlet_group_phase_fun(void *raw_outlet_group_nb) snprintf(marlin_scratch_buf, sizeof(marlin_scratch_buf), "L%i", phases_nb); if (phases_nb < 1 || phases_nb > 3) upsdebugx(3, "WARNING: %s got %i phases which is an unexpected amount", - __func__, phases_nb); + __func__, phases_nb); return marlin_scratch_buf; } @@ -721,7 +721,7 @@ const char *su_temperature_read_fun(void *raw_snmp_value) { NUT_UNUSED_VARIABLE(raw_snmp_value); return "dummy"; } -#endif // WITH_SNMP_LKP_FUN_DUMMY +#endif /* WITH_SNMP_LKP_FUN_DUMMY */ static info_lkp_t eaton_sensor_temperature_unit_info[] = { { 0, "dummy" @@ -749,7 +749,7 @@ static info_lkp_t eaton_sensor_temperature_read_info[] = { } }; -#else // if not WITH_SNMP_LKP_FUN: +#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. @@ -777,7 +777,7 @@ static info_lkp_t eaton_sensor_temperature_unit_info[] = { } }; -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ /* Extracted from powerware-mib.c ; try to commonalize */ static info_lkp_t marlin_ambient_drycontacts_polarity_info[] = { @@ -824,7 +824,7 @@ static info_lkp_t marlin_ambient_drycontacts_state_info[] = { #if WITH_SNMP_LKP_FUN_DUMMY long marlin_device_count_fun(const char *daisy_dev_list) { return 1; } -#endif // WITH_SNMP_LKP_FUN_DUMMY +#endif /* WITH_SNMP_LKP_FUN_DUMMY */ static info_lkp_t marlin_device_count_info[] = { { 1, "dummy" @@ -839,34 +839,48 @@ static info_lkp_t marlin_device_count_info[] = { } }; -#else // if not WITH_SNMP_LKP_FUN: +#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 +#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.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.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", + { "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 */ @@ -882,23 +896,26 @@ static snmp_info_t eaton_marlin_mib[] = { * 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", + { "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", + { "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 */ }, + , 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.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 }, @@ -913,8 +930,9 @@ static snmp_info_t eaton_marlin_mib[] = { { "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 }, + { "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 @@ -940,7 +958,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* 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", + { "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 @@ -948,29 +967,38 @@ static snmp_info_t eaton_marlin_mib[] = { * 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] }, + { "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", + { "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] }, + 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] }, + 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", + { "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", + { "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", + { "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", + { "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: @@ -980,15 +1008,18 @@ static snmp_info_t eaton_marlin_mib[] = { * 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", + * => 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] }, + 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] }, + 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 }, @@ -1006,10 +1037,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1027,10 +1060,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1048,10 +1083,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1074,10 +1111,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1098,10 +1137,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1122,10 +1163,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1146,10 +1189,12 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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] }, + 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 }, @@ -1163,79 +1208,98 @@ static snmp_info_t eaton_marlin_mib[] = { ".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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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", + { "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, NULL }, */ + /* { "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, NULL }, + * 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 /*, NULL */ }, + 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, NULL }, +/* { "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 /*, 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 /*, NULL */ }, + { "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] }, + 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] }, + 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, + &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, @@ -1258,11 +1322,14 @@ static snmp_info_t eaton_marlin_mib[] = { 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] }, + 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, + &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, @@ -1286,40 +1353,67 @@ static snmp_info_t eaton_marlin_mib[] = { /* 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] }, + 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] }, + 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 }, + { "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] }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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] }, + { "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, + { "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 @@ -1328,73 +1422,122 @@ static snmp_info_t eaton_marlin_mib[] = { }, { "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] }, + 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] }, + 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 }, + { "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 }, + { "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 }, + { "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 }, + { "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", 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] }, + 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] }, + 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 }, + { "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 }, + { "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 }, + { "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 }, + { "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 }, + { "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] }, + { "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] }, + { "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", + { "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, + { "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", + { "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] }, + "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] }, + "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", + { "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", + { "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", + { "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", + { "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", + { "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 @@ -1403,14 +1546,17 @@ static snmp_info_t eaton_marlin_mib[] = { /* 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 */ + /* 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] }, + 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. @@ -1423,9 +1569,10 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "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 }, - /* outletID: Outlet physical name, related to its number in the group - * ex: first outlet of the second group (B) is B1 */ + 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, @@ -1461,14 +1608,17 @@ static snmp_info_t eaton_marlin_mib[] = { { "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", + { "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] }, + 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] }, + 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 }, @@ -1481,16 +1631,20 @@ static snmp_info_t eaton_marlin_mib[] = { { "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", + { "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", + { "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] }, + 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] }, + 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 }, @@ -1503,19 +1657,23 @@ static snmp_info_t eaton_marlin_mib[] = { { "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", + { "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] }, + "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] }, + "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] }, + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_type_info[0] }, /* TODO: handle statistics * outletWh.0.1 @@ -1523,7 +1681,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Outlet groups collection */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + { "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 */ @@ -1536,7 +1695,8 @@ static snmp_info_t eaton_marlin_mib[] = { * 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 */ + /* 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, @@ -1670,20 +1830,23 @@ static snmp_info_t eaton_marlin_mib[] = { * 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 }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, */ + 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", + { "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", + { "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", + { "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) @@ -1693,10 +1856,21 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "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 /*, NULL */ }, + 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 /*, NULL */ }, + 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 @@ -1704,18 +1878,10 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "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 /*, NULL */ }, + 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 /*, 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 }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Delays handling: * 0-n :Time in seconds until the group command is issued diff --git a/drivers/eaton-pdu-pulizzi-mib.c b/drivers/eaton-pdu-pulizzi-mib.c index b4a8976fd7..8c61ea4cb4 100644 --- a/drivers/eaton-pdu-pulizzi-mib.c +++ b/drivers/eaton-pdu-pulizzi-mib.c @@ -42,7 +42,7 @@ /* Pulizzi Switched ePDU */ -#define EATON_PULIZZI_SW_MIB_VERSION "0.4" +#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" @@ -91,6 +91,12 @@ static info_lkp_t pulizzi_sw_outlet_switchability_info[] = { /* 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 }, diff --git a/drivers/eaton-pdu-revelation-mib.c b/drivers/eaton-pdu-revelation-mib.c index 7c4e022048..744252e2eb 100644 --- a/drivers/eaton-pdu-revelation-mib.c +++ b/drivers/eaton-pdu-revelation-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-revelation-mib.h" -#define EATON_APHEL_REVELATION_MIB_VERSION "0.51" +#define EATON_APHEL_REVELATION_MIB_VERSION "0.52" /* APHEL PDU-MIB - Revelation MIB (Managed ePDU) * ********************************************* */ @@ -126,6 +126,12 @@ static info_lkp_t revelation_outlet_switchability_info[] = { /* 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 }, diff --git a/drivers/emerson-avocent-pdu-mib.c b/drivers/emerson-avocent-pdu-mib.c index 72459c1083..ae877fc82c 100644 --- a/drivers/emerson-avocent-pdu-mib.c +++ b/drivers/emerson-avocent-pdu-mib.c @@ -25,16 +25,16 @@ #include "emerson-avocent-pdu-mib.h" -#define EMERSON_AVOCENT_MIB_VERSION "1.2" +#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" +#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_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" @@ -42,7 +42,7 @@ #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" +#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" @@ -130,6 +130,12 @@ static info_lkp_t avocent_outlet_status_info[] = { }; 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 }, diff --git a/drivers/ever-hid.c b/drivers/ever-hid.c index 866fff96c3..9f6a674571 100644 --- a/drivers/ever-hid.c +++ b/drivers/ever-hid.c @@ -47,6 +47,7 @@ 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 } diff --git a/drivers/hidparser.c b/drivers/hidparser.c index 48fb1b0bf3..0d7b53f80e 100644 --- a/drivers/hidparser.c +++ b/drivers/hidparser.c @@ -442,6 +442,33 @@ HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Of 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. diff --git a/drivers/hidparser.h b/drivers/hidparser.h index d104b1138b..1a2a3a7045 100644 --- a/drivers/hidparser.h +++ b/drivers/hidparser.h @@ -63,6 +63,7 @@ HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t T 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 * -------------------------------------------------------------------------- */ diff --git a/drivers/hidtypes.h b/drivers/hidtypes.h index 213fe4613d..87d42df3c9 100644 --- a/drivers/hidtypes.h +++ b/drivers/hidtypes.h @@ -92,11 +92,185 @@ extern "C" { #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 -/* Usages */ -#define USAGE_HIGHVOLTAGETRANSFER 0x54 -#define USAGE_VOLTAGE 0x30 /* * HIDNode_t struct * diff --git a/drivers/hpe-pdu-mib.c b/drivers/hpe-pdu-mib.c index 3133b7aba7..8a24aae93f 100644 --- a/drivers/hpe-pdu-mib.c +++ b/drivers/hpe-pdu-mib.c @@ -24,7 +24,7 @@ #include "hpe-pdu-mib.h" #include "dstate.h" -#define HPE_EPDU_MIB_VERSION "0.32" +#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" @@ -560,6 +560,11 @@ static info_lkp_t hpe_pdu_outlet_group_phase_info[] = { /* 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 }, diff --git a/drivers/huawei-mib.c b/drivers/huawei-mib.c index 16aa8d494f..00c929c5db 100644 --- a/drivers/huawei-mib.c +++ b/drivers/huawei-mib.c @@ -21,7 +21,7 @@ #include "huawei-mib.h" -#define HUAWEI_MIB_VERSION "0.3" +#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" @@ -357,6 +357,11 @@ static snmp_info_t huawei_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, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/ietf-mib.c b/drivers/ietf-mib.c index 8763efecc5..9f99c18628 100644 --- a/drivers/ietf-mib.c +++ b/drivers/ietf-mib.c @@ -26,7 +26,7 @@ #include "ietf-mib.h" -#define IETF_MIB_VERSION "1.53" +#define IETF_MIB_VERSION "1.54" /* SNMP OIDs set */ #define IETF_OID_UPS_MIB "1.3.6.1.2.1.33.1." @@ -263,6 +263,12 @@ static info_lkp_t ietf_beeper_status_info[] = { /* 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 */ diff --git a/drivers/libhid.c b/drivers/libhid.c index 39fc22d982..cbc0d8e0ad 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -171,7 +171,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa } 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) ) +#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 @@ -183,6 +183,9 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #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 @@ -220,7 +223,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #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) ) +#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 @@ -281,7 +284,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t SetValue(pData, rbuf->data[id], Value); -#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) ) +#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 @@ -293,6 +296,9 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #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 @@ -324,7 +330,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #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) ) +#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 @@ -543,7 +549,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ { 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) ) +#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 @@ -555,6 +561,9 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #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 @@ -602,7 +611,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #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) ) +#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 @@ -685,7 +694,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) /* needs libusb-0.1.8 to work => use ifdef and autoconf */ 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) ) +#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 @@ -697,6 +706,9 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #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 @@ -733,7 +745,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #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) ) +#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 @@ -904,10 +916,19 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u for (token = strtok_r(buf, ".", &last); token != NULL; token = strtok_r(NULL, ".", &last)) { /* lookup tables first (to override defaults) */ - if ((usage = hid_lookup_usage(token, utab)) >= 0) + /* 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++] = (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" */ @@ -916,6 +937,9 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u 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; @@ -927,6 +951,9 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u { 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; diff --git a/drivers/libhid.h b/drivers/libhid.h index d422002b6a..23e34a1389 100644 --- a/drivers/libhid.h +++ b/drivers/libhid.h @@ -42,12 +42,14 @@ 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 "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 */ diff --git a/drivers/libshut.c b/drivers/libshut.c index 4376d588a4..817fda4642 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -334,7 +334,7 @@ static int shut_control_msg( /* Data portability */ /* realign packet data according to Endianess */ -#define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) +#define BYTESWAP(in) ((((uint16_t)in & 0x00FF) << 8) + (((uint16_t)in & 0xFF00) >> 8)) static void align_request(struct shut_ctrltransfer_s *ctrl) { #if (defined (WORDS_BIGENDIAN)) && (WORDS_BIGENDIAN) @@ -893,7 +893,7 @@ static unsigned char shut_checksum( unsigned char chk=0; for(i=0; idevices; 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); @@ -212,10 +216,30 @@ static int libusb_open(usb_dev_handle **udevp, /* 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, - usb_strerror()); + libusb_error); + + count_open_errors++; + if (strcasestr(libusb_error, "Access denied") + || strcasestr(libusb_error, "insufficient permissions") + ) { + count_open_EACCESS++; + } + continue; } @@ -239,26 +263,44 @@ static int libusb_open(usb_dev_handle **udevp, curDevice->bcdDevice = dev->descriptor.bcdDevice; if (dev->descriptor.iManufacturer) { - ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, - string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = xstrdup(string); + 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) { - ret = usb_get_string_simple(udev, dev->descriptor.iProduct, - string, sizeof(string)); - if (ret > 0) { - curDevice->Product = xstrdup(string); + 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) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, - string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = xstrdup(string); + 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__); } } @@ -307,7 +349,7 @@ static int libusb_open(usb_dev_handle **udevp, /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ - retries = 3; + retries = MAX_RETRY; while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { upsdebugx(2, "failed to claim USB device: %s", @@ -377,7 +419,7 @@ static int libusb_open(usb_dev_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = buf[7] | (buf[8] << 8); + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); } if (rdlen1 < -1) { @@ -407,7 +449,7 @@ static int libusb_open(usb_dev_handle **udevp, ) { p = (usb_ctrl_char *)&iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = p[7] | (p[8] << 8); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); break; } } @@ -506,6 +548,20 @@ static int libusb_open(usb_dev_handle **udevp, 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; } diff --git a/drivers/libusb1.c b/drivers/libusb1.c index dd07435c33..55c0b0332c 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -33,7 +33,7 @@ #include "nut_stdint.h" #define USB_DRIVER_NAME "USB communication driver (libusb 1.0)" -#define USB_DRIVER_VERSION "0.42" +#define USB_DRIVER_VERSION "0.43" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -45,6 +45,7 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 static void nut_libusb_close(libusb_device_handle *udev); @@ -138,10 +139,10 @@ static int nut_libusb_open(libusb_device_handle **udevp, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { -#if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) int retries; -#endif - int rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + /* 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; @@ -156,10 +157,12 @@ static int nut_libusb_open(libusb_device_handle **udevp, 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]; - int rdlen; + int32_t rdlen; /* libusb base init */ if (libusb_init(NULL) < 0) { @@ -196,6 +199,10 @@ static int nut_libusb_open(libusb_device_handle **udevp, 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; @@ -225,38 +232,56 @@ static int nut_libusb_open(libusb_device_handle **udevp, curDevice->bcdDevice = dev_desc.bcdDevice; if (dev_desc.iManufacturer) { - 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"); + 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) { - 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"); + 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) { - 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"); + 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__); } } @@ -341,7 +366,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, #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 = 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)); @@ -425,7 +450,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = buf[7] | (buf[8] << 8); + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); } if (rdlen1 < -1) { @@ -450,7 +475,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, 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 = p[7] | (p[8] << 8); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); break; } } @@ -571,6 +596,20 @@ static int nut_libusb_open(libusb_device_handle **udevp, 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; } diff --git a/drivers/liebert-hid.c b/drivers/liebert-hid.c index 1f05ebdd48..23851d8850 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,7 +27,7 @@ #include "liebert-hid.h" #include "usb-common.h" -#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.4" +#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.41" /* FIXME: experimental flag to be put in upsdrv_info */ /* Phoenixtec Power Co., Ltd */ @@ -79,7 +80,7 @@ static hid_info_t liebert_hid2nut[] = { #endif { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, - { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", 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 }, @@ -89,9 +90,17 @@ static hid_info_t liebert_hid2nut[] = { { "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 }, diff --git a/drivers/main.c b/drivers/main.c index a3f7026449..63d329b7c2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -4,6 +4,7 @@ 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 @@ -26,6 +27,11 @@ #include "dstate.h" #include "attribute.h" +#include +#include +#include +#include + /* data which may be useful to the drivers */ int upsfd = -1; char *device_path = NULL; @@ -37,21 +43,46 @@ int extrafd = -1; /* for ser_open */ int do_lock_port = 1; -/* for dstate->sock_connect, default to asynchronous */ -int do_synchronous = 0; +/* for dstate->sock_connect, default to effectively + * asynchronous (0) with fallback to synchronous (1) */ +int do_synchronous = -1; /* for detecting -a values that don't match anything */ static int upsname_found = 0; static vartab_t *vartab_h = NULL; -/* variables possibly set by the global part of ups.conf */ +/* 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; +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 */ @@ -112,14 +143,17 @@ static void help_msg(void) 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"); @@ -274,6 +308,11 @@ static int main_arg(char *var, char *val) { /* 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; dstate_setinfo("driver.flag.nolock", "enabled"); @@ -287,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 */ @@ -298,6 +337,39 @@ 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 */ @@ -307,6 +379,9 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; else do_synchronous=0; @@ -321,11 +396,29 @@ 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")) { int ipv = atoi(val); if (ipv > 0) { @@ -342,17 +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 */ } @@ -389,9 +519,26 @@ 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; } @@ -483,6 +630,7 @@ static void exit_cleanup(void) free(chroot_path); free(device_path); free(user); + free(group); if (pidfn) { unlink(pidfn); @@ -533,6 +681,9 @@ int main(int argc, char **argv) /* 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); @@ -546,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:s:kDd:hx:Lqr:u:Vi:")) != -1) { + while ((i = getopt(argc, argv, "+a:s:kFBDd:hx:Lqr:u:g:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; @@ -561,6 +712,12 @@ int main(int argc, char **argv) upsname = optarg; upsname_found = 1; break; + case 'F': + background_flag = 0; + break; + case 'B': + background_flag = 1; + break; case 'D': nut_debug_level++; break; @@ -591,8 +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 */ @@ -609,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; @@ -629,6 +829,22 @@ int main(int argc, char **argv) "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); @@ -638,13 +854,14 @@ int main(int argc, char **argv) become_user(new_uid); - /* Only switch to statepath if we're not powering off or just dumping data, for discovery */ - /* This avoid case where ie /var is umounted */ + /* 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(); @@ -741,15 +958,86 @@ int main(int argc, char **argv) /* now we can start servicing requests */ /* Only write pid if we're not just dumping data, for discovery */ - if (!dump_data) - dstate_init(progname, upsname); + 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", "%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":"no"); + (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) @@ -759,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) && (!dump_data) ) { + if (background_flag != 0) { background(); writepid(pidfn); /* PID changes when backgrounding */ } diff --git a/drivers/masterguard.c b/drivers/masterguard.c index 89609ba2e7..46364f6f50 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 @@ -562,6 +567,15 @@ void upsdrv_initups(void) int fail = 0; int good = 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); diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index d30f7412bf..ead2ecc69b 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -38,7 +38,7 @@ #include "mge-hid.h" #include "nut_float.h" -#define MGE_HID_VERSION "MGE HID 1.45" +#define MGE_HID_VERSION "MGE HID 1.46" /* (prev. MGE Office Protection Systems, prev. MGE UPS SYSTEMS) */ /* Eaton */ @@ -56,6 +56,8 @@ /* 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 @@ -1567,6 +1569,21 @@ static int mge_claim(HIDDevice_t *hd) { * 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")) { @@ -1577,6 +1594,21 @@ static int mge_claim(HIDDevice_t *hd) { } 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: diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index df8af1485a..e2d3c28d64 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -374,6 +374,11 @@ static info_lkp_t mge_ambient_drycontacts_info[] = { /* 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, "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 }, diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 19c36616e5..86d272f023 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -60,6 +60,7 @@ #include "main.h" #include "serial.h" #include "mge-utalk.h" +#include "nut_stdint.h" /* --------------------------------------------------------------- */ /* Define "technical" constants */ @@ -908,7 +909,7 @@ static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) /* 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); @@ -922,7 +923,7 @@ static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) /* send terminating string */ for (p = MGE_COMMAND_ENDCHAR; *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); diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 487f78cb1e..4177b693d6 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.32" +#define MGE_XML_VERSION "MGEXML/0.36" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -577,7 +577,7 @@ static const char *mge_ambient_info(const char *arg_val) } } -static const char *mge_drycontact_info(const char *val) +static const char *mge_drycontact_info(const char *arg_val) { /* these values should theoretically be obtained through * Environment.Input[1].State[x].Description @@ -585,7 +585,7 @@ static const char *mge_drycontact_info(const char *val) * open * closed */ - switch (atoi(val)) + switch (atoi(arg_val)) { case 0: return "opened"; @@ -596,8 +596,6 @@ static const char *mge_drycontact_info(const char *val) } } - - static const char *mge_timer_shutdown(const char *delay_before_shutoff) { if (atoi(delay_before_shutoff) > -1 ) { diff --git a/drivers/netvision-mib.c b/drivers/netvision-mib.c index e49057f4d3..89699000df 100644 --- a/drivers/netvision-mib.c +++ b/drivers/netvision-mib.c @@ -25,7 +25,7 @@ #include "netvision-mib.h" -#define NETVISION_MIB_VERSION "0.43" +#define NETVISION_MIB_VERSION "0.44" #define NETVISION_SYSOID ".1.3.6.1.4.1.4555.1.1.1" @@ -191,6 +191,12 @@ static info_lkp_t netvision_output_info[] = { /* 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, diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index b354fef705..1129abb675 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -1566,6 +1566,14 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) 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; @@ -1661,7 +1669,7 @@ static int ablerex_command(const char *cmd, char *buf, size_t buflen) __func__, buflen); buflen = (INT_MAX - 1); } - + int retry; for (retry = 0; retry < 3; retry++) { @@ -2936,6 +2944,8 @@ void upsdrv_initups(void) /* 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 */ @@ -3057,7 +3067,7 @@ void upsdrv_initups(void) ret = usb_get_string(udev, 0, 0, (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); if (ret >= 4) { - langid = tbuf[2] | (tbuf[3] << 8); + langid = ((uint8_t)tbuf[2]) | (((uint8_t)tbuf[3]) << 8); upsdebugx(1, "First supported language ID: 0x%x " "(please report to the NUT maintainer!)", @@ -3754,6 +3764,46 @@ static bool_t qx_ups_walk(walkmode_t mode) } + 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); diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index aa52db3d8a..776a51e86e 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -4,7 +4,7 @@ * Copyright (C) * 2005-2006 Olli Savia * 2005-2006 Niels Baggesen - * 2015-2021 Eaton (author: Arnaud Quette ) + * 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 @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "1.01" +#define PW_MIB_VERSION "0.104" /* TODO: more sysOID and MIBs support: * @@ -454,6 +454,34 @@ static info_lkp_t pw_topology_info[] = { #endif } }; +#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[] = { @@ -911,9 +939,129 @@ static info_lkp_t pw_threshold_humidity_alarms_info[] = { } }; +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 */ @@ -950,7 +1098,7 @@ static snmp_info_t pw_mib[] = { /* 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" */ - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", + { "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 */ diff --git a/drivers/raritan-pdu-mib.c b/drivers/raritan-pdu-mib.c index 210619b20b..af736ce279 100644 --- a/drivers/raritan-pdu-mib.c +++ b/drivers/raritan-pdu-mib.c @@ -25,7 +25,7 @@ #include "raritan-pdu-mib.h" -#define RARITAN_MIB_VERSION "0.7" +#define RARITAN_MIB_VERSION "0.8" /* Raritan MIB * this one uses the same MIB as Eaton Revelation, @@ -68,6 +68,12 @@ static info_lkp_t raritan_pdu_outlet_status_info[] = { /* 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 }, diff --git a/drivers/raritan-px2-mib.c b/drivers/raritan-px2-mib.c index af8966255a..0b0c82be9b 100644 --- a/drivers/raritan-px2-mib.c +++ b/drivers/raritan-px2-mib.c @@ -23,7 +23,7 @@ #include "raritan-px2-mib.h" -#define RARITAN_PX2_MIB_VERSION "0.3" +#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" @@ -179,6 +179,11 @@ static info_lkp_t raritanpx2_outlet_switchability_info[] = { /* 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 }, diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index f052225c74..f8da32e3de 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -571,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 index 69bfe8db1d..8671225304 100644 --- a/drivers/riello.c +++ b/drivers/riello.c @@ -645,42 +645,42 @@ void riello_parse_re(uint8_t* buffer, TRielloData* data) pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout1W = pom_word; + 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_word; + 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_word; + 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_word; + 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_word; + 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_word; + data->Pout3VA = pom_long; } void riello_parse_rc(uint8_t* buffer, TRielloData* data) diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index d6f27e2be2..417ab89b0e 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -95,9 +95,9 @@ static int cypress_setfeatures() /* 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); + 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"); @@ -197,7 +197,7 @@ static int Get_USB_Packet(uint8_t *buffer) } /* copy to buffer */ - size = inBuf[0] & 0x07; + size = (unsigned char)(inBuf[0]) & 0x07; if (size) memcpy(buffer, &inBuf[1], size); @@ -846,6 +846,8 @@ void upsdrv_initups(void) 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"); diff --git a/drivers/salicru-hid.c b/drivers/salicru-hid.c index c93f52a845..b49b0990e5 100644 --- a/drivers/salicru-hid.c +++ b/drivers/salicru-hid.c @@ -86,7 +86,7 @@ static hid_info_t salicru_hid2nut[] = { { "experimental.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, #endif /* DEBUG */ -/* A few more unknow fields +/* 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 @@ -204,6 +204,25 @@ static hid_info_t salicru_hid2nut[] = { { "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 } }; diff --git a/drivers/serial.c b/drivers/serial.c index 693f15d8c4..60af06f37d 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -485,7 +485,7 @@ ssize_t 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); diff --git a/drivers/snmp-ups-helpers.c b/drivers/snmp-ups-helpers.c index fcf231fce2..029864d86e 100644 --- a/drivers/snmp-ups-helpers.c +++ b/drivers/snmp-ups-helpers.c @@ -93,7 +93,6 @@ info_lkp_t su_convert_to_iso_date_info[] = { } }; - /* Process temperature value according to 'temperature_unit' */ const char *su_temperature_read_fun(void *raw_snmp_value) { diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 7cf004c406..157dfcd71a 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -6,7 +6,7 @@ * Copyright (C) * 2002 - 2014 Arnaud Quette * 2015 - 2021 Eaton (author: Arnaud Quette ) - * 2016 - 2021 Eaton (author: Jim Klimov ) + * 2016 - 2022 Eaton (author: Jim Klimov ) * 2016 Eaton (author: Carlos Dominguez ) * 2002 - 2006 Dmitry Frolov * J.W. Hoogervorst @@ -170,10 +170,23 @@ static int semistatic_countdown = 0; static int quirk_symmetra_threephase = 0; -/* Number of device(s): standard is "1", but daisychain means more than 1 */ +/* 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; -static int current_device_number = 0; /* global var to handle daisychain iterations - changed by loops in snmp_ups_walk() and su_addcmd() */ -static bool_t daisychain_enabled = FALSE; /* global var to handle daisychain iterations */ +/* 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 */ @@ -189,7 +202,7 @@ static const char *mibvers; #else # define DRIVER_NAME "Generic SNMP UPS driver" #endif /* WITH_DMFMIB */ -#define DRIVER_VERSION "1.18" +#define DRIVER_VERSION "1.21" /* 42ity */ /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -214,7 +227,7 @@ static time_t lastpoll = 0; #define COMM_UNKNOWN 0 #define COMM_OK 1 #define COMM_LOST 2 -int comm_status = COMM_UNKNOWN; +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 */ @@ -275,10 +288,8 @@ void upsdrv_initinfo(void) && !(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); @@ -324,10 +335,12 @@ void upsdrv_updateinfo(void) /* update all dynamic info fields */ if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { + upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); comm_status = COMM_OK; } else { + upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); comm_status = COMM_LOST; } @@ -814,18 +827,46 @@ void upsdrv_initups(void) dstate_addcmd("shutdown.stayoff"); } - /* Publish sysContact and sysLocation for all subdrivers */ - /* sysContact.0 */ - if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) - dstate_setinfo("device.contact", "%s", model); - else - upsdebugx(2, "Can't get and publish sysContact for device.contact"); + /* 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) */ - /* sysLocation.0 */ - if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) - dstate_setinfo("device.location", "%s", model); - else - upsdebugx(2, "Can't get and publish sysLocation for device.location"); + 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(); @@ -1294,7 +1335,7 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l 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(*cp) || isspace(*cp))) { + if (!(isprint((size_t)*cp) || isspace((size_t)*cp))) { hex = 1; } } @@ -1648,7 +1689,8 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) 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? */ + * 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:""); @@ -1657,25 +1699,67 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) /* 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 + 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 + 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); @@ -2276,7 +2360,8 @@ static bool_t is_multiple_template(const char *OID_template) * 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"); + upsdebugx(1, "%s(%s)", __func__, + info_template ? info_template->info_type : "n/a"); /* sanity check */ if (info_template == NULL) @@ -2284,6 +2369,8 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne 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 (new_instance->info_type) @@ -2293,8 +2380,10 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne if (new_instance->OID) memset((char *)new_instance->OID, 0, SU_INFOSIZE); } - else + 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... */ @@ -2795,10 +2884,12 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) { bool_t status = FALSE; - upsdebugx(1, "%s: %s (%s)", __func__, 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) { @@ -2809,6 +2900,7 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) } 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; } @@ -2855,12 +2947,16 @@ bool_t daisychain_init() 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 */ + /* First test if we have a generic lookup function + * FIXME: Check if the field type is a string? + */ if ( (su_info_p->oid2info != NULL) && (su_info_p->oid2info->fun_s2l != NULL) ) { char buf[1024]; upsdebugx(2, "%s: using generic string-to-long lookup function", __func__); @@ -2871,19 +2967,20 @@ bool_t daisychain_init() } if (devices_count == -1) { -#endif // WITH_SNMP_LKP_FUN +#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 (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 +#endif /* WITH_SNMP_LKP_FUN */ } /* Otherwise (template), use the guesstimation function to get * the number of devices present */ @@ -3161,8 +3258,13 @@ bool_t snmp_ups_walk(int mode) * for the whole (#0) virtual device, so it *seems* similar to unitary. */ - for (current_device_number = 0 ; current_device_number <= devices_count ; current_device_number++) + for (current_device_number = (daisychain_enabled == FALSE && devices_count == 1 ? 1 : 0) ; + current_device_number <= devices_count; current_device_number++) { + + upsdebugx(1, "%s: walking device %d", + __func__, current_device_number); + /* reinit the alarm buffer, before */ if (devices_count > 1) device_alarm_init(); @@ -3231,16 +3333,33 @@ bool_t snmp_ups_walk(int mode) } /* if(su_info_p->flags & SU_FLAG_FUNCTION) - otherwise fall through to static data */ #endif /* WITH_DMF_FUNCTIONS */ - /* FIXME: + /* NOTE: Effectively below we do this: * switch(current_device_number) { - * case 0: devtype = "daisychain whole" - * case 1: devtype = "daisychain master" - * default: devtype = "daisychain slave" + * 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 device %i (%s)", __func__, - current_device_number, - (current_device_number == 1)?"master":"slave"); /* FIXME: daisychain */ + 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, "%s: processing unitary device (%i)", + __func__, current_device_number); } /* Check if we are asked to stop (reactivity++) */ @@ -3263,7 +3382,7 @@ bool_t snmp_ups_walk(int mode) * 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) { + if (current_device_number == 0 && daisychain_enabled == TRUE) { upsdebugx(1, "Skipping daisychain device.0 for now..."); continue; } @@ -3412,14 +3531,22 @@ bool_t su_ups_get(snmp_info_t *su_info_p) 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, "%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) { + 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 @@ -3436,6 +3563,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) #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); @@ -3444,7 +3574,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* 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); + snprintf((char *)tmp_info_p->info_type, + SU_INFOSIZE, "%s", + su_info_p->info_type); } else { free_info(tmp_info_p); @@ -3458,9 +3590,47 @@ bool_t su_ups_get(snmp_info_t *su_info_p) 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) { @@ -3481,6 +3651,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) 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) { @@ -3498,6 +3671,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) * 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); @@ -3549,6 +3725,9 @@ 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) { @@ -3583,10 +3762,16 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* 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) { - status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); + 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") @@ -3604,6 +3789,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } } } 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) @@ -3635,11 +3823,18 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } 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; @@ -3679,21 +3874,62 @@ static int su_setOID(int mode, const char *varname, const char *val) memset(setOID, 0, SU_INFOSIZE); memset(template_count_var, 0, SU_BUFSIZE); - /* Check if it's a daisychain setting */ + /* 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)) { - /* Extract the device number */ - 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 (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; + } - if (daisychain_device_number > devices_count) - upsdebugx(2, "%s: item is out of bound (%i / %ld)", - __func__, daisychain_device_number, devices_count); + 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; @@ -3710,9 +3946,39 @@ static int su_setOID(int mode, const char *varname, const char *val) } /* Check if it is outlet / outlet.group, or standard variable */ - if (strncmp(tmp_varname, "outlet", 6)) + if (strncmp(tmp_varname, "outlet", 6)) { su_info_p = su_find_info(tmp_varname); - else { + /* 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; /* Point the outlet or outlet group number in the string */ const char *item_number_ptr = NULL; diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index a804de0220..e8a69d9a9c 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -31,7 +31,6 @@ */ /* TODO list: -- add syscontact/location (to all mib.h or centralized?) - complete shutdown - add enum values to OIDs. - optimize network flow by: @@ -126,6 +125,12 @@ #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 @@ -163,13 +168,17 @@ typedef int bool_t; * this macro to a specific value while building the codebase and see * what happens under different conditions ;) */ -# if WITH_DMFMIB +# 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; /* SNMP OID value */ @@ -185,7 +194,8 @@ typedef struct { * get away by serving fallback static mapping tables that get into * generated DMF, while the "nuf_s2l", "fun_s2l" and "nuf_vp2s" types * are added for completeness but are not handled and do not have - * real consumers in existing NUT codebase (static mib2nut tables). + * real consumers in 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. @@ -194,7 +204,7 @@ typedef struct { long (*nuf_s2l)(const char *nut_value); /* optional NUT to SNMP mapping function, converting a NUT string into SNMP numeric data */ long (*fun_s2l)(const char *snmp_value); /* optional SNMP to NUT mapping function, converting SNMP string data into a NUT number */ const char *(*nuf_vp2s)(void *nut_value); /* optional NUT to SNMP mapping function, converting a pointer to NUT value (e.g. numeric or string) into SNMP string data */ -#endif +#endif /* WITH_SNMP_LKP_FUN */ } info_lkp_t; /* Structure containing info about one item that can be requested @@ -255,8 +265,8 @@ typedef struct { #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) */ + * (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 @@ -299,11 +309,11 @@ typedef struct { * 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 specifier */ -#define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd specifier */ -#define SU_TYPE_DAISY(t) ((t)->flags & (11UL << 21)) /* Mask the 3 SU_TYPE_DAISY_* but not SU_DAISY */ +#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*/ +/* 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) */ diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c new file mode 100644 index 0000000000..fd1f865310 --- /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> 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)); @@ -1442,7 +1447,7 @@ void upsdrv_updateinfo(void) /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ 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_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. */ @@ -1561,6 +1566,8 @@ void upsdrv_initups(void) 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"); diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index a02a7c31b4..a33fcd1596 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -30,6 +30,7 @@ #include "common.h" #include "upsconf.h" #include "attribute.h" +#include "nut_stdint.h" typedef struct { char *upsname; @@ -62,6 +63,9 @@ 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; @@ -278,8 +282,8 @@ 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; @@ -293,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; @@ -349,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"); @@ -476,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; @@ -497,6 +559,10 @@ int main(int argc, char **argv) nut_debug_level++; break; + case 'd': + nut_debug_level_passthrough = 1; + break; + case 'h': default: help(prog); @@ -516,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/usb-common.c b/drivers/usb-common.c index 1dedfb0757..5f2dc168d1 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -462,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 f3465ad2bc..cc9e8ceeda 100644 --- a/drivers/usb-common.h +++ b/drivers/usb-common.h @@ -513,4 +513,9 @@ int is_usb_device_supported(usb_device_id_t *usb_device_id_list, 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 f08700d4db..dfe65a3e28 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1,7 +1,7 @@ /* usbhid-ups.c - Driver for USB and serial (MGE SHUT) HID UPS units * * Copyright (C) - * 2003-2012 Arnaud Quette + * 2003-2022 Arnaud Quette * 2005 John Stamp * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte @@ -28,7 +28,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.45" +#define DRIVER_VERSION "0.47" #include "main.h" #include "libhid.h" @@ -147,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); @@ -436,9 +443,23 @@ static const char *date_conversion_fun(double value) return buf; } -/* FIXME? Do we need an inverse "nuf()" here? */ +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, NULL } + { 0, NULL, date_conversion_fun, date_conversion_reverse } }; /* returns statically allocated string - must not use it again before @@ -557,26 +578,30 @@ 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); - upsdebugx(3, "%s: using Path '%s'", __func__, hidups_item->hidpath); /* Check for fallback if not found */ if (hidups_item == NULL) { - upsdebugx(3, "%s: cmdname '%s' not found; checking for alternatives", + upsdebugx(3, "%s: cmdname '%s' not found; " + "checking for alternatives", __func__, cmdname); if (!strcasecmp(cmdname, "load.on")) { @@ -590,7 +615,8 @@ 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 */ + /* Ensure "ups.start.auto" is set to "yes", + * if supported */ if (dstate_getinfo("ups.start.auto")) { setvar("ups.start.auto", "yes"); } @@ -633,7 +659,10 @@ int instcmd(const char *cmdname, const char *extradata) return STAT_INSTCMD_INVALID; } - upsdebugx(3, "%s: using Path '%s'", __func__, hidups_item->hidpath); + 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)) { @@ -775,6 +804,9 @@ void upsdrv_makevartable(void) 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 */ nut_usb_addvars(); @@ -985,6 +1017,7 @@ void upsdrv_initups(void) char *regex_array[7]; upsdebugx(1, "upsdrv_initups (non-SHUT)..."); + warn_if_bad_usb_port_filename(device_path); subdriver_matcher = &subdriver_matcher_struct; @@ -1048,6 +1081,12 @@ void upsdrv_initups(void) if (testvar("interruptonly")) { interrupt_only = 1; } + + /* Activate Cyberpower tweaks */ + if (testvar("onlinedischarge")) { + onlinedischarge = 1; + } + val = getval("interruptsize"); if (val) { int ipv = atoi(val); @@ -1530,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) { @@ -1599,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))) { diff --git a/drivers/xppc-mib.c b/drivers/xppc-mib.c index f386ba5495..84a29f4245 100644 --- a/drivers/xppc-mib.c +++ b/drivers/xppc-mib.c @@ -24,7 +24,7 @@ #include "xppc-mib.h" -#define XPPC_MIB_VERSION "0.3" +#define XPPC_MIB_VERSION "0.4" #define XPPC_SYSOID ".1.3.6.1.4.1.935" @@ -166,6 +166,12 @@ static snmp_info_t xppc_mib[] = { * { 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 }, diff --git a/include/common.h b/include/common.h index 67deaff905..ff48ecdc3e 100644 --- a/include/common.h +++ b/include/common.h @@ -103,6 +103,10 @@ 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); @@ -112,7 +116,17 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) /* Report maximum platform value for the pid_t */ pid_t get_max_pid_t(void); -/* open , get the pid, then send it */ +/* 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); @@ -130,6 +144,9 @@ 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, ...) diff --git a/include/str.h b/include/str.h index 89a030ddfb..ceef32091d 100644 --- a/include/str.h +++ b/include/str.h @@ -133,6 +133,10 @@ int str_to_double_strict(const char *string, double *number, const int base); * the caller can use and must free() later on */ char * str_concat(size_t count, ...); +/* 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* */ } diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index c6376f9928..bdf83c4eeb 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -350,6 +350,33 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" 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( @@ -938,13 +965,14 @@ 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], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([dnl #include #include diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 index bd753b34d7..a72fc05246 100644 --- a/m4/ax_check_compile_flag.m4 +++ b/m4/ax_check_compile_flag.m4 @@ -24,10 +24,13 @@ # 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 @@ -43,7 +46,12 @@ 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()])], - [AS_VAR_SET(CACHEVAR,[yes])], + [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, diff --git a/m4/ax_run_or_link_ifelse.m4 b/m4/ax_run_or_link_ifelse.m4 new file mode 100644 index 0000000000..0e36c7efa2 --- /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/nut_check_libgd.m4 b/m4/nut_check_libgd.m4 index 858ae021f4..b6f58efcb1 100644 --- a/m4/nut_check_libgd.m4 +++ b/m4/nut_check_libgd.m4 @@ -1,9 +1,9 @@ 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 @@ -117,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_libnetsnmp.m4 b/m4/nut_check_libnetsnmp.m4 index 836d09da9f..1f89b5af2c 100644 --- a/m4/nut_check_libnetsnmp.m4 +++ b/m4/nut_check_libnetsnmp.m4 @@ -164,6 +164,9 @@ oid * pProto = usmAES128PrivProtocol; #include #include oid * pProto = usmDESPrivProtocol; +#ifdef NETSNMP_DISABLE_DES +#error "NETSNMP_DISABLE_DES is defined" +#endif ], [] )], @@ -179,6 +182,9 @@ oid * pProto = usmDESPrivProtocol; #include #include oid * pProto = usmHMAC256SHA384AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -194,6 +200,9 @@ oid * pProto = usmHMAC256SHA384AuthProtocol; #include #include oid * pProto = usmHMAC384SHA512AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -209,6 +218,9 @@ oid * pProto = usmHMAC384SHA512AuthProtocol; #include #include oid * pProto = usmHMAC192SHA256AuthProtocol; +#ifndef HAVE_EVP_SHA224 +#error "HAVE_EVP_SHA224 is NOT defined" +#endif ], [] )], @@ -224,6 +236,9 @@ oid * pProto = usmHMAC192SHA256AuthProtocol; #include #include oid * pProto = usmAES192PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif ], [] )], @@ -239,6 +254,9 @@ oid * pProto = usmAES192PrivProtocol; #include #include oid * pProto = usmAES256PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif ], [] )], @@ -254,6 +272,9 @@ oid * pProto = usmAES256PrivProtocol; #include #include oid * pProto = usmHMACMD5AuthProtocol; +#ifdef NETSNMP_DISABLE_MD5 +#error "NETSNMP_DISABLE_MD5 is defined" +#endif ], [] )], diff --git a/m4/nut_check_python.m4 b/m4/nut_check_python.m4 index 99843d1f71..6357286874 100644 --- a/m4/nut_check_python.m4 +++ b/m4/nut_check_python.m4 @@ -50,7 +50,7 @@ AC_DEFUN([NUT_CHECK_PYTHON2], PYTHON2="" AS_CASE([${nut_with_python2}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python], [_python2_runtime])], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python2.7 python-2.7 python], [_python2_runtime])], [no], [PYTHON2="no"], [PYTHON2="${nut_with_python2}"] ) @@ -92,7 +92,7 @@ AC_DEFUN([NUT_CHECK_PYTHON3], PYTHON3="" AS_CASE([${nut_with_python3}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON3], [python3 python], [_python3_runtime])], + [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}"] ) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index 4204b3c079..8cdc972b95 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -61,6 +61,11 @@ AC_DEFUN([NUT_COMPILER_FAMILY], 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" @@ -73,7 +78,7 @@ dnl complain if they are forwarded unknown flags accepted by the front-end. AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], [CFLAGS="$CFLAGS ${COMPILERFLAG}" - AC_RUN_IFELSE([AC_LANG_PROGRAM([],[])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [], [CFLAGS="$SAVED_CFLAGS"]) ], [], [-Werror]) AC_LANG_POP([C]) @@ -81,7 +86,7 @@ dnl complain if they are forwarded unknown flags accepted by the front-end. AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], [CXXFLAGS="$CXXFLAGS ${COMPILERFLAG}" - AC_RUN_IFELSE([AC_LANG_PROGRAM([],[])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [], [CXXFLAGS="$SAVED_CXXFLAGS"]) ], [], [-Werror]) AC_LANG_POP([C++]) @@ -102,13 +107,11 @@ 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]) -dnl # Future: test for something else? -dnl m4_foreach_w([TESTCOMPILERFLAG], [ -dnl -Qunused-arguments -dnl -Wno-unknown-warning-option -dnl ], [ -dnl NUT_CHECK_COMPILE_FLAG([TESTCOMPILERFLAG]) -dnl ]) + 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 @@ -142,11 +145,24 @@ 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 # AS_IF([test "x$CLANGCC" = xyes -o "x$GCC" = xyes], -dnl # [CFLAGS="-isystem /usr/include -isystem /usr/local/include $CFLAGS"]) -dnl # AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], -dnl # [CXXFLAGS="-isystem /usr/include -isystem /usr/local/include $CXXFLAGS"]) +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 diff --git a/scripts/DMF/dmfsnmp/apc-ats-mib.dmf b/scripts/DMF/dmfsnmp/apc-ats-mib.dmf index 6cfdcf557a..02920054da 100644 --- a/scripts/DMF/dmfsnmp/apc-ats-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-ats-mib.dmf @@ -23,6 +23,9 @@ + + + @@ -52,6 +55,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/apc-mib.dmf b/scripts/DMF/dmfsnmp/apc-mib.dmf index 9f6cf29968..a71b037600 100644 --- a/scripts/DMF/dmfsnmp/apc-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-mib.dmf @@ -56,6 +56,9 @@ + + + @@ -182,6 +185,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/baytech-mib.dmf b/scripts/DMF/dmfsnmp/baytech-mib.dmf index 3fe64450cd..92afb2c5d5 100644 --- a/scripts/DMF/dmfsnmp/baytech-mib.dmf +++ b/scripts/DMF/dmfsnmp/baytech-mib.dmf @@ -10,6 +10,9 @@ + + + @@ -34,6 +37,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/bestpower-mib.dmf b/scripts/DMF/dmfsnmp/bestpower-mib.dmf index 933b6b3db4..d7730551b6 100644 --- a/scripts/DMF/dmfsnmp/bestpower-mib.dmf +++ b/scripts/DMF/dmfsnmp/bestpower-mib.dmf @@ -8,6 +8,9 @@ + + + @@ -21,6 +24,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/compaq-mib.dmf b/scripts/DMF/dmfsnmp/compaq-mib.dmf index e70d320fc7..2a0acfd2a5 100644 --- a/scripts/DMF/dmfsnmp/compaq-mib.dmf +++ b/scripts/DMF/dmfsnmp/compaq-mib.dmf @@ -54,6 +54,9 @@ + + + @@ -129,6 +132,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf index 72105a2395..2294862899 100644 --- a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf +++ b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf @@ -27,6 +27,9 @@ + + + @@ -65,6 +68,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf index ea9f592c07..08c9a387f1 100644 --- a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf +++ b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf @@ -20,6 +20,9 @@ + + + @@ -37,6 +40,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf index 35dc7188db..c0f7032f6a 100644 --- a/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf @@ -79,6 +79,9 @@ + + + @@ -142,6 +145,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf index 750afcc3ac..2f29a11a62 100644 --- a/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf @@ -47,6 +47,9 @@ + + + @@ -82,6 +85,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf index 66f6d26ac4..7a46b9b80d 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf @@ -4,6 +4,9 @@ + + + @@ -22,6 +25,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf index 5b5e415fde..3c39737a2b 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf @@ -11,7 +11,7 @@ - + @@ -145,6 +145,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf index b1ebff4b12..b028af3ed3 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf @@ -12,6 +12,9 @@ + + + @@ -39,7 +42,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf index 85ff486795..16be1dd55a 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf @@ -16,6 +16,9 @@ + + + @@ -56,6 +59,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf b/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf index 197bf126ea..65812e4d05 100644 --- a/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf @@ -8,6 +8,9 @@ + + + @@ -38,6 +41,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf b/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf index 50c8ed6874..d17c9daba6 100644 --- a/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf @@ -116,6 +116,9 @@ + + + @@ -277,6 +280,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/huawei-mib.dmf b/scripts/DMF/dmfsnmp/huawei-mib.dmf index b615b388b9..b553030991 100644 --- a/scripts/DMF/dmfsnmp/huawei-mib.dmf +++ b/scripts/DMF/dmfsnmp/huawei-mib.dmf @@ -62,6 +62,9 @@ + + + @@ -114,6 +117,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/ietf-mib.dmf b/scripts/DMF/dmfsnmp/ietf-mib.dmf index 97f50d374b..3b8129ff8c 100644 --- a/scripts/DMF/dmfsnmp/ietf-mib.dmf +++ b/scripts/DMF/dmfsnmp/ietf-mib.dmf @@ -46,6 +46,9 @@ + + + @@ -133,7 +136,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/mge-mib.dmf b/scripts/DMF/dmfsnmp/mge-mib.dmf index 1f9e525c42..55a56bed28 100644 --- a/scripts/DMF/dmfsnmp/mge-mib.dmf +++ b/scripts/DMF/dmfsnmp/mge-mib.dmf @@ -72,6 +72,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/netvision-mib.dmf b/scripts/DMF/dmfsnmp/netvision-mib.dmf index 85df18d3e4..ead26b5704 100644 --- a/scripts/DMF/dmfsnmp/netvision-mib.dmf +++ b/scripts/DMF/dmfsnmp/netvision-mib.dmf @@ -26,6 +26,9 @@ + + + @@ -72,6 +75,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/powerware-mib.dmf b/scripts/DMF/dmfsnmp/powerware-mib.dmf index d5fc6286b8..2c73ec7994 100644 --- a/scripts/DMF/dmfsnmp/powerware-mib.dmf +++ b/scripts/DMF/dmfsnmp/powerware-mib.dmf @@ -168,6 +168,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf b/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf index a5416f7b62..131cda3221 100644 --- a/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf @@ -10,6 +10,9 @@ + + + @@ -43,6 +46,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf b/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf index e53e45c5ae..be462f51bf 100644 --- a/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf +++ b/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf @@ -35,6 +35,9 @@ + + + @@ -63,6 +66,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/xppc-mib.dmf b/scripts/DMF/dmfsnmp/xppc-mib.dmf index a03973eceb..2c1144f158 100644 --- a/scripts/DMF/dmfsnmp/xppc-mib.dmf +++ b/scripts/DMF/dmfsnmp/xppc-mib.dmf @@ -20,6 +20,9 @@ + + + @@ -31,6 +34,6 @@ - + diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index 8a50571230..313ace8c58 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -1,10 +1,20 @@ # Network UPS Tools: data/html +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 + EXTRA_DIST = README \ - app/gui-1.3.glade \ - app/NUT-Monitor.in \ app/nut-monitor.appdata.xml \ - app/nut-monitor.desktop \ app/icons/48x48/nut-monitor.png \ app/icons/64x64/nut-monitor.png \ app/icons/256x256/nut-monitor.png \ @@ -20,4 +30,11 @@ EXTRA_DIST = README \ module/PyNUT.py.in \ module/test_nutclient.py.in +# TODO: Make py2/py3-only builds, delivered preferred symlinks, etc. optional: +EXTRA_DIST += $(EXTRA_DIST_PY2GTK2) +EXTRA_DIST += $(EXTRA_DIST_PY3QT5) + MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + rm -rf *.pyc __pycache__ */*.pyc */__pycache__ */*/*.pyc */*/__pycache__ diff --git a/scripts/python/README b/scripts/python/README index 15932d9908..9a96e7131e 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/.gitignore b/scripts/python/app/.gitignore index 4472991c5e..d617cc83a2 100644 --- a/scripts/python/app/.gitignore +++ b/scripts/python/app/.gitignore @@ -1 +1,3 @@ -/NUT-Monitor +# Final scripts generated from .in templates: +/NUT-Monitor-py2gtk2 +/NUT-Monitor-py3qt5 diff --git a/scripts/python/app/NUT-Monitor b/scripts/python/app/NUT-Monitor new file mode 100755 index 0000000000..cab270f574 --- /dev/null +++ b/scripts/python/app/NUT-Monitor @@ -0,0 +1,67 @@ +#!/bin/sh + +# This script wraps selection of a NUT-Monitor implementation usable +# on the current system, using the historic name for users to have +# a single simple call. +# +# Copyright (C): +# 2022 Jim Klimov +# +# License: GPLv2+ + +# Currently we have issues using localization for py3qt5 variant, +# so if both seem functional, the wrapper would call py2gtk2: +PREFER_PY2=true + +# Detect which variant of NUT-Monitor we can run on the local system: +PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,'`" || PYTHON_PY2GTK2="" +PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,'`" || PYTHON_PY3QT5="" +SCRIPTDIR="`dirname "$0"`" && SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" || SCRIPTDIR="./" + +if [ -n "$PYTHON_PY2GTK2" ] \ +&& (command -v $PYTHON_PY2GTK2) >/dev/null 2>/dev/null \ +&& $PYTHON_PY2GTK2 -c "import re,glob,codecs,gtk,gtk.glade,gobject,ConfigParser" >/dev/null 2>/dev/null \ +; then + echo "PYTHON_PY2GTK2 is usable as: $PYTHON_PY2GTK2" >&2 +else + PYTHON_PY2GTK2="" +fi + +if [ -n "$PYTHON_PY3QT5" ] \ +&& (command -v $PYTHON_PY3QT5) >/dev/null 2>/dev/null \ +&& $PYTHON_PY3QT5 -c "import re,glob,codecs,PyQt5.uic,configparser" >/dev/null 2>/dev/null \ +; then + echo "PYTHON_PY3QT5 is usable as: $PYTHON_PY3QT5" >&2 +else + PYTHON_PY3QT5="" +fi + +for P in "$PYTHON_PY2GTK2" "$PYTHON_PY3QT5" ; do + [ -n "$P" ] || continue + + # If running from source tree... + if ! $P -c "import PyNUT" >/dev/null 2>/dev/null \ + && PYTHONPATH="${SCRIPTDIR}/../module" $P -c "import PyNUT" >/dev/null 2>/dev/null \ + ; then + PYTHONPATH="${SCRIPTDIR}/../module" + export PYTHONPATH + fi +done + +if [ -n "$PYTHON_PY2GTK2" ] && [ -n "$PYTHON_PY3QT5" ] ; then + if $PREFER_PY2 ; then + exec "$0"-py2gtk2 "$@" + else + exec "$0"-py3qt3 "$@" + fi +else + if [ -n "$PYTHON_PY2GTK2" ] ; then + exec "$0"-py2gtk2 "$@" + fi + if [ -n "$PYTHON_PY3QT3" ] ; then + exec "$0"-py3qt3 "$@" + fi +fi + +echo "ERROR: No usable Python interpreter version (with needed modules) was found" >&2 +exit 1 diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor-py2gtk2.in similarity index 99% rename from scripts/python/app/NUT-Monitor.in rename to scripts/python/app/NUT-Monitor-py2gtk2.in index c0357a9a9b..3a9dc1a843 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor-py2gtk2.in @@ -1,4 +1,4 @@ -#!@PYTHON@ +#!@PYTHON2@ # -*- coding: utf-8 -*- # 2009-12-27 David Goncalves - Version 1.2 @@ -76,7 +76,8 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() - self.__glade_file = '/usr/share/nut-monitor/gui-1.3.glade' + self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "ui/gui-1.3.glade" ) + #self.__glade_file = '/usr/share/nut-monitor/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") diff --git a/scripts/python/app/NUT-Monitor-py3qt5.in b/scripts/python/app/NUT-Monitor-py3qt5.in new file mode 100755 index 0000000000..cf1c3b6187 --- /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 b7acd299fa..163c4ae47b 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -1,7 +1,55 @@ +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 +------------ + +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 -David Goncalves +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/nut-monitor-py2gtk2.desktop b/scripts/python/app/nut-monitor-py2gtk2.desktop new file mode 100644 index 0000000000..130cd06fcb --- /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 0000000000..66fcd8632d --- /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.desktop b/scripts/python/app/nut-monitor.desktop index b4ccf398ae..e1e71a41b6 100644 --- a/scripts/python/app/nut-monitor.desktop +++ b/scripts/python/app/nut-monitor.desktop @@ -4,7 +4,7 @@ Name[fr]=Moniteur NUT Comment=Network UPS Tools GUI client Comment[fr]=Client graphique pour NUT (Network UPS Tools) Comment[it]=Client grafico per NUT (Network UPS Tools) -Categories=System;Monitor;HardwareSettings;Settings;GTK +Categories=System;Monitor;HardwareSettings;Settings Exec=NUT-Monitor Icon=nut-monitor Terminal=false diff --git a/scripts/python/app/ui/aboutdialog1.ui b/scripts/python/app/ui/aboutdialog1.ui new file mode 100644 index 0000000000..c6a62148d7 --- /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 0000000000..6cf652b1f7 --- /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 0000000000..d31542e206 --- /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 100% rename from scripts/python/app/gui-1.3.glade rename to scripts/python/app/ui/gui-1.3.glade diff --git a/scripts/python/app/gui-1.3.glade.h b/scripts/python/app/ui/gui-1.3.glade.h similarity index 100% rename from scripts/python/app/gui-1.3.glade.h rename to scripts/python/app/ui/gui-1.3.glade.h diff --git a/scripts/python/app/ui/window1.ui b/scripts/python/app/ui/window1.ui new file mode 100644 index 0000000000..1950464463 --- /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.in b/scripts/python/module/PyNUT.py.in index 3379239278..226c0aef84 100644 --- a/scripts/python/module/PyNUT.py.in +++ b/scripts/python/module/PyNUT.py.in @@ -272,17 +272,22 @@ Returns OK on success or raises an error Returns OK on success or raises an error -TODO: API change pending to replace MASTER with PRIMARY +NOTE: API changed since NUT 2.8.0 to replace MASTER with PRIMARY (and backwards-compatible alias handling) """ if self.__debug : - print( "[DEBUG] MASTER called..." ) + print( "[DEBUG] PRIMARY called..." ) - self.__srv_handler.write( ("MASTER %s\n" % ups).encode('ascii') ) + self.__srv_handler.write( ("PRIMARY %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) - if ( result != b"OK MASTER-GRANTED\n" ) : - raise PyNUTError( ( "Master level function are not available", "" ) ) + 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..." ) diff --git a/scripts/python/module/test_nutclient.py.in b/scripts/python/module/test_nutclient.py.in index fbfd0249e5..22e6adb3d5 100755 --- a/scripts/python/module/test_nutclient.py.in +++ b/scripts/python/module/test_nutclient.py.in @@ -5,12 +5,17 @@ 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( ) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 29f59d3d4b..a0979a326a 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -3,13 +3,13 @@ # an auxiliary script to produce a "stub" snmp-ups subdriver from # SNMP data from a real agent or from dump files # -# Version: 0.12-dmf +# Version: 0.13-dmf # # See also: docs/snmp-subdrivers.txt # # Copyright (C) # 2011 - 2012 Arnaud Quette -# 2015 - 2019 Arnaud Quette +# 2015 - 2022 Eaton (author: Arnaud Quette ) # 2011 Jim Klimov # # This program is free software; you can redistribute it and/or modify @@ -92,30 +92,30 @@ DMF=0 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"` +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` + 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 + DEVICE_SYSOID="$SYSOID" OID_COUNT=0 - while (test $OID_COUNT -eq 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 + 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 @@ -133,15 +133,18 @@ get_snmp_data() { } generate_C() { - # create file names - LDRIVER=`echo $DRIVER | tr A-Z a-z` - UDRIVER=`echo $DRIVER | tr a-z A-Z` + # 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" - - #FIXME: LDRIVER & UDRIVER => replace - by _ + # 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 @@ -176,7 +179,8 @@ generate_C() { EOF # generate source file - # create header + # 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 @@ -243,15 +247,26 @@ generate_C() { * { 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 + STR_OID="`echo "$line" | cut -d'.' -f1`" + echo "$line" | grep STRING > /dev/null if [ $? -eq 0 ]; then ST_FLAG_TYPE="ST_FLAG_STRING" SU_INFOSIZE="SU_INFOSIZE" @@ -260,19 +275,21 @@ generate_C() { SU_INFOSIZE="1" fi # get the matching numeric OID - NUM_OID="`sed -n ${LINENB}p ${NUMWALKFILE} | cut -d' ' -f1`" + 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} + done < "${STRWALKFILE}" >> "${CFILE}" - # append footer - cat >> "$CFILE" <<-EOF + # append footer (TABs not stripped): + cat >> "$CFILE" </dev/null 1> $NUMWALKFILE + 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` - echo "$STR_OID = $OID_VALUE" >> $STRWALKFILE - done < $NUMWALKFILE + 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 @@ -503,7 +523,7 @@ else 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 + if echo "$SYSOID" | egrep -q '[^0-9.]'; then echo "Please use only the numeric form, with dots and digits" SYSOID="" fi @@ -530,23 +550,23 @@ while [ -z "$DRIVER" ]; do 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 + 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} +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`" +NUM_OID_COUNT="`cat "$NUMWALKFILE" | wc -l`" +STR_OID_COUNT="`cat "$STRWALKFILE" | wc -l`" -echo "COUNT = $NUM_OID_COUNT / $NUM_OID_COUNT" +echo "SNMP OIDs extracted = $NUM_OID_COUNT / $NUM_OID_COUNT" generate_C if [ "$DMF" -eq 1 ]; then diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in index f5db0fbb43..76d65c103c 100644 --- a/scripts/systemd/nut-driver@.service.in +++ b/scripts/systemd/nut-driver@.service.in @@ -1,7 +1,20 @@ [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 @@ -29,6 +42,16 @@ 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 Restart=on-failure RestartSec=5min diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 74394f1af0..4ee216e160 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -18,9 +18,9 @@ PartOf=nut.target [Service] EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsmon +ExecStart=@SBINDIR@/upsmon -F +ExecReload=@SBINDIR@/upsmon -c reload PIDFile=@PIDPATH@/upsmon.pid -Type=forking [Install] WantedBy=nut.target diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index f9e4c6f88a..cdea9d9b2d 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -35,10 +35,12 @@ PartOf=nut.target LimitNOFILE=1048576 EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsd -ExecReload=@SBINDIR@/upsd -c reload +# 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 ExecStartPost=-/bin/grep -E 'Units|Max open files' /proc/${MAINPID}/limits -Type=forking +#Type=simple [Install] WantedBy=nut.target diff --git a/scripts/upower/95-upower-hid.hwdb b/scripts/upower/95-upower-hid.hwdb new file mode 100644 index 0000000000..e97372ce34 --- /dev/null +++ b/scripts/upower/95-upower-hid.hwdb @@ -0,0 +1,201 @@ +############################################################################################################## +# Uninterruptible Power Supplies with USB HID interfaces +# +# This file was automatically generated by NUT: +# https://github.com/networkupstools/nut/ +# +# To keep up to date, monitor upstream NUT +# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.hwdb +# or checkout the NUT repository and call 'tools/nut-usbinfo.pl' + +# Hewlett Packard +usb:v03F0p0001* +usb:v03F0p1F06* +usb:v03F0p1F08* +usb:v03F0p1F09* +usb:v03F0p1F0A* +usb:v03F0p1FE0* +usb:v03F0p1FE1* +usb:v03F0p1FE2* +usb:v03F0p1FE3* +usb:v03F0p1FE5* +usb:v03F0p1FE6* +usb:v03F0p1FE7* +usb:v03F0p1FE8* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Hewlett Packard + +# Eaton +usb:v0463p0001* +usb:v0463pFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Eaton + +# Dell +usb:v047CpFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Dell + +# ST Microelectronics +usb:v0483pA113* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=ST Microelectronics + +# IBM +usb:v04B3p0001* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=IBM + +# Minibox +usb:v04D8pD004* +usb:v04D8pD005* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Minibox + +# Belkin +usb:v050Dp0375* +usb:v050Dp0551* +usb:v050Dp0750* +usb:v050Dp0751* +usb:v050Dp0900* +usb:v050Dp0910* +usb:v050Dp0912* +usb:v050Dp0980* +usb:v050Dp0F51* +usb:v050Dp1100* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Belkin + +# APC +usb:v051Dp0000* +usb:v051Dp0002* +usb:v051Dp0003* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=APC + +# Powerware +usb:v0592p0004* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Powerware + +# Delta UPS +usb:v05DDp041B* +usb:v05DDpA011* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Delta UPS + +# Phoenixtec Power Co., Ltd +usb:v06DApFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Phoenixtec Power Co., Ltd + +# iDowell +usb:v075Dp0300* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=iDowell + +# Cyber Power Systems +usb:v0764p0005* +usb:v0764p0501* +usb:v0764p0601* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Cyber Power Systems + +# TrippLite +usb:v09AEp1003* +usb:v09AEp1007* +usb:v09AEp1008* +usb:v09AEp1009* +usb:v09AEp1010* +usb:v09AEp1330* +usb:v09AEp2005* +usb:v09AEp2007* +usb:v09AEp2008* +usb:v09AEp2009* +usb:v09AEp2010* +usb:v09AEp2011* +usb:v09AEp2012* +usb:v09AEp2013* +usb:v09AEp2014* +usb:v09AEp3008* +usb:v09AEp3009* +usb:v09AEp3010* +usb:v09AEp3011* +usb:v09AEp3012* +usb:v09AEp3013* +usb:v09AEp3014* +usb:v09AEp3015* +usb:v09AEp3016* +usb:v09AEp3024* +usb:v09AEp4001* +usb:v09AEp4002* +usb:v09AEp4003* +usb:v09AEp4004* +usb:v09AEp4005* +usb:v09AEp4006* +usb:v09AEp4007* +usb:v09AEp4008* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=TrippLite + +# PowerCOM +usb:v0D9Fp0001* +usb:v0D9Fp0004* +usb:v0D9Fp00A2* +usb:v0D9Fp00A3* +usb:v0D9Fp00A4* +usb:v0D9Fp00A5* +usb:v0D9Fp00A6* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=PowerCOM + +# Liebert +usb:v10AFp0001* +usb:v10AFp0004* +usb:v10AFp0008* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Liebert + +# Legrand +usb:v1CB0p0032* +usb:v1CB0p0038* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Legrand + +# Arduino +usb:v2341p0036* +usb:v2341p8036* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Arduino + +# Arduino +usb:v2A03p0036* +usb:v2A03p0040* +usb:v2A03p8036* +usb:v2A03p8040* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Arduino + +# AEG +usb:v2B2DpFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=AEG + +# Ever +usb:v2E51p0000* +usb:v2E51pFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Ever + +# Salicru +usb:v2E66p0201* +usb:v2E66p0202* +usb:v2E66p0203* +usb:v2E66p0300* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Salicru + +# Powervar +usb:v4234p0002* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Powervar diff --git a/scripts/upower/95-upower-hid.rules b/scripts/upower/95-upower-hid.rules index e709b7e15c..c6e2d06800 100644 --- a/scripts/upower/95-upower-hid.rules +++ b/scripts/upower/95-upower-hid.rules @@ -1,192 +1,2 @@ -############################################################################################################## -# Uninterruptible Power Supplies with USB HID interfaces -# -# This file was automatically generated by NUT: -# https://github.com/networkupstools/nut/ -# -# To keep up to date, monitor upstream NUT -# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.rules -# or checkout the NUT repository and call 'tools/nut-usbinfo.pl' - -# newer hiddev are part of the usbmisc class -SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev" -# only support USB, else ignore -SUBSYSTEM!="usb", GOTO="up_hid_end" - -# if usbraw device, ignore -LABEL="up_hid_chkdev" -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}=="0483", ENV{UPOWER_VENDOR}="ST Microelectronics" -ATTRS{idVendor}=="04b3", ENV{UPOWER_VENDOR}="IBM" -ATTRS{idVendor}=="04d8", ENV{UPOWER_VENDOR}="Minibox" -ATTRS{idVendor}=="050d", ENV{UPOWER_VENDOR}="Belkin" -ATTRS{idVendor}=="051d", ENV{UPOWER_VENDOR}="APC" -ATTRS{idVendor}=="0592", ENV{UPOWER_VENDOR}="Powerware" -ATTRS{idVendor}=="05dd", ENV{UPOWER_VENDOR}="Delta UPS" -ATTRS{idVendor}=="06da", ENV{UPOWER_VENDOR}="Phoenixtec Power Co., Ltd" -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" -ATTRS{idVendor}=="1cb0", ENV{UPOWER_VENDOR}="Legrand" -ATTRS{idVendor}=="2341", ENV{UPOWER_VENDOR}="Arduino" -ATTRS{idVendor}=="2A03", ENV{UPOWER_VENDOR}="Arduino" -ATTRS{idVendor}=="2b2d", ENV{UPOWER_VENDOR}="AEG" -ATTRS{idVendor}=="2e51", ENV{UPOWER_VENDOR}="Ever" -ATTRS{idVendor}=="2e66", ENV{UPOWER_VENDOR}="Salicru" -ATTRS{idVendor}=="4234", ENV{UPOWER_VENDOR}="Powervar" - -# Hewlett Packard -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -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" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe2", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe3", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe5", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe6", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe7", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe8", 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" - -# ST Microelectronics -ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a113", ENV{UPOWER_BATTERY_TYPE}="ups" - -# IBM -ATTRS{idVendor}=="04b3", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Minibox -ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="d004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="d005", 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}=="0f51", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{UPOWER_BATTERY_TYPE}="ups" - -# APC -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0000", ENV{UPOWER_BATTERY_TYPE}="ups" -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" - -# Delta UPS -ATTRS{idVendor}=="05dd", ATTRS{idProduct}=="041b", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="05dd", ATTRS{idProduct}=="a011", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Phoenixtec Power Co., Ltd -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}=="1330", 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}=="3016", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3024", 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}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -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" -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0008", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Legrand -ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0032", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0038", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Arduino -ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2341", ATTRS{idProduct}=="8036", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Arduino -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="0036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="0040", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="8036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="8040", ENV{UPOWER_BATTERY_TYPE}="ups" - -# AEG -ATTRS{idVendor}=="2b2d", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Ever -ATTRS{idVendor}=="2e51", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Salicru -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0201", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0202", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0203", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0300", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Powervar -ATTRS{idVendor}=="4234", ATTRS{idProduct}=="0002", 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/server/conf.c b/server/conf.c index 639b750220..3dd2696236 100644 --- a/server/conf.c +++ b/server/conf.c @@ -23,11 +23,20 @@ #include "sstate.h" #include "user.h" #include "netssl.h" +#include "nut_stdint.h" #include 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) { @@ -136,9 +145,21 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) 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")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { maxage = atoi(arg[1]); return 1; } @@ -150,7 +171,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* TRACKINGDELAY */ if (!strcmp(arg[0], "TRACKINGDELAY")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { tracking_delay = atoi(arg[1]); return 1; } @@ -162,7 +183,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* ALLOW_NO_DEVICE */ if (!strcmp(arg[0], "ALLOW_NO_DEVICE")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { allow_no_device = (atoi(arg[1]) != 0); /* non-zero arg is true here */ return 1; } @@ -175,7 +196,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { - if (isdigit(arg[1][0])) { + 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; @@ -217,7 +238,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION /* CERTREQUEST (0 | 1 | 2) */ if (!strcmp(arg[0], "CERTREQUEST")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { certrequest = atoi(arg[1]); return 1; } @@ -313,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", @@ -339,6 +367,15 @@ void load_upsdconf(int reloading) } + 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); } diff --git a/server/conf.h b/server/conf.h index 40341389fe..432b7f9c3e 100644 --- a/server/conf.h +++ b/server/conf.h @@ -51,6 +51,7 @@ void delete_acls(void); void delete_access(void); extern int num_ups; +extern int nut_debug_level_global; #ifdef __cplusplus /* *INDENT-OFF* */ diff --git a/server/netcmds.h b/server/netcmds.h index c8bb31aa13..6a5a632d01 100644 --- a/server/netcmds.h +++ b/server/netcmds.h @@ -50,6 +50,7 @@ static struct { } 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 }, @@ -61,9 +62,10 @@ static struct { { "LOGIN", net_login, FLAG_USER }, { "LOGOUT", net_logout, 0 }, - /* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + /* 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 }, diff --git a/server/netuser.c b/server/netuser.c index 2c3a9bcb49..6f4c677688 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -53,6 +53,8 @@ void net_login(nut_ctype_t *client, size_t 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; } @@ -83,34 +85,60 @@ void net_logout(nut_ctype_t *client, size_t numarg, const char **arg) client->last_heard = 0; } -/* MASTER */ -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. +/* NOTE: Protocol updated since NUT 2.8.0 to handle master/primary + * and API bumped, to rename/alias the routine. */ -void net_master(nut_ctype_t *client, size_t numarg, const char **arg) +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 */ diff --git a/server/netuser.h b/server/netuser.h index 069c1f6200..7532b3cce9 100644 --- a/server/netuser.h +++ b/server/netuser.h @@ -33,10 +33,14 @@ extern "C" { 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); -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + +/* 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); diff --git a/server/sockdebug.c b/server/sockdebug.c index 031e02cc36..f9fe4767c5 100644 --- a/server/sockdebug.c +++ b/server/sockdebug.c @@ -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); diff --git a/server/sstate.c b/server/sstate.c index f33df625ba..167bc09b2f 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -181,6 +181,8 @@ int sstate_connect(upstype_t *ups) 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); diff --git a/server/upsd.c b/server/upsd.c index 85249a3993..629915003a 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -453,17 +453,22 @@ int ups_available(const upstype_t *ups, nut_ctype_t *client) 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; } @@ -473,13 +478,18 @@ static void check_command(int cmdnum, nut_ctype_t *client, size_t 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 < 2) ? 0 : (numarg - 1), (numarg > 1) ? &arg[1] : NULL); } @@ -669,6 +679,9 @@ 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) { @@ -985,7 +998,11 @@ static void mainloop(void) /* 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; } @@ -1173,7 +1190,11 @@ static void help(const char *arg_progname) 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"); @@ -1245,10 +1266,11 @@ void check_perms(const char *fn) int main(int argc, char **argv) { - int i, cmd = 0, cmdret = 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]); @@ -1261,7 +1283,7 @@ 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 'p': case 'i': @@ -1299,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; @@ -1317,18 +1355,50 @@ int main(int argc, char **argv) } } + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + if (cmd) { - cmdret = sendsignalfn(pidfn, cmd); + 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 (sendsignalfn(pidfn, 0) == 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; @@ -1361,6 +1431,14 @@ int main(int argc, char **argv) /* 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 @@ -1414,11 +1492,17 @@ 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) */ diff --git a/tests/.gitignore b/tests/.gitignore index 40cf44ca81..23e0f10359 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,7 +1,9 @@ /cppunittest /cppunittest.log /cppunittest.trs -/nutlogtest +/cppnit +/cppnit.log +/cppnit.trs /test-suite.log /selftest-rw/* /nutlogtest diff --git a/tests/Makefile.am b/tests/Makefile.am index d0283191c3..023561a934 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,7 @@ # Network UPS Tools: tests +SUBDIRS = . NIT + all: $(TESTS) EXTRA_DIST = nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf @@ -12,6 +14,10 @@ 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 @@ -46,6 +52,8 @@ CPPUNITTESTSRC_NUTCONF = nutconf.cpp nutstream_ut.cpp nutconf_ut.cpp nutipc_ut.c # The test driver which orchestrates running those tests above CPPUNITTESTERSRC = cpputest.cpp +CPPCLIENTTESTSRC = cpputest-client.cpp + TESTS_CXX11 = cppunittest if HAVE_CXX11 @@ -55,15 +63,17 @@ if HAVE_CPPUNIT 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_LIBS) -cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la -cppunittest_LDADD += $(top_builddir)/clients/libnutclientstub.la +cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) # Currently nutconf and related codebase causes woes for static analysis @@ -73,6 +83,11 @@ cppunittest_SOURCES += $(CPPUNITTESTSRC_NUTCONF) cppunittest_LDADD += $(top_builddir)/common/libnutconf.la endif +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 \ @@ -80,16 +95,22 @@ $(top_builddir)/clients/libnutclientstub.la: dummy @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) else !HAVE_CPPUNIT -# Just redistribute test source into tarball +# Just redistribute test source into tarball if not building tests + +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) $(CPPUNITTESTSRC_NUTCONF) -EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) $(CPPUNITTESTSRC_NUTCONF) +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 +# Just redistribute test source into tarball if not building C++ at all + +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) $(CPPUNITTESTSRC_NUTCONF) -EXTRA_DIST += example.cpp cpputest.cpp +cppnit: + @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 endif !HAVE_CXX11 diff --git a/tests/NIT/.gitignore b/tests/NIT/.gitignore new file mode 100644 index 0000000000..77091adc00 --- /dev/null +++ b/tests/NIT/.gitignore @@ -0,0 +1,2 @@ +/tmp +/Makefile.in diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am new file mode 100644 index 0000000000..6ce64a9ec8 --- /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/README b/tests/NIT/README new file mode 100644 index 0000000000..30da730fdc --- /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 0000000000..f404cb9f9f --- /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 0000000000..9a6169a1df --- /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/getvaluetest.c b/tests/getvaluetest.c index 0723969f00..769df06ff2 100644 --- a/tests/getvaluetest.c +++ b/tests/getvaluetest.c @@ -10,6 +10,7 @@ * * 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 @@ -34,6 +35,7 @@ #include #include #include "hidtypes.h" +#include "usb-common.h" #include "common.h" void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue); @@ -125,6 +127,106 @@ static int RunBuiltInTests(char *argv[]) { 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); } diff --git a/tools/Makefile.am b/tools/Makefile.am index 8867f259fc..6179226eb3 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,7 +13,7 @@ SUBDIRS = . nut-scanner nutconf PYTHON = @PYTHON@ -EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh \ +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 # DMF recipe modification: diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh new file mode 100755 index 0000000000..0dcb4707e2 --- /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-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 56b41f9d66..c6ce9ce4bc 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -112,7 +112,7 @@ endif # object .so names would differ) # # libnutscan version information -libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 +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 diff --git a/tools/nut-usbinfo.pl b/tools/nut-usbinfo.pl index 63cd074484..9fd07a1bd2 100755 --- a/tools/nut-usbinfo.pl +++ b/tools/nut-usbinfo.pl @@ -52,12 +52,7 @@ my $output_devd="$TOP_BUILDDIR/scripts/devd/nut-usb.conf.in"; # UPower output file -my $outputUPower="$TOP_BUILDDIR/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 = "$TOP_BUILDDIR/tools/nut-scanner/nutscan-usb.h"; @@ -125,17 +120,8 @@ sub gen_usb_files 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.rules'."\n"; - print $outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n\n"; - print $outputUPower '# newer hiddev are part of the usbmisc class'."\n"; - print $outputUPower 'SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev"'."\n"; - print $outputUPower '# only support USB, else ignore'."\n"; - print $outputUPower 'SUBSYSTEM!="usb", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if usbraw device, ignore'."\n"; - print $outputUPower 'LABEL="up_hid_chkdev"'."\n"; - print $outputUPower 'KERNEL!="hiddev*", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if an interface, ignore'."\n"; - print $outputUPower 'ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end"'."\n\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 : $!"; @@ -183,7 +169,7 @@ sub gen_usb_files # UPower vendor header flag - $upowerMfrHeaderDone = 0; + my $upowerVendorHasDevices = 0; foreach my $productId (sort { lc $a cmp lc $b } keys %{$vendor{$vendorId}}) { @@ -212,33 +198,30 @@ sub gen_usb_files # 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"; } + + 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 "\n\t/* Terminating entry */\n\t{ 0, 0, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; }