diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..c6a2acb950 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,181 @@ +# Content modeled after settings tested with .travis.yml +# TODO: Implement a similar envvar-based matrix +# https://circleci.com/docs/2.0/env-vars/#circleci-environment-variable-descriptions +# TODO: save_cache installed brew dependencies? => seems not possible to reliably cache/restore outside user homedir +# TODO: Windows eventually? +# TODO: yaml-aliases to define steps once? => https://circleci.com/blog/decrease-your-build-times-by-running-jobs-in-parallel-with-workflows/ + +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/2.0/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/2.0/configuration-reference/#jobs +# https://circleci.com/docs/2.0/configuration-reference/#parameters-requires-version-21 +jobs: + osx-xcode: + parameters: + XCODE_VER: + type: string + default: "12.5.1" + CC: + type: string + default: "" # e.g. "clang" + CXX: + type: string + default: "" # e.g. "clang++" + CC_STDVER: + type: string + default: "" # e.g. "-std=gnu17" + CXX_STDVER: + type: string + default: "" # e.g. "-std=gnu++17" + BUILD_TYPE: + type: string + default: "default-all-errors" + CI_BUILDDIR: + type: string + default: "" # e.g. "obj" for out-of-tree build tests + BREW_MORE: + type: string + default: "" # e.g. "avahi" for all-driver tests + + environment: + CC: << parameters.CC >> + CXX: << parameters.CXX >> + CC_STDVER: << parameters.CC_STDVER >> + CXX_STDVER: << parameters.CXX_STDVER >> + BUILD_TYPE: << parameters.BUILD_TYPE >> + CI_BUILDDIR: << parameters.CI_BUILDDIR >> + BREW_MORE: << parameters.BREW_MORE >> + + # Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor + macos: + xcode: << parameters.XCODE_VER >> + + # Add steps to the job + # See: https://circleci.com/docs/2.0/configuration-reference/#steps + steps: + - checkout + +# - run: +# name: "check shell" +# command: /usr/bin/env bash --version || true; command -v bash || true + + # Note: MacOS default /bin/bash 3.x is too old for ci_build.sh + # Brew brings /usr/local/bin/bash 5.x as of this writing + # TODO: Are Binutils needed? + - run: + name: "homebrew" + command: |- + HOMEBREW_NO_AUTO_UPDATE=1; export HOMEBREW_NO_AUTO_UPDATE; + brew install ccache bash libtool pkg-config gd libusb neon net-snmp openssl $BREW_MORE #binutils + +# - run: +# name: "homebrew-libtool" +# command: |- +# #find /usr /opt /lib* -name '*ltdl*' -ls 2>/dev/null || true +# brew unlink libtool && brew link libtool +# #find /usr /opt /lib* -name '*ltdl*' -ls 2>/dev/null || true + + - restore_cache: + keys: + - ccache-{{ .Branch }}-{{ arch }}-{{ .Environment.CIRCLE_JOB }} + - ccache-master-{{ arch }}-{{ .Environment.CIRCLE_JOB }} + +# - run: +# name: "check shell" +# command: /usr/bin/env bash --version || true; command -v bash || true + + - run: + name: "ccache stats before build" + command: ccache -s || true + + # TODO: Move -Wno-poison-system-directories into configure.ac to set + # optionally just on detected cross-build attempts (*X*code after all) + # and verifying that the compiler supports it? + # TODO: Relocate or address -Wno-deprecated-declarations (reported for + # uses of sem_init() and sem_destroy() in nut-scanner.c) + # NOTE: CANBUILD_NIT_TESTS=yes to check if single-executor environments + # do not have a problem with it. + - run: + name: "ci_build" + command: |- + CI_CCACHE_SYMLINKDIR="/usr/local/opt/ccache/libexec" \ + CANBUILD_NIT_TESTS=yes \ + CFLAGS="$CC_STDVER -Wno-poison-system-directories -Wno-deprecated-declarations" \ + CXXFLAGS="$CXX_STDVER -Wno-poison-system-directories" \ + LDFLAGS="-L/usr/local/lib" \ + ./ci_build.sh + + - run: + name: "ccache stats after build" + command: ccache -s || true + + # NOTE: Detailed key name allows every scenario to only track its + # own ccache objects, which makes sense for different compilers + # and their command-line flags which make an object unique. + # However if we were to build many scenarios with overlapping + # settings (e.g. same C standards with same compilers), it could + # be beneficial to instead share the cache between jobs; more so + # while we are on free CircleCI tier and run them sequentially. + - save_cache: + paths: + - ~/.ccache + key: ccache-{{ .Branch }}-{{ arch }}-{{ .Environment.CIRCLE_JOB }} + +# Invoke jobs via workflows +# See: https://circleci.com/docs/2.0/configuration-reference/#workflows +workflows: + xcode-workflow: + jobs: + # Note: while "ccache" lists hordes of symlinks to gcc-XXX versions, + # in practice these toolkits are not installed (by default) + +### This scenario is a subset of fightwarn-all below (modulo C standard), +### so disabled to not waste time from free CircleCI allowance limit: +# - osx-xcode: +# name: "gnu17-clang-xcode12_5_1-default-all-errors" +# XCODE_VER: "12.5.1" +# CC: "clang" +# CXX: "clang++" +# CC_STDVER: "-std=gnu17" +# CXX_STDVER: "-std=gnu++17" + + - osx-xcode: + name: "gnu11-gcc-xcode12_5_1-out-of-tree" + CC: "gcc" + CXX: "g++" + CC_STDVER: "-std=gnu11" + CXX_STDVER: "-std=gnu++11" + # Try an out-of-tree build: + CI_BUILDDIR: "obj" + + - osx-xcode: + name: "c99-cxx11-gcc-xcode12_5_1-default-distcheck" + CC: "gcc" + CXX: "g++" + CC_STDVER: "-std=c99" + CXX_STDVER: "-std=c++11" + # Try usual and distchecked build: + BUILD_TYPE: "default" + + - osx-xcode: + name: "stdDefault-xcode12_5_1-fightwarn-all" + # Run "default-all-errors" with both compiler families, + # using their default C/C++ standard for current release: + BUILD_TYPE: "fightwarn-all" + +### This does not work due to missing dependencies built for MacOS in homebrew: +### TODO? Evaluate other packagers (MacPorts, fink...)? +# - osx-xcode: +# name: "c17-clang-xcode12_5_1-alldrv" +# XCODE_VER: "12.5.1" +# CC: "clang" +# CXX: "clang++" +# CC_STDVER: "-std=c17" +# CXX_STDVER: "-std=c++17" +# # Try all drivers, and a distcheck: +# BUILD_TYPE: "default-alldrv" +# BREW_MORE: "avahi powerman" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..fb640f2fd6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,82 @@ +# EditorConfig is awesome: https://EditorConfig.org +# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +max_line_length = 80 + +indent_style = tab +tab_width = 4 +#indent_style = space +#indent_size = 4 + +# Platform-dependent, except for certain interpreters +# whose sources must use LF, see .gitattributes +###end_of_line = lf + +#ij_formatter_enabled = false + +[.editorconfig] +trim_trailing_whitespace = false + +[*.{bat,cmd,ps1}] +end_of_line = crlf + +[*.{am,hwdb,service,target,path}{,.in}] +end_of_line = lf +line_comment = # + +[*.sh{,.in}] +end_of_line = lf +line_comment = # + +# Borrowed from https://github.com/armbian/build/blob/master/.editorconfig +shell_variant = bash +binary_next_line = false +switch_case_indent = true +space_redirects = true +keep_padding = false +function_next_line = false + +[*.{m4,ac}{,.in}] +end_of_line = lf +line_comment = dnl + +[*.{conf,sample}{,.in}] +max_line_length = 76 +line_comment = # + +[*.txt{,.in},*.adoc{,.in},AUTHORS,COPYING,INSTALL.nut,MAINTAINERS,NEWS,README,TODO,UPGRADING] +max_line_length = 76 +indent_style = space +indent_size = 4 + +# Assumes asciidoc comments: +block_comment_start = //////// +block_comment_end = //////// + +[*.{yaml,yml,json}{,.in}] +indent_style = space +indent_size = 4 + +################################################################ +# Primary concern: C/C++ style +# See also docs/developers.txt => Code Style chapter + +[*.{c,h,cpp}{,.in}] +spaces_around_operators = true +spaces_around_brackets = none + +# Plus one TAB: +continuation_indent_size = 1 + +indent_brace_style = K&R + +block_comment_start = /* +block_comment_end = */ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..b2943eda2f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,25 @@ +# Windows script files should be CR+LF always: +*.bat text eol=crlf + +# Unix/Linux script files should be LF always: +*.sh text eol=lf +*.m4 text eol=lf +*.ac text eol=lf +*.am text eol=lf +*.hwdb text eol=lf + +# Aspell claims issues finding `utf-8\r` sometimes (from heading line of +# the dictionary file), with messages like this: +# .cset" could not be opened for reading or does not exist.lib/aspell/utf-8 +# which tends to happen in mixed-OS development environments. Tracer shows it: +# read(3, "personal_ws-1.1 en 3225 utf-8\r\nA"..., 4096) = 4096 +# access("/usr/lib/aspell/utf-8\r.cset", F_OK) = -1 ENOENT (No such file or directory) +/docs/nut.dict text eol=lf + +# Some files are binary always: +*.png bin +*.ico bin + +# The rest are assumed text sources with platform-dependent EOL being okay, +# or we let Git guess otherwise: +* text=auto diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..a36f1ed44c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: networkupstools +open_collective: networkupstools diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 084ac806ce..3087cbeb6a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,6 +4,11 @@ as for which text documents to update. See also docs/developer-guide.txt for general points on NUT architecture and design. +* Please note that we require "Signed-Off-By" tags in each Git Commit + message, to conform to the common DCO (Developer Certificate of Origin) + as posted in LICENSE-DCO at root of NUT codebase as well as published + at https://developercertificate.org/ + * The checklist below is more of a reminder of steps to take and "dangers" to look out for. PRs to update this template are also welcome :) @@ -22,6 +27,8 @@ of "real" changes in the other commits. Similarly for typo fixes in comments or text documents. +- [ ] Please star NUT on GitHub, this helps with sponsorships! ;) + ## Frequent "underwater rocks" for driver addition/update PRs - [ ] Revised existing driver families and added a sub-driver if applicable @@ -45,7 +52,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/.github/workflows/PyNUTClient.yml b/.github/workflows/PyNUTClient.yml new file mode 100644 index 0000000000..018562485b --- /dev/null +++ b/.github/workflows/PyNUTClient.yml @@ -0,0 +1,78 @@ +name: Publish PyNUT client bindings for NUT šŸ distributions šŸ“¦ to PyPI +# based on https://medium.com/@VersuS_/automate-pypi-releases-with-github-actions-4c5a9cfe947d +# and https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ + +# NOTE: We may need to split this workflow into two files to do only +# paths for master branch, and run always for (release) tags? +on: + push: + paths: + - 'scripts/python/module/*' + - '.github/workflows/PyNUTClient.yml' + tags: + - '*' + branches: + - 'master' + +permissions: + id-token: write + +jobs: + build-n-publish: + if: github.repository_owner == 'networkupstools' + name: Build and publish Python šŸ distributions šŸ“¦ to PyPI + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Extract python interpreter path name + id: pythoncmd + run: >- + set -x ; + echo "PYTHON=$(command -v python)" >> $GITHUB_OUTPUT + - name: Extract tag name + id: tag + # Note: this is all a single shell line in the end, + # so we need semicolons between commands! + run: >- + set -x ; + TAG_NAME="$(echo $GITHUB_REF | cut -d / -f 3)" ; + if [ x"$TAG_NAME" = xmaster ]; then + { TAG_NAME="$(git describe --tags --match 'v[0-9]*.[0-9]*.[0-9]' --exclude '*-signed' --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*')" \ + || TAG_NAME="$(git describe --tags --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*' --exclude '*Windows*' --exclude '*IPM*')" ; } \ + && test -n "${TAG_NAME}" \ + || TAG_NAME="2.8.1-`TZ=UTC date +%s`" ; + fi >&2 ; + TAG_NAME="$(echo "$TAG_NAME" | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,' -e 's/^v//' -e 's/-g.*$//' -e 's/-/./g')" ; + echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT + - name: Install pypa/setuptools + run: >- + ${{ steps.pythoncmd.outputs.PYTHON }} -m + pip install wheel build + - name: Prepare source layout and Build a binary wheel + run: >- + set -e ; + cd scripts/python/module ; + cp -f Makefile.am Makefile ; + make -f Makefile.am clean-local dist NUT_SOURCE_GITREV_NUMERIC="${{ steps.tag.outputs.TAG_NAME }}" PYTHON="${{ steps.pythoncmd.outputs.PYTHON }}" top_srcdir="../../.." srcdir="." builddir="." ; + find . -ls + - name: Publish master distribution šŸ“¦ to Test PyPI + # https://github.com/pypa/gh-action-pypi-publish + if: ${{ !startsWith(github.ref, 'refs/tags') }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: scripts/python/module/dist + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository-url: https://test.pypi.org/legacy/ + - name: Publish tagged release distribution šŸ“¦ to PyPI + if: startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: scripts/python/module/dist + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..2438bd0555 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,75 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '32 12 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + # TODO: Want to find Python sources to test like with LGTM, see https://github.com/networkupstools/nut/issues/1726 + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ā„¹ļø Command-line programs to run using the OS shell. + # šŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitignore b/.gitignore index ff67167786..bc2e70a1a3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,13 +10,16 @@ .libs/ .inst/ /tmp/ +/obj/ /_install_pkgprotodir/ Makefile Makefile.in ## Parent directory only /aclocal.m4 +/ar-lib /autom4te.cache/ /ChangeLog +/ChangeLog.adoc /config.guess /config.log /config.status @@ -24,24 +27,38 @@ Makefile.in /config.h /config.cache /configure -/conf_nut_report_feature +/config.nut_report_feature.log* /conf??????/ +/conf??*.file /dir.??????/ /dir?.???????/ /configure-test*/ +/confdefs.h +/conftest.* /cscope.* /depcomp /INSTALL +/INSTALL.nut +/NEWS +/README +/TODO +/UPGRADING /install-sh /libtool /ltmain.sh /missing /test-driver *-spellchecked +*-prepped +*.adoc-parsed +*.adoc*.tmp +*.txt*.tmp /cppcheck*.xml +/.ci*.txt* /.ci*.log /.ci*.log.* .dirstamp +*.exe # Python precompiled files __pycache__/ @@ -54,6 +71,8 @@ __pycache__/ /NUT*.local.gz /NUT*.p5i /NUT*.depot +/*.msi +/*.MSI # Official dist /nut-*.tar.gz.md5 @@ -63,3 +82,11 @@ __pycache__/ # Debuggers and IDEs .gdb_history /nbproject +/.idea +/.vscode +/*.iml + +# Coredumps +*.stackdump +*.core +core 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/AUTHORS b/AUTHORS index c179b0c55f..3eaff4f4ef 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,17 +7,23 @@ # This is a blatant ripoff of the fields found in the Linux kernel's CREDITS # file. If we need more data, those fields can always be added later. # -# N = name, E = email, W = web address, D = description, P = PGP info, +# N = name, E = email, W = web address, D = description, P = PGP info, # S = snailmail address, etc. # # This file is supposed to be roughly alpha-sorted by the last name, but # if you want to hide at the bottom, that's fine by me. Just clarify # your preference when submitting changes to this file. +N: Axel Gembe +E: axel@gembe.net +W: http://axel.gembe.net/ +D: Added APC Modbus support +P: rsa4096/43109AAC 11E6 515C B8A4 26C6 07C1 36A4 1CB3 AA21 4310 9AAC + N: Stephen Brown E: steve@datalimbo.net W: http://www.datalimbo.net/ -D: Hacked genericups to add TrippLite Lan2.x support (Internet Office 700) +D: Hacked genericups to add TrippLite Lan2.x support (Internet Office 700) N: Bill Carlson E: wcarlson@wkks.org @@ -54,7 +60,7 @@ D: Provided a Best Fortress for development of a Best driver (bestups) N: Russell Kroll E: rkroll@exploits.org -W: http://www.networkupstools.org/ +W: https://www.networkupstools.org/ D: Original NUT author and coordinator P: 1024D/9DC0E77E 6A5C 7D2D 7945 C022 6104 D421 D61D C97F 9DC0 E77E @@ -140,7 +146,7 @@ E: lwhite@darkfires.net N: Walt Holman E: walt_h@lorettotel.net -D: Hacked up the cpsups driver for CyberPower text protocol UPSes +D: Hacked up the cpsups driver for CyberPower text protocol UPSes N: Fabio Di Niro E: blaxwan@users.sourceforge.net @@ -174,3 +180,8 @@ D: Author of bcmxcp driver, 3-phase work. N: Giuseppe Corbelli E: giuseppe.corbelli@copanitalia.com D: Author of asem driver + +N: zakx +E: zakx@zakx.de +D: Updating device support docs after unnecessarily writing a redundant +D: driver first diff --git a/COPYING b/COPYING index 883d3b1218..94adc8c31f 100644 --- a/COPYING +++ b/COPYING @@ -10,3 +10,27 @@ The Perl client module (scripts/perl/Nut.pm) is released under the same license as Perl itself. That is to say either GPL version 1 or (at your option) any later version, or the "Artistic License". + + Several fallback implementations for methods absent from standard library of + an end-user's current build platform are derived from source code available + under the two-clause BSD license (common/strptime.c, common/strnlen.c) + + Various methods may be adapted from code or ideas posted on Stack Exchange + sites (comments in NUT sources refer to original posts in this case), which + according to https://stackoverflow.com/help/licensing are made available under + different Creative Commons Attribution-ShareAlike license (CC BY-SA) versions + depending on contribution timestamp. + + Several autoconf methods under m4/ directory are derived from curl codebase + and were originally available under curl license. Which is not unlike the MIT + license, see https://daniel.haxx.se/blog/2022/06/17/curl-is-reuse-compliant/ + + To the best of our knowledge, conditions of the 2/3-clause BSD, MIT, curl and + CC BY-SA licenses allow redistribution and reuse of the codebase in projects + made available under GPL license terms, as long as attribution is provided. + + NUT contributors are encouraged to "sign off" their git commits as a conscious + act done under Developer's Certificate of Origin. See the copy of "LICENSE-DCO" + in the root of this distribution, but please note that it is not a "license" on + its own - rather a proclamation that work was done and submitted according to + applicable open-source licenses. diff --git a/INSTALL.nut b/INSTALL.nut deleted file mode 100644 index 4708b56684..0000000000 --- a/INSTALL.nut +++ /dev/null @@ -1,341 +0,0 @@ -Installation instructions -========================= - -This chapter describe 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. - -[[Installing_source]] -Installing from source ----------------------- - -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. - -[NOTE] -.Keep in mind that... -================================================================================ - -- the paths shown below are the default values you get by just calling -configure by itself. If you have used --prefix or similar, things will be -different. Also, if you didn't install this program from source yourself, the -paths will probably have a number of differences. - -- by default, your system probably won't find the man pages, since they -install to /usr/local/ups/man. You can fix this by editing your MANPATH, or -just do this: - - man -M /usr/local/ups/man - -- if your favorite system offers up to date binary packages, you should always -prefer these over a source installation. Along with the known advantages of such -systems for installation, upgrade and removal, there are many integration issues -that have been addressed. - -================================================================================ - - -Prepare your system -~~~~~~~~~~~~~~~~~~~~ - -System User creation -^^^^^^^^^^^^^^^^^^^^ - -Create at least one system user and a group for running this software. You -might call them "ups" and "nut". The exact names aren't important as -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 -will be 'ups' and 'nut' respectively. - -Be sure the new user is a member of the new group! If you forget to -do this, you will have problems later on when you try to start upsd. - - -Build and install -~~~~~~~~~~~~~~~~~ - -[[Configuration]] -Configuration -^^^^^^^^^^^^^ - -Configure the source tree for your system. Add the '--with-user' and -'--with-group' switch to set the user name and group that you created -above. - - ./configure --with-user=ups --with-group=nut - -If you need any other switches for configure, add them here. For example: - -* to build and install USB drivers, add '--with-usb' (note that you - need to install libusb development package or files). - -* to build and install SNMP drivers, add '--with-snmp' (note that - you need to install libsnmp development package or files). - -* to build and install CGI scripts, add '--with-cgi'. - -See <> from the User Manual, -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. - -Reference: <> from the -User Manual. - - -Build the programs -^^^^^^^^^^^^^^^^^^ - - make - -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. - - -Installation -^^^^^^^^^^^^ - -[NOTE] -===================================================================== - -you should now gain privileges for installing software if necessary: - - su - -===================================================================== - -Install the files to a system level directory: - - make install - -This will install the compiled programs and man pages, as well as -some data files required by NUT. Any optional features selected -during configuration will also be installed. - -This will also install sample versions of the NUT configuration -files. Sample files are installed with names like ups.conf.sample -so they will not overwrite any existing real config files you may -have created. - -If you are packaging this software, then you will probably want to -use the DESTDIR variable to redirect the build into another place, -i.e.: - - make DESTDIR=/tmp/package install - make DESTDIR=/tmp/package install-conf - -[[StatePath]] -State path creation -^^^^^^^^^^^^^^^^^^^ - -Create the state path directory for the driver(s) and server to use -for storing UPS status data and other auxiliary files, and make it -group-writable by the group of the system user you created. - - mkdir -p /var/state/ups - chmod 0770 /var/state/ups - chown root:nut /var/state/ups - -[[Ownership]] -Ownership and permissions -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Set ownership data and permissions on your serial or USB ports -that go to your UPS hardware. Be sure to limit access to just -the user you created earlier. - -These examples assume the second serial port (ttyS1) on a typical -Slackware system. On FreeBSD, that would be cuaa1. Serial ports -vary greatly, so yours may be called something else. - - chmod 0660 /dev/ttyS1 - chown root:nut /dev/ttyS1 - -//////////////////////////////////////////////////////////////////////////////// -FIXME: TBR -//////////////////////////////////////////////////////////////////////////////// - -The setup for USB ports is slightly more complicated. Device files -for USB devices, such as /proc/bus/usb/002/001, are usually -created "on the fly" when a device is plugged in, and disappear -when the device is disconnected. Moreover, the names of these -device files can change randomly. To set up the correct -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". - -(If you want to try if a driver works without setting up -hotplugging, you can add the "-u root" option to upsd, upsmon, and -drivers; this should allow you to follow the below -instructions. However, don't forget to set up the correct -permissions later!). - -NOTE: if you are using something like udev or devd, make sure -these permissions stay set across a reboot. If they revert to the -old values, your drivers may fail to start. - - -You are now ready to configure NUT, and start testing and using it. - -You can jump directly to the <>. - - -[[Installing_packages]] -Installing from packages ------------------------- - -This chapter describes the specific installation steps when using -binary packages that exist on various major systems. - -[[Debian]] -Debian, Ubuntu and other derivatives -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -NOTE: NUT is packaged and well maintained in these systems. -The official Debian packager is part of the NUT Team. - -Using your preferred method (apt-get, aptitude, Synaptic, ...), install -the 'nut' package, and optionally the following: - -- 'nut-cgi', if you need the CGI (HTML) option, -- 'nut-snmp', if you need the snmp-ups driver, -- 'nut-xml', for the netxml-ups driver, -- 'nut-powerman-pdu', to control the PowerMan daemon (PDU management) -- 'nut-dev', if you need the development files. - -//////////////////////////////////////////////////////////////////////////////// -- nut-client -//////////////////////////////////////////////////////////////////////////////// - -Configuration files are located in /etc/nut. -linkman:nut.conf[5] must be edited to be able to invoke /etc/init.d/nut - -NOTE: Ubuntu users can access the APT URL installation by clicking on link:apt://nut[this link]. - - -[[Mandriva]] -Mandriva -~~~~~~~~ - -NOTE: NUT is packaged and well maintained in these systems. -The official Mandriva packager is part of the NUT Team. - -Using your preferred method (urpmi, RPMdrake, ...), install one of the two below -packages: - -- 'nut-server' if you have a 'standalone' or 'netserver' installation, -- 'nut' if you have a 'netclient' installation. - -Optionally, you can also install the following: - -- 'nut-cgi', if you need the CGI (HTML) option, -- 'nut-devel', if you need the development files. - - -[[SUSE]] -SUSE / openSUSE -~~~~~~~~~~~~~~~ - -NOTE: NUT is packaged and well maintained in these systems. -The official SUSE packager is part of the NUT Team. - -Install the 'nut-classic' package, and optionally the following: - -- 'nut-drivers-net', if you need the snmp-ups or the netxml-ups drivers, -- 'nut-cgi', if you need the CGI (HTML) option, -- 'nut-devel', if you need the development files, - -NOTE: SUSE and openSUSE users can use the -link:http://software.opensuse.org/search?baseproject=ALL&p=1&q=nut[one-click install method] -to install NUT. - - -[[RedHat]] -Red Hat, Fedora and CentOS -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -NOTE: NUT is packaged and well maintained in these systems. -The official Red Hat packager is part of the NUT Team. - -Using your preferred method (yum, Add/Remove Software, ...), install one of the -two below packages: - -- 'nut' if you have a 'standalone' or 'netserver' installation, -- 'nut-client' if you have a 'netclient' installation. - -Optionally, you can also install the following: - -- 'nut-cgi', if you need the CGI (HTML) option, -- 'nut-xml', if you need the netxml-ups driver, -- 'nut-devel', if you need the development files. - - -[[FreeBSD]] -FreeBSD -~~~~~~~ - -You can either install NUT as a binary package or as a port. - -Binary package -^^^^^^^^^^^^^^ - -To install NUT as a package execute: - - # pkg install nut - -Port -^^^^ - -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: - - # make install clean - -USB UPS on FreeBSD -^^^^^^^^^^^^^^^^^^ - -For USB UPS devices the NUT package/port installs devd rules in +/usr/local/etc/devd/nut-usb.conf+ to set USB device permissions. 'devd' needs to be restarted for these rules to apply: - - # service devd restart - -(Re-)connect the device after restarting 'devd' and check that the USB device has the proper -permissions. Check the last entries of the system message buffer. You should -find an entry like - - # dmesg | tail - [...] - ugen0.2: at usbus0 - -The device file must be owned by group +uucp+ and must be group -read-/writable. In the example from above this would be - - # ls -Ll /dev/ugen0.2 - crw-rw---- 1 root uucp 0xa5 Mar 12 10:33 /dev/ugen0.2 - -If the permissions are not correct, verify that your device is registered in -+/usr/local/etc/devd/nut-usb.conf+. The vendor and product id can be found -using: - - # usbconfig -u 0 -a 2 dump_device_desc - -where +-u+ specifies the USB bus number and +-a+ specifies the USB device index. - - -You are now ready to configure NUT, and start testing and using it. - -You can jump directly to the -<>. diff --git a/INSTALL.nut.adoc b/INSTALL.nut.adoc new file mode 100644 index 0000000000..302b3b5948 --- /dev/null +++ b/INSTALL.nut.adoc @@ -0,0 +1,692 @@ +Installation instructions +========================= + +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 operating 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 +---------------------- + +These are the essential steps for compiling and installing this software +from distribution archives (usually "release tarballs") which include a +pre-built copy of the `configure` script and some other generated source +files. + +To build NUT from a Git checkout you may need some additional tools +(referenced just a bit below) and run `./autogen.sh` to generate the +needed files. For common developer iterations, porting to new platforms, +or in-place testing, running the `./ci_build.sh` script can be helpful. +The "<>" section details some more hints about such workflow, including some +`systemd` integration. + +The NUT linkdoc:packager-guide[Packager Guide], which presents the best +practices for installing and integrating NUT, is also a good reading. + +The <> +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: This "Config Prereqs" document for latest NUT iteration can be found at +https://github.com/networkupstools/nut/blob/master/docs/config-prereqs.txt +or as `docs/config-prereqs.txt` in your build workspace (from Git or tarball). + +[NOTE] +.Keep in mind that... +================================================================================ + +- the paths shown below are the default values you get by just calling + configure by itself. If you have used --prefix or similar, things will be + different. Also, if you didn't install this program from source yourself, + the paths will probably have a number of differences. + +- by default, your system probably won't find the man pages, since they + install to /usr/local/ups/man. You can fix this by editing your MANPATH, + or just do this: + + man -M /usr/local/ups/man + +- if your favorite system offers up to date binary packages, you should + always prefer these over a source installation (unless there are known + deficiencies in the package or one is too obsolete). Along with the known + advantages of such systems for installation, upgrade and removal, there + are many integration issues that have been addressed. + +================================================================================ + + +Prepare your system +~~~~~~~~~~~~~~~~~~~~ + +System User creation +^^^^^^^^^^^^^^^^^^^^ + +Create at least one system user and a group for running this software. +You might call them "ups" and "nut". The exact names aren't important as +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 +will be 'ups' and 'nut' respectively. + +Be sure the new user is a member of the new group! If you forget to +do this, you will have problems later on when you try to start upsd. + + +Build and install +~~~~~~~~~~~~~~~~~ + +NOTE: See also <>. + +[[Configuration]] +Configuration +^^^^^^^^^^^^^ + +Configure the source tree for your system. Add the '--with-user' and +'--with-group' switch to set the user name and group that you created +above. + + ./configure --with-user=ups --with-group=nut + +If you need any other switches for configure, add them here. For example: + +* to build and install USB drivers, add '--with-usb' (note that you + need to install libusb development package or files). + +* to build and install SNMP drivers, add '--with-snmp' (note that + you need to install libsnmp development package or files). + +* to build and install CGI scripts, add '--with-cgi'. + +See <> from the User Manual, +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. + +Reference: <> from the +User Manual. + + +Build the programs +^^^^^^^^^^^^^^^^^^ + + make + +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. + + +Installation +^^^^^^^^^^^^ + +[NOTE] +===================================================================== + +you should now gain privileges for installing software if necessary: + + su + +===================================================================== + +Install the files to a system level directory: + + make install + +This will install the compiled programs and man pages, as well as +some data files required by NUT. Any optional features selected +during configuration will also be installed. + +This will also install sample versions of the NUT configuration +files. Sample files are installed with names like ups.conf.sample +so they will not overwrite any existing real config files you may +have created. + +If you are packaging this software, then you will probably want to +use the DESTDIR variable to redirect the build into another place, +i.e.: + + make DESTDIR=/tmp/package install + make DESTDIR=/tmp/package install-conf + +[[StatePath]] +State path creation +^^^^^^^^^^^^^^^^^^^ + +Create the state path directory for the driver(s) and server to use +for storing UPS status data and other auxiliary files, and make it +group-writable by the group of the system user you created. + + mkdir -p /var/state/ups + chmod 0770 /var/state/ups + chown root:nut /var/state/ups + +[[Ownership]] +Ownership and permissions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Set ownership data and permissions on your serial or USB ports +that go to your UPS hardware. Be sure to limit access to just +the user you created earlier. + +These examples assume the second serial port (ttyS1) on a typical +Slackware system. On FreeBSD, that would be cuaa1. Serial ports +vary greatly, so yours may be called something else. + + chmod 0660 /dev/ttyS1 + chown root:nut /dev/ttyS1 + +//////////////////////////////////////////////////////////////////////////////// +FIXME: TBR +//////////////////////////////////////////////////////////////////////////////// + +The setup for USB ports is slightly more complicated. Device files +for USB devices, such as /proc/bus/usb/002/001, are usually +created "on the fly" when a device is plugged in, and disappear +when the device is disconnected. Moreover, the names of these +device files can change randomly. To set up the correct +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". + +(If you want to try if a driver works without setting up +hotplugging, you can add the "-u root" option to upsd, upsmon, and +drivers; this should allow you to follow the below +instructions. However, don't forget to set up the correct +permissions later!). + +NOTE: if you are using something like udev or devd, make sure +these permissions stay set across a reboot. If they revert to the +old values, your drivers may fail to start. + + +You are now ready to configure NUT, and start testing and using it. + +You can jump directly to the <>. + +[[Installing_inplace]] +Building NUT for inā€place upgrades or nonā€disruptive tests +---------------------------------------------------------- + +NOTE: The NUT GitHub Wiki article at +https://github.com/networkupstools/nut/wiki/Building-NUT-for-in%E2%80%90place-upgrades-or-non%E2%80%90disruptive-tests +may contain some more hints as contributed by the community. + +Overview +~~~~~~~~ + +Since late 2022/early 2023 NUT codebase supports "in-place" builds +which try their best to discover the configuration of an earlier build +(configuration and run-time paths and OS accounts involved, maybe an +exact configuration if stored in deployed binaries). + +This optional mode is primarily intended for several use-cases: + +* Test recent GitHub "master" branch or a proposed PR to see if it + solves a practical problem for a particular user; +* Replace an existing deployment, e.g.Ā if OS-provided packages deliver + obsolete code, to use newer NUT locally in "production mode". + - In such cases ideally get your distribution, NAS vendor, etc. + to provide current NUT -- and benefit from a better integrated + and tested product. + +Note that "just testing" often involves building the codebase and new +drivers or tools in question, and running them right from the build +workspace (without installing into the system and so risking an +unpredictable-stability state). In case of testing new driver builds, +note that you would need to stop the normally running instances to +free up the communications resources (USB/serial ports, etc.), run the +new driver program in data-dump mode, and restart the normal systems +operations. + +Such tests still benefit from matching the build configuration to what +is already deployed, in order to request same configuration files and +system access permissions (e.g.Ā to own device nodes for physical-media +ports involved, and to read the production configuration files). + +Pre-requisites +^^^^^^^^^^^^^^ + +The <> +document details tools and dependencies that were added on NUT CI build +environments, which now cover many operating systems. This should +provide a decent starting point for the build on yours (PRs to update +the document are welcome!) + +Note that unlike distribution tarballs, Git sources do not include a +`configure` script and some other files -- these should be generated by +running `autogen.sh` (or `ci_build.sh` that calls it). + +Getting the right sources +^^^^^^^^^^^^^^^^^^^^^^^^^ + +To build the current tip of development iterations (usually after PR +merges that passed CI, reviews and/or other tests), just clone the NUT +repository and "master" branch should get checked out by default (also +can request that explicitly, per example posted below). + +If you want to quickly test a particular pull request, see the link on +top of the PR page that says `... wants to merge ... from : ...` and +copy the proposed-source URL of that "from" part. + +For example, in some PR this says `jimklimov:issue-1234` and links to +`https://github.com/jimklimov/nut/tree/issue-1234`. +For manual git-cloning, just paste that URL into the shell and replace +the `/tree/` with "`-b`" CLI option for branch selection, like this: + + :; cd /tmp + ### Checkout https://github.com/jimklimov/nut/tree/issue-1234 + :; git clone https://github.com/jimklimov/nut -b issue-1234 + +Testing with CI helper +~~~~~~~~~~~~~~~~~~~~~~ + +NOTE: this uses the `ci_build.sh` script to arrange some rituals and +settings, in this case primarily to default the choice of drivers to +auto-detection of what can be built, and to skip building documentation. +Also note that this script supports many other scenarios for CI and +developers, managed by `BUILD_TYPE` and other environment variables, +which are not explored here. + +An "in-place" _testing_ build and run would probably go along these lines: + + :; cd /tmp + :; git clone -b master https://github.com/networkupstools/nut + :; cd nut + :; ./ci_build.sh inplace + ### Temporarily stop your original drivers + :; ./drivers/nutdrv_qx -a DEVNAME_FROM_UPS_CONF -d1 -DDDDDD \ + # -x override...=... -x subdriver=... + ### Can start back your original drivers + ### Analyze and/or post back the data-dump + +[NOTE] +====== +To probe a device for which you do not have an `ups.conf` section +yet, you must specify `-s name` and all config options (including +`port`) on command-line with `-x` arguments, e.g.: + + :; ./drivers/nutdrv_qx -s temp-ups \ + -d1 -DDDDDD -x port=auto \ + -x vendorid=... -x productid=... \ + -x subdriver=... +====== + +Replacing a NUT deployment +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While `ci_build.sh inplace` can be a viable option for preparation of +local builds, you may want to have precise control over `configure` +options (e.g.Ā choice of required drivers, or enabled documentation). + +A sound starting point would be to track down packaging recipes used by +your distribution (e.g. +link:https://src.fedoraproject.org/rpms/nut/blob/rawhide/f/nut.spec[RPM spec] +or +link:https://salsa.debian.org/debian/nut/-/blob/debian/debian/rules[DEB rules] +files, etc.) to detail the same paths if you intend to replace those, +and copy the parameters for `configure` script from there -- especially +if your system is not currently running NUT v2.8.1 or newer (which embeds +this information to facilitate in-place upgrade rebuilds). + +Note that the primary focus of in-place automated configuration mode is +about critical run-time options, such as OS user accounts, configuration +location and state/PID paths, so it alone might not replace your driver +binaries that the package would put into an obscure location like +`/lib/nut`. It would however install init-scripts or systemd units that +would refer to new locations specified by the current build, so such old +binaries would just consume disk space but not run. + +Replacing any NUT deployment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +NOTE: For deployments on OSes with `systemd` see the next section. + +This goes similar to usual build and install from Git: + + :; cd /tmp + :; git clone https://github.com/networkupstools/nut + :; cd nut + :; ./autogen.sh + :; ./configure --enable-inplace-runtime # --maybe-some-other-options + :; make -j 4 all && make -j 4 check && sudo make install + +Note that `make install` does not currently handle all the nuances that +packaging installation scripts would, such as customizing filesystem +object ownership, daemon restarts, etc. or even creating locations like +`/var/state/ups` and `/var/run/nut` as part of the `make` target (but +e.g.Ā the delivered `systemd-tmpfiles` configuration can handle that for +a large part of the audience). This aspect is tracked as +link:https://github.com/networkupstools/nut/issues/1298[issue #1298] + +At this point you should revise the locations for PID files +(e.g.Ā `/var/run/nut`) and pipe files (e.g.Ā `/var/state/ups`) that they +exist and permissions remain suitable for NUT run-time user selected by +your configuration, and typically stop your original NUT drivers, +data-server (upsd) and upsmon, and restart them using the new binaries. + +Replacing a systemd-enabled NUT deployment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For modern Linux distributions with `systemd` this replacement procedure +could be enhanced like below, to also re-enable services (creating proper +symlinks) and to get them started: + + :; cd /tmp + :; git clone https://github.com/networkupstools/nut + :; cd nut + :; ./autogen.sh + :; ./configure --enable-inplace-runtime # --maybe-some-other-options + :; make -j 4 all && make -j 4 check && \ + { sudo systemctl stop nut-monitor nut-server || true ; } && \ + { sudo systemctl stop nut-driver.service || true ; } && \ + { sudo systemctl stop nut-driver.target || true ; } && \ + { sudo systemctl stop nut.target || true ; } && \ + sudo make install && \ + sudo systemctl daemon-reload && \ + sudo systemd-tmpfiles --create && \ + sudo systemctl disable nut.target nut-driver.target \ + nut-monitor nut-server nut-driver-enumerator.path \ + nut-driver-enumerator.service && \ + sudo systemctl enable nut.target nut-driver.target \ + nut-monitor nut-server nut-driver-enumerator.path \ + nut-driver-enumerator.service && \ + { sudo systemctl restart udev || true ; } && \ + sudo systemctl restart nut-driver-enumerator.service \ + nut-monitor nut-server + +Note the several attempts to stop old service units -- naming did change +from 2.7.4 and older releases, through 2.8.0, and up to current codebase. +Most of the NUT units are now `WantedBy=nut.target` (which is in turn +`WantedBy=multi-user.target` and so bound to system startup). You should +only `systemctl enable` those units you need on this system -- this allows +it to not start the daemons you do not need (e.g. not run `upsd` NUT data +server on systems which are only `upsmon secondary` clients). + +The `nut-driver-enumerator` units (and corresponding shell script) are +part of a new feature introduced in NUT 2.8.0, which automatically +discovers `ups.conf` sections and changes to their contents, and manages +instances of a `nut-driver@.service` definition. + +You may also have to restart (or reload if supported) some system services +if your updates impact them, like `udev` for updates USB support (note also +link:https://github.com/networkupstools/nut/pull/1342[PR #1342] regarding +the change from `udev.rules` to `udev.hwdb` file with NUT v2.8.0 or later -- +you may have to remove the older file manually). + +Iterating with a systemd deployment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are regularly building NUT from GitHub "master" branch, or iterating +local development branches of your own, you *may* get away with shorter +constructs to just restart the services after installing newly built files +(if you know there were no changes to unit file definitions and dependencies), +e.g.: + + :; cd /tmp + :; git clone https://github.com/networkupstools/nut + :; cd nut + :; git checkout -b issue-1234 ### your PR branch name, arbitrary + :; ./autogen.sh + :; ./configure --enable-inplace-runtime # --maybe-some-other-options + ### Iterate your code changes (e.g. PR draft), build and install with: + :; make -j 4 all && make -j 4 check && \ + sudo make install && \ + sudo systemctl daemon-reload && \ + sudo systemd-tmpfiles --create && \ + sudo systemctl restart \ + nut-driver-enumerator.service nut-monitor nut-server + +Next steps after an in-place upgrade +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can jump directly to the <> +if you need to revise the settings for your new NUT version, take advantage +of new configuration options, etc. + +Check the linkdoc:NEWS and linkdoc:UPGRADING[Upgrade Notes] files in your +Git workspace to review features that should be present in your new build. + +[[Installing_packages]] +Installing from packages +------------------------ + +This chapter describes the specific installation steps when using +binary packages that exist on various major systems. + +[[Debian]] +Debian, Ubuntu and other derivatives +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NOTE: NUT is packaged and well maintained in these systems. +The official Debian packager is part of the NUT Team. + +Using your preferred method (apt-get, aptitude, Synaptic, ...), install +the 'nut' package, and optionally the following: + +- 'nut-cgi', if you need the CGI (HTML) option, +- 'nut-snmp', if you need the snmp-ups driver, +- 'nut-xml', for the netxml-ups driver, +- 'nut-powerman-pdu', to control the PowerMan daemon (PDU management) +- 'nut-dev', if you need the development files. + +//////////////////////////////////////////////////////////////////////////////// +- nut-client +//////////////////////////////////////////////////////////////////////////////// + +Configuration files are located in /etc/nut. +linkman:nut.conf[5] must be edited to be able to invoke /etc/init.d/nut + +NOTE: Ubuntu users can access the APT URL installation by clicking +on link:apt://nut[this link]. + + +[[Mandriva]] +Mandriva +~~~~~~~~ + +NOTE: NUT is packaged and well maintained in these systems. +The official Mandriva packager is part of the NUT Team. + +Using your preferred method (urpmi, RPMdrake, ...), install one of the +two below packages: + +- 'nut-server' if you have a 'standalone' or 'netserver' installation, +- 'nut' if you have a 'netclient' installation. + +Optionally, you can also install the following: + +- 'nut-cgi', if you need the CGI (HTML) option, +- 'nut-devel', if you need the development files. + + +[[SUSE]] +SUSE / openSUSE +~~~~~~~~~~~~~~~ + +NOTE: NUT is packaged and well maintained in these systems. +The official SUSE packager is part of the NUT Team. + +Install the 'nut-classic' package, and optionally the following: + +- 'nut-drivers-net', if you need the snmp-ups or the netxml-ups drivers, +- 'nut-cgi', if you need the CGI (HTML) option, +- 'nut-devel', if you need the development files, + +NOTE: SUSE and openSUSE users can use the +link:http://software.opensuse.org/search?baseproject=ALL&p=1&q=nut[one-click install method] +to install NUT. + + +[[RedHat]] +Red Hat, Fedora and CentOS +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NOTE: NUT is packaged and well maintained in these systems. +The official Red Hat packager is part of the NUT Team. + +Using your preferred method (yum, Add/Remove Software, ...), install +one of the two below packages: + +- 'nut' if you have a 'standalone' or 'netserver' installation, +- 'nut-client' if you have a 'netclient' installation. + +Optionally, you can also install the following: + +- 'nut-cgi', if you need the CGI (HTML) option, +- 'nut-xml', if you need the netxml-ups driver, +- 'nut-devel', if you need the development files. + + +[[FreeBSD]] +FreeBSD +~~~~~~~ + +You can either install NUT as a binary package or as a port. + +Binary package +^^^^^^^^^^^^^^ + +To install NUT as a package execute: + + # pkg install nut + +Port +^^^^ + +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: + + # make install clean + +USB UPS on FreeBSD +^^^^^^^^^^^^^^^^^^ + +For USB UPS devices the NUT package/port installs devd rules in ++/usr/local/etc/devd/nut-usb.conf+ to set USB device permissions. + 'devd' needs to be restarted for these rules to apply: + + # service devd restart + +(Re-)connect the device after restarting 'devd' and check that the USB +device has the proper permissions. Check the last entries of the system +message buffer. You should find an entry like: + + # dmesg | tail + [...] + ugen0.2: at usbus0 + +The device file must be owned by group +uucp+ and must be group +read-/writable. In the example from above this would be + + # ls -Ll /dev/ugen0.2 + crw-rw---- 1 root uucp 0xa5 Mar 12 10:33 /dev/ugen0.2 + +If the permissions are not correct, verify that your device is registered in ++/usr/local/etc/devd/nut-usb.conf+. The vendor and product id can be found +using: + + # usbconfig -u 0 -a 2 dump_device_desc + +where +-u+ specifies the USB bus number and +-a+ specifies the USB device +index. + + +[[Windows]] +Windows +~~~~~~~ + +Windows binary package +^^^^^^^^^^^^^^^^^^^^^^ + +[NOTE] +====== +NUT binary package built for Windows platform was last issued for +a much older codebase (using NUT v2.6.5 as a baseline). While the current +state of the codebase you are looking at aims to refresh the effort of +delivering NUT on Windows, the aim at the moment is to help developers +build and modernize it after a decade of blissful slumber, and packages +are not being regularly produced yet. Functionality of such builds varies +a lot depending on build environment used. This effort is generally +tracked at https://github.com/orgs/networkupstools/projects/2/views/1 +and help would be welcome! + +It should currently be possible to build the codebase in native Windows +with MSYS2/MinGW and cross-building from Linux with mingw (preferably +in a Debian/Ubuntu container). Refer to +link:config-prereqs.txt[Prerequisites for building NUT on different OSes] +and link:scripts/Windows/README.adoc[scripts/Windows/README.adoc file] +for respective build environment preparation instructions. + +Note that to use NUT for Windows, non-system dependency DLL files must +be located in same directory as each EXE file that uses them. This can be +accomplished for FOSS libraries (copying them from the build environment) +by calling `make install-win-bundle DESTDIR=/some/valid/location` easily. + +Archives with binaries built by recent iterations of continuous integration +jobs should be available for exploration on the respective CI platforms. +====== + +*Information below may be currently obsolete, but the NUT project wishes +it to become actual and factual again :)* + +NUT binary package built for Windows platform comes in a `.msi` file. + +If you are using Windows 95, 98 or Me, you should install +link:http://www.microsoft.com/downloads/en/details.aspx?familyid=cebbacd8-c094-4255-b702-de3bb768148f&displaylang=en[Windows Installer 2.0] +from Microsoft site. + +If you are using Windows 2000 or NT 4.0, you can +link:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=4b6140f9-2d36-4977-8fa1-6f8a0f5dca8f&DisplayLang=en[download it here]. + +Newer Windows releases should include the Windows Installer natively. + +Run `NUT-Installer.msi` and follow the wizard indications. + +If you plan to use an UPS which is locally connected to an USB port, +you have to install +link:https://sourceforge.net/projects/libusb-win32/files/[libUSB-win32] +on your system. Then you must install your device via libusb's "Inf Wizard". + +NOTE: If you intend to build from source, relevant sources may be available at +https://github.com/mcuee/libusb-win32 and keep in mind that it is a variant of +libusb-0.1. Current NUT supports libusb-1.0 as well, and that project should +have Windows support out of the box (but it was not explored for NUT yet). + +If you have selected default directory, all configuration files are located in +`C:\Program Files\NUT\ups\etc` + +Building for Windows +^^^^^^^^^^^^^^^^^^^^ + +For suggestions about setting up the NUT build environment variants +for Windows, please see link:docs/config-prereqs.txt and/or +link:scripts/Windows/README.adoc files. Note this is rather experimental +at this point. + + +Runtime configuration +~~~~~~~~~~~~~~~~~~~~~ + +You are now ready to configure NUT, and start testing and using it. + +You can jump directly to the +<>. diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index f254cfd736..d425fd255b 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -12,7 +12,10 @@ def buildCommit = '86a32237c7df45c5aba640746f7afc4de09505a1' // See https://github.com/networkupstools/jenkins-dynamatrix/ for the lib // Agent setup evolves at https://ci.networkupstools.org/computer/ -@Library('jenkins-dynamatrix') _ +// NOTE: The "${BRANCH_NAME}" below IS NOT A VARIABLE! +// Special notation per custom plugin build including changes from +// https://github.com/jenkinsci/pipeline-groovy-lib-plugin/pull/19/ +@Library('jenkins-dynamatrix@${BRANCH_NAME}') _ import org.nut.dynamatrix.dynamatrixGlobalState; import org.nut.dynamatrix.*; @@ -27,11 +30,21 @@ import org.nut.dynamatrix.*; dynacfgPipeline.disableSlowBuildCIBuild = false dynacfgPipeline.disableSlowBuildCIBuildExperimental = false + // NOTE: Disabled by default because with -std=c* the compiler and linker + // (at least on environments NUT CI farm has) do not "see" many things, + // and do not even define WIN32, and this is unrelated to NUT codebase. + // This toggle aims to only disable 'c' builds in the scenario; but the + //'gnu' ones should still happen if it is enabled overall. + dynacfgPipeline.disableStrictCIBuild_CrossWindows = true + // At this time, GCC succeeds building C89/GNU89 mode for NUT // while CLANG complains about things we can't fix easily. 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): @@ -52,12 +65,28 @@ import org.nut.dynamatrix.*; dynacfgPipeline.disableSlowBuildCIBuild_QEMU = false } - dynacfgPipeline.traceBuildShell_configureEnvvars = false// true - dynacfgPipeline.traceBuildShell = false //true + dynacfgPipeline.traceBuildShell_configureEnvvars = false // true + dynacfgPipeline.traceBuildShell = false // true + + //if (false) // <<< (Un-)comment away in select runs/branches + //if (true) // <<< (Un-)comment away in select runs/branches + if ( env?.BRANCH_NAME ==~ /.*fightwarn-verbose.*/ ) + { + dynacfgPipeline.traceBuildShell_configureEnvvars = true // false + dynacfgPipeline.traceBuildShell = true // false + } 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. @@ -87,7 +116,7 @@ import org.nut.dynamatrix.*; //'stageNameFunc': null, //'dynamatrixAxesLabels': [~/^OS_.+/, 'MAKE'], 'dynamatrixAxesLabels': ['OS_FAMILY', 'OS_DISTRO', 'MAKE'], - 'single': '( \${MAKE} shellcheck )', + 'single': '( if [ x"\${MAKE-}" = x ]; then echo "WARNING: MAKE is somehow unset, defaulting!" >&2; MAKE=make; fi; \${MAKE} shellcheck )', 'multi': '(cd tests && SERVICE_FRAMEWORK="selftest" SHELL_PROGS="$SHELL_PROGS" ./nut-driver-enumerator-test.sh )', 'multiLabel': 'SHELL_PROGS', 'skipShells': [ 'zsh', 'tcsh', 'csh' ] @@ -141,9 +170,16 @@ import org.nut.dynamatrix.*; dynacfgPipeline.axisCombos_STRICT_C = [~/CSTDVARIANT=c/] dynacfgPipeline.axisCombos_GNU_C = [~/CSTDVARIANT=gnu/] + // Here we consider native-platform builds on a Windows box + // (possibly with need for "bat" instead of "sh" steps): dynacfgPipeline.axisCombos_WINDOWS = [~/OS_FAMILY=windows/] dynacfgPipeline.axisCombos_NOT_WINDOWS = [~/OS_FAMILY=(?!windows)/] + // TODO: some cross-build enviroments like Linux with mingw? + // Currently done as an explicit scenario below... + dynacfgPipeline.axisCombos_WINDOWS_CROSS = [~/OS_FAMILY=(mingw|mingw32|mingw64|msys2)/] + dynacfgPipeline.axisCombos_NOT_WINDOWS_CROSS = [~/OS_FAMILY=(?!(mingw|mingw32|mingw64|msys2))/] + // At a minimum, we don't want to mess up our arches: dynacfgPipeline.excludeCombos_DEFAULT = [ dynacfgPipeline.axisCombos_ARCH32x64, @@ -174,6 +210,7 @@ import org.nut.dynamatrix.*; dynamatrixGlobalState.branchDefaultStable = 'fightwarn' } +/* if ( env?.BRANCH_NAME ==~ /^FTY.*$/ || env?.CHANGE_TARGET ==~ /^FTY.*$/ ) { // NOTE: Currently the 42ITy fork of NUT is not up to speed // regarding the codebase quality (generates lots of warnings) @@ -181,6 +218,7 @@ import org.nut.dynamatrix.*; // we quickly bail out of its automated builds on NUT CI farm. dynacfgPipeline.failFast = true } +*/ if ( env?.BRANCH_NAME ==~ dynacfgPipeline.branchStableRegex ) { // For our main branches we want all builds in full, @@ -229,19 +267,24 @@ import org.nut.dynamatrix.*; if (!dynacfgPipeline.containsKey('buildPhases')) { dynacfgPipeline.buildPhases = [:] } - dynacfgPipeline.buildPhases['distcheck'] = """( eval \${CONFIG_ENVVARS} time \${MAKE} \${MAKE_OPTS} distcheck DISTCHECK_FLAGS="\${CONFIG_OPTS}" )""" + + // Imported from jenkins-dynamatrix JSL vars/autotools.groovy: + // a workaround for the cases of curiously missing MAKE envvar... + dynacfgPipeline.buildPhases['distcheck'] = """( if [ x"\${MAKE-}" = x ]; then echo "WARNING: MAKE is somehow unset, defaulting!" >&2; MAKE=make; fi; eval \${CONFIG_ENVVARS} time \${MAKE} \${MAKE_OPTS} distcheck DISTCHECK_FLAGS="\${CONFIG_OPTS}" )""" // Note: shellcheck/spellcheck/... require autotools currently // or need to be redefined with respective BUILD_TYPE //dynacfgPipeline.buildSystem = 'ci_build.sh' //dynacfgPipeline.slowBuildDefaultBody = { echo "Running default custom build" } - dynacfgPipeline.slowBuildDefaultBody_autotools = { delegate -> setDelegate(delegate) + dynacfgPipeline.slowBuildDefaultBody_autotools = { def delegate -> setDelegate(delegate) + // Be sure to have a fixed resolved String here ASAP: + String stageNameClone = "${stageName}" def dsbcClone = dsbc.clone() - def stageNameClone = "${stageName}" stage('Investigate envvars (Autotools DEBUG)') { - echo "Running default custom build for '${stageName}' ==> ${dsbc.toString()}" + echo "Running default custom build for '${stageNameClone}' ==> ${dsbcClone.toString()}" + + (dynacfgPipeline?.configureEnvvars ? "" : " (note: has no dynacfgPipeline.configureEnvvars)") // Trick about endianness via ELF binary header picked up from https://serverfault.com/a/749469/490516 sh label: 'Inspect initial envvars', script: """ hostname; date -u; uname -a echo "LONG_BIT:`getconf LONG_BIT` WORD_BIT:`getconf WORD_BIT`" || true @@ -257,19 +300,21 @@ set | sort -n """ } } - infra.withEnvOptional(dynacfgPipeline.defaultTools) { + withEnvOptional(dynacfgPipeline.defaultTools) { unstashCleanSrc(dynacfgPipeline.stashnameSrc) buildMatrixCellCI(dynacfgPipeline, dsbcClone, stageNameClone) //buildMatrixCellCI(dynacfgPipeline, dsbc, stageName) } } - dynacfgPipeline.slowBuildDefaultBody_ci_build = { delegate -> setDelegate(delegate) + dynacfgPipeline.slowBuildDefaultBody_ci_build = { def delegate -> setDelegate(delegate) + // Be sure to have a fixed resolved String here ASAP: + String stageNameClone = "${stageName}" def dsbcClone = dsbc.clone() - def stageNameClone = "${stageName}" stage('Investigate envvars (CI_Build DEBUG)') { - echo "Running default custom build for '${stageName}' ==> ${dsbc.toString()}" + echo "Running default custom build for '${stageNameClone}' ==> ${dsbcClone.toString()}" + + (dynacfgPipeline?.configureEnvvars ? "" : " (note: has no dynacfgPipeline.configureEnvvars)") // Trick about endianness via ELF binary header picked up from https://serverfault.com/a/749469/490516 sh label: 'Inspect initial envvars', script: """ hostname; date -u; uname -a echo "LONG_BIT:`getconf LONG_BIT` WORD_BIT:`getconf WORD_BIT`" || true @@ -285,7 +330,7 @@ set | sort -n """ } } - infra.withEnvOptional(dynacfgPipeline.defaultTools) { + withEnvOptional(dynacfgPipeline.defaultTools) { unstashCleanSrc(dynacfgPipeline.stashnameSrc) def dynacfgPipeline_ciBuild = dynacfgPipeline.clone() dynacfgPipeline_ciBuild.buildSystem = 'ci_build.sh' @@ -317,7 +362,7 @@ set | sort -n """ //branchRegexTarget: dynacfgPipeline.branchStableRegex, branchRegexTarget: ~/fightwarn/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: dynacfgBase.commonLabelExpr, //defaultDynamatrixConfig: dynacfgBase.defaultDynamatrixConfig, @@ -342,6 +387,7 @@ set | sort -n """ //dynamatrixAxesLabels: ['OS_FAMILY', 'OS_DISTRO', '${COMPILER}VER', 'ARCH${ARCH_BITS}'], //dynamatrixAxesLabels: [~/^OS/, '${COMPILER}VER', 'ARCH${ARCH_BITS}'], excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_STRICT_C + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages //'bodyParStages': {} @@ -354,7 +400,7 @@ set | sort -n """ branchRegexTarget: ~/fightwarn/, // NOTE: For fightwarn, we want some schenarios that would always build to test //appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: dynacfgBase.commonLabelExpr, //defaultDynamatrixConfig: dynacfgBase.defaultDynamatrixConfig, @@ -385,6 +431,7 @@ set | sort -n """ //dynamatrixAxesLabels: ['OS_FAMILY', 'OS_DISTRO', '${COMPILER}VER', 'ARCH${ARCH_BITS}'], //dynamatrixAxesLabels: [~/^OS/, '${COMPILER}VER', 'ARCH${ARCH_BITS}'], excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_STRICT_C + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages //'bodyParStages': {} @@ -395,7 +442,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -419,7 +466,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -430,7 +479,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: ["(NUT_BUILD_CAPS=valgrind=yes||NUT_BUILD_CAPS=valgrind)"], excludedNodelabels: ["NUT_BUILD_CAPS=valgrind=no"], @@ -455,6 +504,7 @@ 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 + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -474,7 +524,7 @@ set | sort -n """ //branchRegexTarget: dynacfgPipeline.branchStableRegex, branchRegexTarget: ~/fightwarn/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ dynamatrixAxesLabels: ['OS_FAMILY'], // + [ 'OS_DISTRO', 'MAKE'], requiredNodelabels: ["(NUT_BUILD_CAPS=cppcheck||NUT_BUILD_CAPS=cppcheck=yes)"], @@ -503,6 +553,7 @@ 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 + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -517,7 +568,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: "nut-builder:alldrv", requiredNodelabels: ["(NUT_BUILD_CAPS=drivers:all||nut-builder:alldrv)"], @@ -558,7 +609,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: "nut-builder:alldrv", requiredNodelabels: ["(NUT_BUILD_CAPS=drivers:all||nut-builder:alldrv)"], @@ -583,6 +634,7 @@ 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_STRICT_C + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -593,7 +645,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, branchRegexTarget: ~/fightwarn/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: "nut-builder:alldrv", requiredNodelabels: ["(NUT_BUILD_CAPS=drivers:all||nut-builder:alldrv)"], @@ -618,7 +670,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -638,7 +692,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_DMF_PY, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ commonLabelExpr: "nut-builder:DMF", // TOTHINK: Should we also vary compilers here? @@ -673,13 +727,15 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], 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?) @@ -687,7 +743,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_DOC, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: dynacfgBase.commonLabelExpr + " && doc-builder", //commonLabelExpr: infra.labelDocumentationWorker(), @@ -703,6 +759,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 +770,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -724,7 +785,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_TXT, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: dynacfgBase.commonLabelExpr + " && doc-builder", //commonLabelExpr: infra.labelDocumentationWorker(), @@ -749,7 +810,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -760,7 +823,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -776,7 +839,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: [ @@ -785,6 +848,7 @@ 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 + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -799,7 +863,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, //branchRegexTarget: ~/fightwarn.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -812,7 +876,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: [ @@ -822,6 +886,7 @@ 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 + [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -832,7 +897,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*89.*)$/, branchRegexTarget: ~/fightwarn.*89.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -855,6 +920,7 @@ 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 + [dynacfgPipeline.axisCombos_COMPILER_GCC] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -865,7 +931,7 @@ set | sort -n """ //branchRegexSource: ~/^(PR-.+|fightwarn.*|.*qemu.*)$/, //branchRegexTarget: ~/^(master|main|stable|.*qemu.*)$/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ //commonLabelExpr: "qemu-" + dynacfgBase.commonLabelExpr, commonLabelExpr: "qemu-nut-builder || ssh-qemu-nut-builder", @@ -894,7 +960,8 @@ set | sort -n """ excludeCombos: [ [~/BITS=32/, ~/ARCH_BITS=64/], [~/BITS=64/, ~/ARCH_BITS=32/], [~/CSTDVARIANT=c/], - [~/OS_DISTRO=(openindiana|freebsd).*/, ~/CSTDVERSION_cxx=[12].+/, ~/COMPILER=GCC/] + [~/OS_DISTRO=(openindiana|freebsd).*/, ~/CSTDVERSION_cxx=[12].+/, ~/COMPILER=GCC/], + dynacfgPipeline.axisCombos_WINDOWS_CROSS ] ], body) }, // getParStages @@ -906,7 +973,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, branchRegexTarget: ~/fightwarn/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -931,7 +998,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] //+ [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] ] ], body) }, // getParStages @@ -948,7 +1017,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*89.*)$/, branchRegexTarget: ~/fightwarn.*89.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -972,18 +1041,20 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], 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, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -997,6 +1068,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 +1084,9 @@ 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] + + [dynacfgPipeline.axisCombos_WINDOWS_CROSS] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -1021,7 +1097,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, branchRegexTarget: ~/fightwarn/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -1073,12 +1149,14 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], 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 + [ dynacfgPipeline.axisCombos_GNU_C, + dynacfgPipeline.axisCombos_WINDOWS_CROSS, dynacfgPipeline.axisCombos_WINDOWS ] ], body) @@ -1094,7 +1172,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, branchRegexTarget: ~/fightwarn.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -1111,12 +1189,14 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], 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 + [ dynacfgPipeline.axisCombos_GNU_C, + dynacfgPipeline.axisCombos_WINDOWS_CROSS, dynacfgPipeline.axisCombos_WINDOWS ] + [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] ], body) @@ -1129,7 +1209,7 @@ set | sort -n """ branchRegexSource: ~/^(PR-.+|.*fightwarn.*89.*)$/, branchRegexTarget: ~/fightwarn.*89.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -1146,12 +1226,14 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], 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 + [ dynacfgPipeline.axisCombos_GNU_C, + dynacfgPipeline.axisCombos_WINDOWS_CROSS, dynacfgPipeline.axisCombos_WINDOWS ] + [dynacfgPipeline.axisCombos_COMPILER_GCC] ], body) @@ -1159,12 +1241,12 @@ set | sort -n """ 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration - ,[name: 'Strict C and GNU standard builds on Windows platforms, without distcheck and docs (allowed to fail)', + ,[name: 'Strict C and GNU standard builds on native-Windows platforms, without distcheck and docs (allowed to fail)', disabled: dynacfgPipeline.disableSlowBuildCIBuildExperimental, - branchRegexSource: ~/^(PR-.+|.*fightwarn.*)$/, - branchRegexTarget: ~/fightwarn/, + branchRegexSource: ~/^(PR-.+|.*fightwarn.*|.*Windows.*)$/, + branchRegexTarget: ~/fightwarn|Windows-.*/, appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, - 'getParStages': { dynamatrix, Closure body -> + 'getParStages': { def dynamatrix, Closure body -> return dynamatrix.generateBuild([ requiredNodelabels: [], excludedNodelabels: [], @@ -1191,12 +1273,58 @@ set | sort -n """ excludeCombos: [ dynacfgPipeline.axisCombos_ARCH32x64, dynacfgPipeline.axisCombos_ARCH64x32, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_WINDOWS_CROSS, dynacfgPipeline.axisCombos_NOT_WINDOWS ] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration + + ,[name: 'Strict C and GNU standard builds on cross-Windows platforms (Linux+mingw), without distcheck and docs (allowed to fail)', + disabled: dynacfgPipeline.disableSlowBuildCIBuild, + //branchRegexSource: ~/^(PR-.+|.*fightwarn.*|.*Windows.*)$/, + //branchRegexTarget: ~/fightwarn|Windows-.*/, + appliesToChangedFilesRegex: dynacfgPipeline.appliesToChangedFilesRegex_C, + 'getParStages': { def dynamatrix, Closure body -> + return dynamatrix.generateBuild([ + commonLabelExpr: "cross-windows-nut-builder", + dynamatrixAxesLabels: ['OS_FAMILY', 'OS_DISTRO', 'ARCH${ARCH_BITS}', 'COMPILER'], + requiredNodelabels: ["NUT_BUILD_CAPS=cross-windows-mingw"], + excludedNodelabels: [], + + dynamatrixAxesVirtualLabelsMap: [ + 'BITS': [64, 32], + 'CSTDVERSION_${KEY}': [ ['c': '99', 'cxx': '11'] ], + 'CSTDVARIANT': ['gnu'] + (dynacfgPipeline.disableStrictCIBuild_CrossWindows ? [] : ['c']), + ], + dynamatrixAxesCommonEnv: [ + ['LANG=C','LC_ALL=C','TZ=UTC', + 'BUILD_TYPE=cross-windows-mingw', + // Note: warnings options are currently ignored in ci_build.sh + // for this BUILD_TYPE (technically in build-mingw-nut.sh) + 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=hard' + ] + ], + allowedFailure: [ + dynacfgPipeline.axisCombos_STRICT_C, + //dynacfgPipeline.axisCombos_WINDOWS_CROSS, + //[~/BUILD_WARNOPT=hard/] + ], + 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.axisCombos_ARCH32x64, + dynacfgPipeline.axisCombos_ARCH64x32, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_WINDOWS, + dynacfgPipeline.axisCombos_NOT_WINDOWS_CROSS + ] + ], body) + }, // getParStages + 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build + ] // one slowBuild filter configuration ] dynacfgPipeline.notifyHandler = { @@ -1227,14 +1355,23 @@ def stageNameFunc_ShellcheckCustom(DynamatrixSingleBuildConfig dsbc) { /////////////////////////////////////////////////////////////////////////// - // Hacky big switch for a max debug option - if (true) - if (false) - { +// Hacky big switch for a max debug option +//if (true) // <<< (Un-)comment away in select runs/branches +//if (false) // <<< (Un-)comment away in select runs/branches +if ( env?.BRANCH_NAME ==~ /.*verbose.*/ ) +{ dynamatrixGlobalState.enableDebugTrace = true dynamatrixGlobalState.enableDebugErrors = true dynamatrixGlobalState.enableDebugMilestones = true dynamatrixGlobalState.enableDebugMilestonesDetails = true - } + dynamatrixGlobalState.enableDebugTraceGithubStatusHighlights = true +} + +//if (true) // <<< (Un-)comment away in select runs/branches +//if (false) // <<< (Un-)comment away in select runs/branches +if ( env?.BRANCH_NAME ==~ /.*fightwarn.*/ ) +{ + dynamatrixGlobalState.enableDebugTraceGithubStatusHighlights = true +} dynamatrixPipeline(dynacfgBase, dynacfgPipeline) diff --git a/LICENSE-DCO b/LICENSE-DCO new file mode 100644 index 0000000000..49b8cb0549 --- /dev/null +++ b/LICENSE-DCO @@ -0,0 +1,34 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/MAINTAINERS b/MAINTAINERS index b2fcaf8ae6..87b03d11c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -60,6 +60,10 @@ P: Arnaud Quette M: aquette@debian.org S: Maintained: Official Debian packages +P: Greg Troxel +M: gdt@lexort.com +S: Maintained: pkgsrc (sysutils/ups-nut) + Everything else =============== diff --git a/Makefile.am b/Makefile.am index 7a66ac7dfd..51fd2b5065 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,14 @@ # top-level Makefile for NUT +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + # include directory for aclocal ACLOCAL_AMFLAGS = -I m4 @@ -8,25 +17,51 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = include common clients conf data docs drivers tools \ lib scripts server tests +bindir = @bindir@ +sbindir = @sbindir@ +driverexecdir = @driverexecdir@ +cgiexecdir = @cgiexecdir@ + # Automatically update the libtool script if it becomes out-of-date # See https://www.gnu.org/software/libtool/manual/html_node/LT_005fINIT.html LIBTOOL_DEPS = @LIBTOOL_DEPS@ libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status libtool -# COPYING is included automatically. -EXTRA_DIST = INSTALL.nut LICENSE-GPL2 LICENSE-GPL3 MAINTAINERS UPGRADING +# COPYING and other autotools-standard files are included automatically +# by automake. Note that the INSTALL file is (re-)imposed by autotools +# runs and is essentially a manual on configure script general usage, so +# NUT's actual installation notes have had to use a different filename. +EXTRA_DIST = LICENSE-GPL2 LICENSE-GPL3 LICENSE-DCO MAINTAINERS + +# Since the renaming of documentation to `*.adoc` extension to help IDE +# and GitHub UIs to render the source files in a pretty fashion, we need +# to list them: +EXTRA_DIST += INSTALL.nut.adoc UPGRADING.adoc TODO.adoc NEWS.adoc README.adoc + +# Tarballs created by `make dist` include the `configure.ac` and `m4/*` sources +# but lack NUT magic logic to recreate the `configure` script if someone would +# want to adapt it to their autotools or locally fix a tarball-based build. +EXTRA_DIST += autogen.sh + +if KEEP_NUT_REPORT +nodist_data_DATA = config.nut_report_feature.log +endif # ---------------------------------------------------------------------- # flags to pass to ./configure when calling "make distcheck" and "make # distcheck-light". Try to check as many features as possible! Also -# need to give augeas-lenses-dir, hotplug-dir and udev-dir, so that -# staged install does not fail. - -DISTCHECK_FLAGS = --with-all --with-ssl --with-doc=auto -DISTCHECK_LIGHT_FLAGS = --with-all=auto --with-ssl=auto --with-doc=auto -DISTCHECK_LIGHT_MAN_FLAGS = --with-all=auto --with-ssl=auto --with-doc=man -DISTCHECK_VALGRIND_FLAGS = --with-all=auto --with-ssl=auto --with-doc=skip --with-valgrind CXXFLAGS='$(CXXFLAGS) -g' CFLAGS='$(CFLAGS) -g' +# need to give augeas-lenses-dir, hotplug-dir and udev-dir, and request +# PyNUT to be installed near the NUT-Monitor app (if feasible) so that +# staged install does not fail. Note that by default PyNUT tries to go +# into the system Python site-packages location, and autotools does not +# tweak paths not using ${prefix} so `make distcheck` fails for it as +# it does not play with a `DESTDIR` either. + +DISTCHECK_FLAGS = --with-all --with-ssl --with-doc=auto --with-pynut=app --with-nut_monitor=force +DISTCHECK_LIGHT_FLAGS = --with-all=auto --with-ssl=auto --with-doc=auto --with-pynut=app --with-nut_monitor=force +DISTCHECK_LIGHT_MAN_FLAGS = --with-all=auto --with-ssl=auto --with-doc=man --with-pynut=app --with-nut_monitor=force +DISTCHECK_VALGRIND_FLAGS = --with-all=auto --with-ssl=auto --with-doc=skip --with-valgrind CXXFLAGS='$(CXXFLAGS) -g' CFLAGS='$(CFLAGS) -g' --with-pynut=app --with-nut_monitor=force # The checks for DMF builds involve several distinct combinations of options # that the recipes should accept as valid. We generally skip items tested @@ -46,6 +81,7 @@ DISTCHECK_DMF_ALL = $(DISTCHECK_DMF_WITH_REGEN_YES_FLAGS) --with-snmp_dmf_lua=ye # Also have a way to test legacy code structure with no DMF support built in DISTCHECK_DMF_NO = --with-all=auto --with-dmf=no --with-doc=skip --with-dev=yes -C +# Note: this rule uses envvar DISTCHECK_FLAGS expanded at run-time DISTCHECK_CONFIGURE_FLAGS = ${DISTCHECK_FLAGS} \ --with-systemdsystemunitdir='$${prefix}/lib/systemd/system' \ --with-systemdshutdowndir='$${prefix}/lib/systemd/system-shutdown' \ @@ -53,81 +89,82 @@ DISTCHECK_CONFIGURE_FLAGS = ${DISTCHECK_FLAGS} \ --with-augeas-lenses-dir='$${prefix}/usr/share/augeas/lenses' \ --with-hotplug-dir='$${prefix}/etc/hotplug' \ --with-udev-dir='$${prefix}/etc/udev' \ - --with-devd-dir='$${prefix}/etc/devd' + --with-devd-dir='$${prefix}/etc/devd' \ + --with-pynut=app --with-nut_monitor=force distcheck-light: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_LIGHT_FLAGS)" distcheck + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_LIGHT_FLAGS)" distcheck distcheck-light-man: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_LIGHT_MAN_FLAGS)" distcheck + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_LIGHT_MAN_FLAGS)" distcheck # Make a distcheck (and check in particular) with enabled valgrind and debug info memcheck distcheck-valgrind: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_VALGRIND_FLAGS)" distcheck + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_VALGRIND_FLAGS)" distcheck distcheck-dmf-features-REGEN_NO: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_REGEN_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_REGEN_NO_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_REGEN_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_REGEN_NO_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-LTDL_YES: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LTDL_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LTDL_YES_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LTDL_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LTDL_YES_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-LTDL_NO: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LTDL_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LTDL_NO_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LTDL_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LTDL_NO_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-LUA_YES: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_YES_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_YES_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-LUA_YESNO: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_YESNO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_YESNO_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_YESNO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_YESNO_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-LUA_NO: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_NO_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_LUA_NO_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_LUA_NO_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-features-REGEN_YES: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_REGEN_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_REGEN_YES_FLAGS" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_WITH_REGEN_YES_FLAGS)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_WITH_REGEN_YES_FLAGS" >&2; exit $$RES; } echo "PASSED $@ suite!" # Intentionally done as one rule, to avoid parallelization and conflicts distcheck-dmf-features: - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-REGEN_NO - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LTDL_YES - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LTDL_NO - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_YES - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_YESNO - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_NO - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-REGEN_YES + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-REGEN_NO + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LTDL_YES + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LTDL_NO + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_YES + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_YESNO + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-LUA_NO + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features-REGEN_YES echo "PASSED $@ suite!" # Just test the common useful case of enabling (requiring) everything # related to DMF at once distcheck-dmf-all-yes: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_ALL)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_ALL" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_ALL)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_ALL" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-no: - $(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_NO)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_NO" >&2; exit $$RES; } + +$(MAKE) $(AM_MAKEFLAGS) DISTCHECK_FLAGS="$(DISTCHECK_DMF_NO)" distcheck || { RES=$$? ; echo "FAILED with DISTCHECK_DMF_NO" >&2; exit $$RES; } echo "PASSED $@ suite!" distcheck-dmf-warnings: - $(MAKE) $(AM_MAKEFLAGS) CFLAGS="$(CFLAGS) -Werror -Wall" DISTCHECK_FLAGS="$(DISTCHECK_DMF_ALL)" distcheck + +$(MAKE) $(AM_MAKEFLAGS) CFLAGS="$(CFLAGS) -Werror -Wall" DISTCHECK_FLAGS="$(DISTCHECK_DMF_ALL)" distcheck echo "PASSED $@ suite!" distcheck-dmf: - $(MAKE) $(AM_MAKEFLAGS) distcheck-light-man - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features - $(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-warnings + +$(MAKE) $(AM_MAKEFLAGS) distcheck-light-man + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-features + +$(MAKE) $(AM_MAKEFLAGS) distcheck-dmf-warnings echo "PASSED $@ suite!" # Shortcut for DMF development - only checks its specifics after building NUT # The extent of actions and tests called below depends on project configuration check-dmf: all - cd $(top_srcdir)/scripts/DMF && $(MAKE) check + +cd $(top_srcdir)/scripts/DMF && $(MAKE) check # workaround the dist generated files that are also part of the distribution # Note that distcleancheck is disabled for now, while waiting for a proper @@ -141,7 +178,7 @@ distcleancheck: realclean: maintainer-clean # Files made by our targets: -CLEANFILES = *-spellchecked cppcheck*.xml +CLEANFILES = *-spellchecked *.adoc-parsed cppcheck*.xml DISTCLEANFILES = ChangeLog # Most of the files generated by custom rules in the configure script @@ -161,13 +198,13 @@ DISTCLEANFILES += include/config.h.in~ MAINTAINERCLEANFILES = INSTALL MAINTAINERCLEANFILES += aclocal.m4 config.guess config.sub MAINTAINERCLEANFILES += configure -MAINTAINERCLEANFILES += depcomp install-sh ltmain.sh test-driver +MAINTAINERCLEANFILES += depcomp install-sh ltmain.sh test-driver ar-lib MAINTAINERCLEANFILES += m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 MAINTAINERCLEANFILES += Makefile.in .dirstamp include/config.h.in # Executed after default rules maintainer-clean-local: - rm -f missing || true + $(AM_V_at)rm -f missing || true # Do not let $SUBDIRS/Makefile rules delete their local .deps because # this breaks our ability to clean up (e.g. some common/.../*.Plo files @@ -175,27 +212,108 @@ maintainer-clean-local: # should be available during their clean-up). Just in case, we make sure # here that their sub-distcleans complete first. distclean-local: - @for DIR in $(SUBDIRS) ; do \ + +@for DIR in $(SUBDIRS) ; do \ if test -f "$${DIR}/Makefile" ; then \ echo " DISTCLEAN in $${DIR}" >&2 ; \ - ( cd "$${DIR}" && $(MAKE) -s distclean ) || exit ; \ + ( cd "$${DIR}" && $(MAKE) $(AM_MAKEFLAGS) -s distclean ) || exit ; \ fi ; \ done - rm -rf .inst tmp autom4te.cache - find "$(builddir)" -type d -name '.deps' | while read DIR ; do rm -rf "$${DIR}" ; done + $(AM_V_at)rm -rf .inst tmp autom4te.cache + $(AM_V_at)find "$(builddir)" -type d -name '.deps' | while read DIR ; do rm -rf "$${DIR}" ; done # Hook the documentation building and validating recipes # Note: these are optionally available (as determined during configure runs) +# Maint: grep -l 'SPELLCHECK_' `git grep -lw spellcheck '*.am'` spellcheck spellcheck-interactive: - @RES=0; \ - (cd $(builddir)/docs && $(MAKE) -s $@) || RES=$$? ; \ - (cd $(builddir)/docs/man && $(MAKE) -s $@) || RES=$$? ; \ - (cd $(builddir)/conf && $(MAKE) -s $@) || RES=$$? ; \ - (cd $(builddir)/data && $(MAKE) -s $@) || RES=$$? ; \ + +@RES=0; \ + (cd $(builddir)/docs && $(MAKE) $(AM_MAKEFLAGS) -s $(abs_top_builddir)/docs/.prep-src-docs) || RES=$$? ; \ + (cd $(builddir)/docs/man && $(MAKE) $(AM_MAKEFLAGS) -s $(abs_top_builddir)/docs/.prep-src-docs) || RES=$$? ; \ + (cd $(builddir)/docs && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/docs/man && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/conf && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/data && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/data/html && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/Solaris && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/Windows && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/devd && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/hotplug && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/installer && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/python && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/systemd && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/udev && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/scripts/upsdrvsvcctl && $(MAKE) $(AM_MAKEFLAGS) -s $@) || RES=$$? ; \ + (cd $(builddir)/tests/NIT && $(MAKE) $(AM_MAKEFLAGS) -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) $(AM_MAKEFLAGS) $@ + +INSTALL.nut UPGRADING NEWS README: + +cd $(builddir)/docs && $(MAKE) $(AM_MAKEFLAGS) ../$(@F).adoc-parsed && cp -f ../$(@F).adoc-parsed ../$(@F) + +# Workarounds for https://github.com/github/markup/issues/1095 +# require direct definition of our attributes in each source +# document, in order for GitHub Web-UI to render them nicely +# (unfortunately, asciidoc configs and includes are not handled +# at this time). Hopefully this will go away at some point. +# The following rule updates definitions in source asciidoc files +# between GH_MARKUP_1095_INCLUDE_BEGIN/END tags with contents of +# current docs/asciidoc-vars.conf file. It is intended to be used +# by maintainers (or brave contributors who would dare edit those +# definitions), to apply them into the committed document sources. +# Not bothering about with "make dist" constraints etc. - changes +# the contents of srcdir directly and intentionally. +maintainer-asciidocs: + @USEDREV="`git log -1 --oneline --pretty=format:'%h (%cs) %s' docs/asciidoc-vars.conf`" || exit ; \ + USEDREV_NOSUBJ="`git log -1 --oneline --pretty=format:'%h (%cs)' docs/asciidoc-vars.conf`" || exit ; \ + echo "$@: Updating asciidoc text sources with docs/asciidoc-vars.conf as of commit: $${USEDREV}"; \ + echo "//GH_MARKUP_1095_INCLUDE_BEGIN//$${USEDREV}" > docs/asciidoc-vars.conf.lastrev.tmp || exit ; \ + find . -name '*.adoc' -or -name '*.txt' | ( \ + FILES=""; \ + while read F ; do \ + grep -E '^//+GH_MARKUP_1095_INCLUDE_(BEGIN|END)' "$$F" >/dev/null \ + || { echo "$@: SKIP: no GH_MARKUP_1095_INCLUDE_* tags: $$F"; continue ; } ; \ + rm -f "$${F}"*.tmp || exit ; \ + EXT="1.tmp"; \ + while IFS='' read LINE ; do \ + case "$${LINE}" in \ + "//GH_MARKUP_1095_INCLUDE_BEGIN"*) EXT="2.tmp" ; continue ;; \ + "//GH_MARKUP_1095_INCLUDE_END"*|"////GH_MARKUP_1095_INCLUDE_END"*) EXT="3.tmp" ; continue ;; \ + esac ; \ + printf '%s\n' "$${LINE}" >> "$${F}.$${EXT}" || exit ; \ + done < "$$F" || { echo "$@: FAILED injection for $${F}" >&2; exit 1; } ; \ + if test -s "$${F}.2.tmp" && test -z "`diff "$${F}.2.tmp" docs/asciidoc-vars.conf | tr -d '\n'`" ; then \ + rm -f "$${F}"*.tmp ; \ + echo "$@: SKIP: no changes: $$F"; continue ; \ + fi; \ + cat "$${F}.1.tmp" docs/asciidoc-vars.conf.lastrev.tmp docs/asciidoc-vars.conf > "$${F}.tmp" \ + && echo '//GH_MARKUP_1095_INCLUDE_END//' >> "$${F}.tmp" \ + && cat "$${F}.3.tmp" >> "$${F}.tmp" \ + && mv -f "$${F}.tmp" "$${F}" \ + || { echo "$@: FAILED injection for $${F}" >&2; exit 1; } ; \ + echo "$@: UPDATED: $$F"; \ + FILES="$${FILES} $${F}"; \ + rm -f "$${F}"*.tmp ; \ + done; \ + rm -f docs/asciidoc-vars.conf.lastrev.tmp; \ + if test -z "$${FILES}" ; then \ + echo "$@: OVERALL-SKIP: No text files found with GH_MARKUP_1095_INCLUDE_ tags, or obsoleted docs/asciidoc-vars.conf contents";\ + else \ + echo "$@: OVERALL-UPDATED: You may now want to:"; \ + echo " git add -p $${FILES} && git commit -sm 'Update NUT documentation sources with current docs/asciidoc-vars.conf: $${USEDREV_NOSUBJ}'"; \ + fi; \ + ) + +check-NIT check-NIT-devel: + +cd $(builddir)/tests/NIT && $(MAKE) $(AM_MAKEFLAGS) $@ # This target adds syntax-checking for committed shell script files, # to avoid surprises and delays in finding fatal typos after packaging @@ -268,10 +386,16 @@ cppcheck: @echo "CPPCHECK analysis not available since 'cppcheck' was not found." endif !HAVE_CPPCHECK +sockdebug: + +cd $(builddir)/server && $(MAKE) $(AM_MAKEFLAGS) sockdebug$(EXEEXT) + # ---------------------------------------------------------------------- # Automatically generate the ChangeLog from Git logs: MAINTAINERCLEANFILES += ChangeLog +# CI builds can leave a log of selected features: +MAINTAINERCLEANFILES += config.nut_report_feature.log* + # Older boundary of the ChangeLog commits range # It can be a tag ('v2.2.0'), a commit hash, a date, ... # See gitrevisions for more information on specifying ranges @@ -281,26 +405,51 @@ GITLOG_START_POINT=master # Force ChangeLog regeneration upon make dist (due to nonexistant 'dummy-stamp'), # in case it has already been generated previously -# Note that the script is hard-coded to generate "ChangeLog" in the current dir +# Note that the script is hard-coded to inspect Git workspace which contains +# the current dir, and defaults to generate a "ChangeLog" in the current dir. +# The script itself is generated from a template, so resides in builddir. dummy-stamp: -ChangeLog: tools/gitlog2changelog.py dummy-stamp - cd $(top_builddir) && \ - ./tools/gitlog2changelog.py $(GITLOG_START_POINT) || \ - { echo "gitlog2changelog.py failed to generate the ChangeLog. See https://github.com/networkupstools/nut/commits/master" > $@ ; } +ChangeLog: dummy-stamp + +@$(MAKE) $(AM_MAKEFLAGS) $(abs_top_builddir)/ChangeLog + +# Be sure to not confuse with a DIST'ed file (and so try to overwrite it): +$(abs_top_builddir)/ChangeLog: tools/gitlog2changelog.py dummy-stamp + @cd $(abs_top_srcdir) && \ + if test -e .git ; then \ + CHANGELOG_FILE="$@" $(abs_top_builddir)/tools/gitlog2changelog.py $(GITLOG_START_POINT) || \ + { printf "gitlog2changelog.py failed to generate the ChangeLog.\n\nNOTE: See https://github.com/networkupstools/nut/commits/master for change history.\n\n" > "$@" ; } ; \ + else \ + if test x"$(abs_top_srcdir)" != x"$(abs_top_builddir)" -a -s ./ChangeLog ; then \ + echo "Using distributed ChangeLog file from sources" >&2 ; \ + rm -f "$@" || true ; \ + cat ./ChangeLog > "$@" ; \ + else \ + if ! test -s "$@" ; then \ + printf "Failed to generate the ChangeLog.\n\nNOTE: See https://github.com/networkupstools/nut/commits/master for change history.\n\n" > "$@" ; \ + fi ; \ + fi ; \ + fi + +ChangeLog.adoc: ChangeLog + +cd $(abs_top_builddir)/docs && $(MAKE) $(AM_MAKEFLAGS) ../ChangeLog.adoc nut_version.h include/nut_version.h: - cd $(abs_top_builddir)/include && $(MAKE) nut_version.h + +cd $(abs_top_builddir)/include && $(MAKE) $(AM_MAKEFLAGS) nut_version.h tools/gitlog2changelog.py: tools/gitlog2changelog.py.in - cd $(@D) && $(MAKE) -s $(@F) + +cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) -s $(@F) # ---------------------------------------------------------------------- # 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 @@ -314,35 +463,35 @@ build: @echo $(WARN) @echo "Warning: 'make build' is deprecated. Use 'make all' instead." @echo $(WARN) - $(MAKE) $(AM_MAKEFLAGS) all + +$(MAKE) $(AM_MAKEFLAGS) all install-bin: @echo $(WARN) @echo "Warning: 'make install-bin' is deprecated." @echo "Use 'make install-exec' instead for a similar effect." @echo $(WARN) - cd common; $(MAKE) $(AM_MAKEFLAGS) install - cd drivers; $(MAKE) $(AM_MAKEFLAGS) install - cd server; $(MAKE) $(AM_MAKEFLAGS) install - cd clients; $(MAKE) $(AM_MAKEFLAGS) install + +cd common; $(MAKE) $(AM_MAKEFLAGS) install + +cd drivers; $(MAKE) $(AM_MAKEFLAGS) install + +cd server; $(MAKE) $(AM_MAKEFLAGS) install + +cd clients; $(MAKE) $(AM_MAKEFLAGS) install install-man: install-data-recursive @echo $(WARN) @echo "Warning: 'make install-man' is deprecated." @echo "Use 'cd man; make install' instead." @echo $(WARN) - cd man; $(MAKE) $(AM_MAKEFLAGS) install + +cd man; $(MAKE) $(AM_MAKEFLAGS) install install-conf: @echo $(WARN) @echo "Warning: 'make install-conf' is deprecated." @echo "Use 'cd conf; make install' instead." @echo $(WARN) - cd conf; $(MAKE) $(AM_MAKEFLAGS) install + +cd conf; $(MAKE) $(AM_MAKEFLAGS) install # The target install-data already has a standardized meaning under automake install-dirs: @echo $(WARN) @echo "Warning: 'make install-dirs' is deprecated." @echo "Use 'make installdirs' instead." @echo $(WARN) - $(MAKE) installdirs + +$(MAKE) installdirs cgi build-cgi install-cgi install-cgi-dir install-cgi-bin \ install-cgi-man install-cgi-conf install-cgi-html: @echo "Error: 'make $@' no longer exists." @@ -376,8 +525,8 @@ MAINTAINERCLEANFILES_PACKAGES += *.p5p MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_DISTBALL) MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_PACKAGES) -package: - DESTDIR="$(abs_builddir)/_install_pkgprotodir" ; export DESTDIR; \ +package: dist + +DESTDIR="$(abs_builddir)/_install_pkgprotodir" ; export DESTDIR; \ rm -rf "$$DESTDIR"; \ case "`uname -s`" in \ "HP-UX") \ @@ -403,6 +552,88 @@ package: *) echo "Unsupported OS for 'make $@' (no recipe bound)" >&2; exit 1;; \ esac +if HAVE_WINDOWS +# Steam-roll over all executables/libs we have placed in DESTDIR and copy over +# any resolved dependencies from the cross-build (or native MSYS2) environment. +# Then hardlink libraries for sbin... (alternative: all bins in one dir) +# TOTHINK: Are there more dirs to consider? So far we cover bindir, sbindir and +# driverexecdir (e.g. some Linux distros place drivers to /lib/nut while tools +# and daemons are in /usr/bin and /usr/sbin), and cgiexecdir; anything else?.. +# Note we hold existance of cgiexecdir as optional, but the name is expected to +# be defined. Other dirs are "just assumed" to exist (that we are not packaging +# some NUT build without drivers/tools/daemons). Subject to change if needed. +# Currently this is handled by a CHECKING... step that should fail if it hits +# anything. +install-win-bundle: all + @if test -z "$(DESTDIR)" ; then echo "ERROR: '$@': Bundle may only be installed to some DESTDIR prototype area'" >&2 ; exit 1; fi + +$(MAKE) $(AM_MAKEFLAGS) DESTDIR='$(DESTDIR)' install + +$(MAKE) $(AM_MAKEFLAGS) DESTDIR='$(DESTDIR)' install-win-bundle-thirdparty + +install-win-bundle-thirdparty: + @if test -z "$(DESTDIR)" ; then echo "ERROR: '$@': Bundle may only be installed to some DESTDIR prototype area'" >&2 ; exit 1; fi + @echo "Searching which DLLs need to be bundled with NUT for Windows..." >&2 + @if test -z "$$ARCH" ; then \ + if test -n "$(target)" ; then \ + ARCH='$(target)' \ + ; else \ + if test -n "$(target_triplet)" ; then ARCH='$(target_triplet)' ; fi ; \ + fi ; \ + fi ; \ + if test -n "$$ARCH" ; then export ARCH ; fi ; \ + DESTDIR='$(DESTDIR)' ; export DESTDIR ; \ + ( cd '$(DESTDIR)' || exit ; \ + DESTDIR="" '$(abs_top_srcdir)/scripts/Windows/dllldd.sh' dllldddir . \ + | while read D ; do \ + echo " DLL->bin $$D" 2>&1 ; \ + cp -pf "$$D" './$(bindir)/' ; \ + done ; \ + ) || exit ; \ + ( if test x"$(bindir)" = x"$(sbindir)" ; then exit 0 ; fi ; \ + cd '$(DESTDIR)/$(sbindir)' || exit ; \ + '$(abs_top_srcdir)/scripts/Windows/dllldd.sh' dllldddir . \ + | while read D ; do \ + echo " DLL->sbin $$D" 2>&1 ; \ + ln -f '$(DESTDIR)/$(bindir)'/"`basename "$$D"`" ./ ; \ + done ; \ + ) || exit ; \ + ( if test x"$(driverexecdir)" = x"$(bindir)" ; then exit 0 ; fi ; \ + if test x"$(driverexecdir)" = x"$(sbindir)" ; then exit 0 ; fi ; \ + cd '$(DESTDIR)/$(driverexecdir)' || exit ; \ + '$(abs_top_srcdir)/scripts/Windows/dllldd.sh' dllldddir . \ + | while read D ; do \ + echo " DLL->drv $$D" 2>&1 ; \ + ln -f '$(DESTDIR)/$(bindir)'/"`basename "$$D"`" ./ ; \ + done ; \ + ) || exit ; \ + ( if test -z "$(cgiexecdir)" -o ! -d "$(DESTDIR)/$(cgiexecdir)" ; then exit 0 ; fi ; \ + if test x"$(cgiexecdir)" = x"$(bindir)" ; then exit 0 ; fi ; \ + if test x"$(cgiexecdir)" = x"$(sbindir)" ; then exit 0 ; fi ; \ + if test x"$(driverexecdir)" = x"$(cgiexecdir)" ; then exit 0 ; fi ; \ + cd '$(DESTDIR)/$(cgiexecdir)' || exit ; \ + '$(abs_top_srcdir)/scripts/Windows/dllldd.sh' dllldddir . \ + | while read D ; do \ + echo " DLL->cgi $$D" 2>&1 ; \ + ln -f '$(DESTDIR)/$(bindir)'/"`basename "$$D"`" ./ ; \ + done ; \ + ) || exit + @echo "CHECKING if any executable files were installed to locations other than those covered by this recipe, so might not have needed DLLs bundled near them" >&2 ; \ + relbindir="`echo './$(bindir)/' | sed 's,//*,/,g'`" ; \ + relsbindir="`echo './$(sbindir)/' | sed 's,//*,/,g'`" ; \ + reldriverexecdir="`echo './$(driverexecdir)/' | sed 's,//*,/,g'`" ; \ + relcgiexecdir="`echo './$(cgiexecdir)/' | sed 's,//*,/,g'`" ; \ + cd '$(DESTDIR)' || exit ; \ + find . -type f | grep -Ei '\.(exe|dll)$$' \ + | grep -vE "^($${relbindir}|$${relsbindir}|$${reldriverexecdir}|$${relcgiexecdir})" \ + | ( RES=0 ; while IFS= read LINE ; do echo "$$LINE" ; RES=1; done; exit $$RES ) + +else +install-win-bundle: + @echo "SKIP: '$@' not enabled for current build configuration" + +install-win-bundle-thirdparty: + @echo "SKIP: '$@' not enabled for current build configuration" +endif + print-MAINTAINERCLEANFILES print-REALCLEANFILES: @echo $(MAINTAINERCLEANFILES) diff --git a/NEWS b/NEWS.adoc similarity index 54% rename from NEWS rename to NEWS.adoc index d7aad9d3b6..bb940c62ef 100644 --- a/NEWS +++ b/NEWS.adoc @@ -1,11 +1,754 @@ -If you're upgrading from an earlier version, see the UPGRADING file. +ifdef::txt[] +NUT Release Notes +================= +endif::txt[] + +If you're upgrading from an earlier version, see the link:UPGRADING.adoc[] file. + +Please note that web and source document links, product and service names +listed in historic entries of past releases may no longer be relevant. For a complete and more detailed list of changes, please refer to the 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: + +PLANNED: Release notes for NUT 2.8.4 - what's new since 2.8.3 +------------------------------------------------------------- + +https://github.com/networkupstools/nut/milestone/9 + + - (expected) Dynamic Mapping Files (DMF) feature supported, to allow + the driver binaries to be built once and data mappings to be loaded + and modernized on the fly [Ported from 42ITy project] + + +PLANNED: Release notes for NUT 2.8.3 - what's new since 2.8.2 +------------------------------------------------------------- + +https://github.com/networkupstools/nut/milestone/11 + + - (expected) clean-up of libusb API variants support [#300 and follow-ups] + + - (expected) CI automation for coding style + + - (expected) CI automation for use of data points in drivers that conform + to patterns defined in link:docs/nut-names.txt[] + + - (expected) Porting of performance and bug fixes from 42ITy project + + - (expected) Bug fixes for fallout possible due to "fightwarn" effort in 2.8.0 + + +PLANNED: Release notes for NUT 2.8.2 - what's new since 2.8.1 +------------------------------------------------------------- + +https://github.com/networkupstools/nut/milestone/10 + + - Fix fallout of development in NUT v2.8.0 and/or v2.8.1: + * dstate machinery: a segmentation fault (null pointer dereference) was + possible with `INSTCMD` processing of commands without parameters nor + `TRACKING` identifier. [#2155] + * USB bus number detection for libusb-1.0 builds was overly zealous and + wrongly considered zero values as an error. [#2198] + * `upsmon` recognition of `CAL` state could linger after the calibration + activity was completed by the hardware, which led to mis-processing of + shutdown triggers. Also, notification was added to report "finished + calibration". [issue #2168, PR #2169] + * `upsmon` recognition of `OFF` state as a trigger for FSD (forced shut + down) criticality considered also the input line state, which may be + an independently evolving circumstance. [issue #2278, PR #2279] + * `upsmon` support for `POLLFAIL_LOG_THROTTLE_MAX` did not neuter the + applied setting when live-reloading configuration, so commenting it + away in `upsmon.conf` did not have the effect of resetting the logging + frequency to default. It also did not reset the counters to certainly + follow the new configuration for existing faults. [issue #2207, PR #2209] + * `upsmon` support for `POLLFAIL_LOG_THROTTLE_MAX` had an off-by-one error + (e.g. reporting "Data stale" or "Driver not connected" every 30 sec with + `POLLFAIL_LOG_THROTTLE_MAX 5` and `POLLFREQ 5` settings). [#2207] + * Drivers running with non-default user account (e.g. with `user=root` + in their configuration) failed to apply group ownership and permissions + to their Unix socket file for interaction with the local data server. + [#2185, #2096] + * Dispatcher script `scripts/python/app/NUT-Monitor` referenced `py3qt3` + instead of the correct `py3qt5`. It also tries to check both `py2gtk2` + and `py3qt5` implementations verbosely, even if one is not installed. + [#2199, #2201] + * Set the `DesktopFileName` in `scripts/python/app/NUT-Monitor-py3qt5`, + this binds the application with the desktop file and allow the Open + Desktop compatible implementation to display the proper icon and + application name. [#2205] + * Original recipe for `apc_modbus` strictly required USB support even if + building NUT without it. [#2262] + * Builds requested with a specific C/C++ language standard revision via + `CFLAGS` and `CXXFLAGS` should again be honoured. [PR #2306] + * Allow requesting detailed debug builds (with disabled optimizations for + binaries to best match the source code) for supported compilers using + `configure` script option `--with-debuginfo`. Note that default autoconf + behavior usually embeds moderate optimizations and debug information on + its own. [PR #2310] + + - nut-usbinfo.pl, nut-scanner and libnutscan: + * Library API version for `libnutscan` was bumped from 2.2.0 to 2.5.0 + during evolution of this NUT release. + * USB VendorID:ProductID support list files generated by the script for + different OS frameworks now include a comment with other possibly + compatible driver names, where the respective file format allows for + comments. + * Added the concept of `alt_driver_names` in `nutscan_device_t` structure + for ability to suggest a comment with other possibly compatible driver + names in configuration snippets generated by `nut-scanner`; practical + support implemented for USB connected drivers. + * Added the concept of commented-away suggested option values `comment_tag` + and a method to `nutscan_add_commented_option_to_device()`, instead of + hacks in prepared config data which broke some use-cases. [#2221] + * Command-line option `-U` for USB scan can now be specified several times + to increase the detail level about hardware link to the device (this was + previously always suggested, but may be not reliable if USB enumeration + gets changed over time). [#2221] + * Added generation of FreeBSD/pfSense quirks for USB devices supported + by NUT (may get installed to `$datadir` e.g. `/usr/local/share/nut` + and need to be pasted into your `/boot/loader.conf.local`). [#2159] + * nut-scanner now avoids creating ambiguous `nutdevN` device section names + when called separately to scan different media buses (one at a time). + Now the "bus" name would be embedded (e.g. non-colliding `nutdev-usb1` + and `nutdev-snmp1`). [#2247] + * nut-scanner can now discover NUT simulated devices (`.dev` and `.seq` + files) located in your sysconfig directory, and prepare configuration + sections with the simulation driver (currently `dummy-ups`). [#2246] + * nut-scanner now reports `dummy-ups` as driver when scanning NUT "bus" + with Old or Avahi method. [#2236, #2245] + + - upsd: Fixed conditions for "no listening interface available" diagnosis + to check how many listeners we succeeded with, not whether the first one + succeeded or not. If not all requested (non-localhost) listeners were + available, default to fail the daemon start-up attempt; support for an + `ALLOW_NOT_ALL_LISTENERS` setting was added to control this behavior. [#723] + + - NUT CI improvements: + * Added publishing recipes for PyNUT client bindings for NUT, so it ends + up in the link:https://pypi.org/project/PyNUTClient[PyPI repository]. + [#2158] + * Added support for new `ccache` namespace concept, where possible. [#2256] + * Fixed an issue for builds configured `--without-usb`. [#2263] + * Added a fallback for `libgd` discovery (for CGI etc. builds). [#2287] + * Made `aspell` TeX module detection more reliable. [#2206] + * Fixed recipes for completely out-of-tree builds to pass with documentation + generation and checking on all tested "make" implementations. [#2318] + * Various other recipe and documentation clean-up efforts. [#2284, #2269, + #2261] + + - main driver core codebase: + * Help users of drivers that can be built to support optionally USB and + other media (like `nutdrv_qx` built for serial-only support), and built + in fact without USB support but used for USB devices, with some more + information to make troubleshooting easier. [issue #2259, PR #2260] + * Driver programs with debug tracing support via `-D` CLI option and/or + the `NUT_DEBUG_LEVEL` environment variable now check those earlier in + their life-time, so that initialization routine can be debugged. [#2259] + * Multiple USB-capable drivers got options to customize `usb_config_index` + `usb_hid_rep_index`, `usb_hid_desc_index`, `usb_hid_ep_in` and + `usb_hid_ep_out` hardware connection settings via `ups.conf` options. + This is treated as experimental, not all code paths may be actually + using such values from `struct usb_communication_subdriver_t` rather + than hard-coded defaults. Discovery of correct values is up to the + user at the moment (using `lsusb`, internet search, luck...) [#2149] + + - nut-driver-enumerator (NDE) service/script: + * The optional daemon mode (primarily useful for systems which monitor + a large and dynamic population of power devices) was enhanced with a + `--daemon-after` variant which parses the configuration once before + daemonization and this has a chance to fail while not forked off, as + well as to allow only completing the service unit initialization when + everything is actually ready to work (so further dependencies can start + at the proper time). [#682] + * Also applied other optimizations to the script implementation. [#682] + + - powerpanel text driver now handles status responses in any format and should + support most devices. [#2156] + + - tripplite_usb driver now allows any device to match if a particular Unit ID + was not specified in `ups.conf`. [PR #2297, issues #2282 and #2258] + + - snmp-ups driver: + * added support for Eaton EMP002 sensor for ATS16 NM2 sub-driver. [#2286] + * mapping table updates for apc-mib sub-driver. [#2264] + + - usbhid-ups driver: + * `arduino-hid` subdriver was enhanced from "initial bare bones" experimental + set of mapped data points to support some 20 more mappings to make it more + useful as an UPS driver, not just a controller developer sandbox. [#2188] + * `cps-hid` subdriver now supports devices branded as Cyber Energy and built + by cooperation with Cyber Power Systems. [#2312] + * The `onlinedischarge` configuration flag name was too ambiguous and got + deprecated (will be supported but no longer promoted by documentation), + introducing `onlinedischarge_onbattery` as the meaningful alias. [#2213] + * Logged notifications about `OL+DISCHRG` state should now be throttled + (see the driver manual page for more details) [#2214, #2215]: + - If `battery.charge` is available, make the message when entering the + state and then only if the charge differs from that when we posted + the earlier message (e.g. really discharging) and is under + `onlinedischarge_log_throttle_hovercharge` value (defaults to 100%); + - Also can throttle to a time frequency configurable by a new option + `onlinedischarge_log_throttle_sec`, by default 30 sec if `battery.charge` + is not reported by the device (should be frequent by default, in case + the UPS-reported state combination does reflect a bad power condition). + + - nutdrv_qx driver: + * Fixed handling of `battery_voltage_reports_one_pack` configuration flag + introduced in NUT v2.8.1. [originally by PR #1279; fixed by PR #2324, + issue #2325] + + - Various code and documentation fixes for NSS crypto support. [#2274, #2268] + + - Laid foundations for the SmartNUT effort (aiming to integrate drivers with + some other backends than the networked NUT data server process). + + - Eaton contributed recipes and scripts used to create the IPP for Unix + bundle (aka Eaton IPSS Unix or UPP), a freely available value-added + packaging of NUT distributed as the UPS software companion for OSes + where their more complex UPS monitoring/management tools had not been + ported. This allows for delivery of NUT packages with an interactive + installer and some system integration scripts (events, notifications, + status, shutdown daemon...), and was contributed to the NUT upstream + project by Eaton -- provided "as is" at the moment, and may later serve + as foundation or inspiration for new NUT features. [#2288] + + - nutconf (C++ library and tool to read and manage NUT configuration files) + was started in the open by Eaton employees and used in the IPP installer, + but the code lingered in a side branch. It was now brushed up to our common + best practices and added to the main codebase. As of this import, there are + known deficiencies in Windows platform support, as well as some un-awareness + about configuration key words which appeared in NUT since 2013. [#2290] + + +Release notes for NUT 2.8.1 - what's new since 2.8.0 +---------------------------------------------------- + +https://github.com/networkupstools/nut/milestone/8 + + - "UPS management protocol", Informational RFC 9271 published + by IETF at https://www.rfc-editor.org/info/rfc9271 and the + IANA port number registry was updated accordingly at + https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=3493 + (even though this RFC is not formally an Internet Standard) + + - NUT documentation files were rearranged, renaming some to `*.adoc` pattern + to facilitate automatic rendering in GitHub and IDE GUIs, and adding recipe + support for GitHub issue/PR links. This `NEWS` file is now proper asciidoc + rendered into `release-notes.pdf` (and HTML versions). [issue #1953, PR #2048] + Internally, the documents would use a new way to define cross-linking to + other pages and their chapters, to facilitate different renderers (including + GitHub UI), and file names created for "chunked HTML" documentation format + will no longer have the "chapter number, section number" format which is + not easy to maintain over time with independent builds of documentation + in NUT and the actual and historic snapshots for nut-website for example. + Chapter/Section names will be adapted to produce "chunked HTML" file names + instead. Documentation links rendered in GitHub UI should point to the HTML + pages served by a current iteration of the NUT website. [PR #226, PR #669] + + - A new `configure --enable-spellcheck` toggle should add spelling checks + to `make check` (by default, if tools are available) to facilitate quicker + acceptance of contributions. [#2067] + + - Published a new maintainer GPG key to sign tags and release artifacts, + and possibly git commits as well, as part of solution for issue #1410. + You can pull it from common OpenPGP servers with the following command: ++ +---- +:; gpg --recv-key DE0184DA7043DCF7 +gpg: key DE0184DA7043DCF7: public key "Jim Klimov (Doing FOSS + since last millennium) " imported +gpg: Total number processed: 1 +gpg: imported: 1 +---- ++ +as part of https://github.com/networkupstools/nut/issues/1410 solution. + + - Bug fixes for fallout possible due to "fightwarn" effort and other + evolution in NUT v2.8.0 release: + * The `upsdebugx()` and similar methods were converted to macros in #685 + to avoid useless data manipulations and requests for logged information, + whose results would be ignored instantly because the debug level is + too low. As issue #1455 and PR #1495 found, in two cases the called + commands did "meaningfully" modify data -- so without debug logs the + program misbehaved. A known regression for `upscode2` driver; might + be or not be a problem with `upsd` server in NUT v2.8.0 release, + fixed for NUT v2.8.1. + * A table in `cyberpower-mib` (for `snmp-ups` driver) sources was + arranged in NUT v2.8.0 release in a way that precluded the driver + logic from looking at all of its entries. Also a fix for instant + command definitions had in fact broken them due to other development. + Regressions fixed for NUT v2.8.1 [#1432, #2029] + * A change for file-change detection in `dummy-ups` driver for NUT + v2.8.0 release misfired on some platforms. Regression fixed for NUT + v2.8.1 [#1420] + * Fixed building of NUT man pages when just a few drivers are selected + by `configure` script for custom builds [#1467] + * Now that `upsdrvctl` can pass debugging level through to the launched + driver(s), they would by default stay in the foreground. This can + complicate (or simplify, when intentional) the management of service + instances. Now there are explicit `upsdrvctl` options for choosing + this (`-F`/`-B`), although default behavior is retained. Note that + explicit foregrounding mode also keeps `upsdrvctl` tool from exiting + and would not wait for one driver to complete initialization before + starting another in case of mass-management loop to start all drivers + (without specifying the single device) [#1759, #1806, #1875] + * The `apcsmart` and `apcsmart-old` handled invalid data too zealously + and aborted instead of skipping over it, like they did before [#2015] + * A bit maths optimization in `riello_ser` and `riello_usb` misfired [#2137] + * Something about compile-time macros or other warnings-related refactoring + seems to have confused the MGE SHUT (Serial HID UPS Transfer) driver + support [#2022] + * Some warnings were not detected by the tools or build scenarios used + earlier, and only got addressed now + + - An issue was identified which could cause `libupsclient` parser of device + and host names to crash upon bad inputs (e.g. poorly resolved environment + variables in scripts). Now it should fail more gracefully [#2052] + + - New `configure --enable-inplace-runtime` option should set default values + for `--sysconfdir`, `--with-user` and `--with-group` options to match an + existing NUT deployment -- for users who are trying if a custom build + of recent codebase solves their practical issues. For "quick tests", a + shortcut operation `./ci_build.sh inplace` was added [#1714] + + - State tree structure and methods (including "dstate" wrapper for common + driver internals) was enhanced with time-stamping of last modification + (setting, changing, deleting the value or some fields in an entry): + this allows to detect stale information in a centralized fashion [#2010] + + - We lacked log information about changes of chroot jail (uncommon) and + of UID/GID (everywhere), which makes troubleshooting harder (e.g. lack + of access to config files or USB device nodes). Now we have it [#1694] + + - A `NUT_DEBUG_PID` envvar (presence) support was added to add current + process ID to tags with debug-level identifiers. This may be useful + when many NUT daemons write to the same console or log file. [#2118] + + - huawei-ups2000 is now known to support more devices, noted in docs and + for auto-detection [#1448, #1684] + + - nutdrv_qx updates: + * a `battery_voltage_reports_one_pack` driver option was added for devices + which "natively" report a `battery.voltage` for a single battery pack or + cell, not for the whole assembly [#1279] + * the `voltronic_qs_protocol` should now accept both "V" (as before) + and newly "H" dialects, which otherwise seem interchangeable [#1623] + * the `armac` subdriver was enhanced to support devices with a different + response pattern than previously expected per initial contribution. + It was tested to work with Vultech V2000 and Armac PF1 series. [#1978] + + - nutdrv_qx and blazer updates: + * extended default ranges for max battery voltage when guessing [#1279] + + - sms_ser, a driver for SMS Brazil UPS Protocol 1Phase, was introduced. + NOTE: it may later become a subdriver under nutdrv_qx. [#2090] + + - usbhid-ups updates: + * added support for `subdriver` configuration option, to select the + USB HID subdriver for the device manually where automatic match + does not suffice (e.g. new devices for which no `vendorid`/`productid` + pair was built into any driver, or for different-capability devices + with same interface chips, notably "phoenixtec/liebert" and "mge") [#1369] + * cps-hid subdriver now applies same report descriptor fixing logic to + devices with ProductID 0x0601 as done earlier for 0x0501, to get the + correct output voltage data [#1497] + * apc-hid subdriver now also supports ProductID 0x0004 [#1429] + * ever-hid subdriver reported a `powerfactor` without a namespace (bug + in 2.8.0 release), fixed to `outlet.powerfactor` + * the `usbhid-ups` driver should now reconnect if `libusb` returned a + memory allocation error [#1422] (seen as "Can't retrieve Report 0a: + Resource temporarily unavailable"), which can cause practical problems + in the field -- the driver otherwise interpreted the situation as + `ups.status` being `OL OFF` and cut the power supply. + * powercom-hid subdriver: fixed `UPS.Battery.ManufacturerDate` to map + to `battery.mfr.date` (not `battery.date` which is the maintenance + replacement date) [#1644] + * added `onlinedischarge_calibration` option for UPSes that report + `OL+DISCHRG` when they are in calibration mode [#2104] + + - powercom driver should now try harder to refresh data from device [#356] + + - tripplite_usb driver now supports configuration of `upsid` to match the + specific device (not all firmware/hardware models support this) [#2075] + + - apcupsd-ups: + * improvement for `POLL_INTERVAL_MIN` from PR #797 was buggy [#2007] + * fix to clean obsoleted readings (if any) AFTER getting new info from an + `apcupsd` daemon, to avoid the gap when NUT driver knows nothing [#2007] + + - apc_modbus driver was introduced, to cover the feature gap between existing + NUT drivers for APC hardware and the actual USB-connected devices (or their + firmwares) released since roughly 2010, which deprecated standard USB HID + support in favor of Modbus-based protocol which is used across the board + (also with their network management cards). The new driver can monitor APC + UPS devices over TCP and Serial connections, as well as USB with a patched + libmodbus (check https://github.com/EchterAgo/libmodbus/commits/rtu_usb + for now, PR pending). [#139, #2063] + * For a decade until this driver got introduced, people were advised to + use apcupsd project as the actual program which talks to a device, and + NUT apcupsd-ups driver to relay information back and forth. This was a + limited solution due to lack of command and variable setting support, + as well as relaying of just some readings (just whatever apcupsd exposes, + further constrained by what our driver knows to re-translate), with + little leverage for NUT to tap into everything the device has to offer. + There were also issues on some systems due to packaging (e.g. marking + NUT and apcupsd as competing implementations of the same features) which + required clumsy workarounds to get both installed and running. Finally, + there is a small matter of long-term viability of that approach: last + commits to apcupsd sources were in 2017 (with last release 3.14.14 in + May 2016): https://sourceforge.net/p/apcupsd/svn/HEAD/tree/ + + - dummy-ups: + * Added an `repeater_disable_strict_start` option to disable the driver + exiting upon encountering any kind of error at startup (as repeater). + This option should allow for collective `upsdrvctl` startup despite + individual target UPS to be repeated or `upsd` not having come up yet. + [#2132] + * Revised detection of file path (for "dummy" mode) which misfired under + some conditions, and unified several implementations. [#2118] + + - NUT for Windows: + * Ability to build NUT for Windows, last tackled with a branch based on + NUT v2.6.5 a decade ago, has been revived with the 2.8.x era codebase [#5]. + It is known that at this time some features are not complete, for more + details see https://github.com/orgs/networkupstools/projects/2/views/1 + * Cross-builds of NUT for Windows using Linux and MinGW (and many custom + built dependency packages, as documented in the + link:scripts/Windows/README.adoc[scripts/Windows/README.adoc file]) + are now regularly tested on NUT CI farm with moderate integration via + custom build script `scripts/Windows/build-mingw-nut.sh` [#1489] + * Semi-native NUT for Windows builds with MSYS2/MinGW x64 environment are + now regularly tested on AppVeyor, with the same `ci_build.sh` script and + `Makefile` checks as used across the board for local developer builds, + Linux/illumos/FreeBSD/OpenBSD/... on dedicated NUT CI farm on Fosshost, + and MacOS on CircleCI [#1552] + + - snmp-ups updates: + * Fixed detection for device agents which wrongly return the sysOID value + as a string instead of an OID [#1710] + * Clearer messages about skipping MIBs during driver initialization [#2037] + * IETF MIB mapping updated for data points where negative readings + are invalid [#1558] + * Added SNMP subdriver "apc-epdu-mib" for APC easy PDU support [#1674] + * Added SNMP subdriver "eaton-pdu-nlogic-mib" for nLogic (rebranded Eaton) + support [#1698] + * Added SNMP subdriver "hpe-pdu3-cis-mib" for HPE G2 Metered & Switched PDU + initial "unitary" support (no daisychain support yet); also note that due + to SNMP v1 implementation limitations on this device, you should prefer + SNMP v3 to get both read and write rights [#1713] + * Fixed processing loop for large SNMPv2/SNMPv3 responses where one item + in the middle has a type error [#1682] + * Better manage the slight nuances (especially in `ups.status`) between + Eaton UPSs, and rename mibs from `pw` to `eaton_pw_nm2`, and from + `pxgx_ups` to `eaton_pxg_ups` [#1715] + * Fixed the long standing "Warning: excessive poll failures" issue, that + was tied to non-existent OIDs, not well handled in some parts of the + driver [#1716] + * `baytech-mib.c` subdriver: fixed `baytech_outlet_status_info[]` set + of valid outlet status values [#1871] + * `cyberpower-mib.c` subdriver: support devices which report the shorter + Vendor OID as their sysOID, e.g. "CyberPower PowerPanel Personal" [#1997] + and support more data points including hardware status alarms [#1982] + + - The `bestfortress` driver shutdown handling was fixed to use a non-trivial + default timeout [#1820] + + - The `optiups` driver only gave accurate voltage information with 120VAC + models and assumed a 12V battery when calculating capacity. There is + a protocol command that gives a (fixed) voltage which correlates with + the voltage selection DIP switches on the back of the UPS, taking into + account whether it is a 120 or 240VAC model. Likewise, now the battery + capacity fix is applied globally, based on whether or not the battery + voltage is greater than 20V. [#2089] + + - GPIO drivers [#1855]: + * Added a new category of drivers, using GPIO interface to locally connected + devices (currently limited to 2018+ Linux libgpiod, but its architecture + was designed to support more OSes with their equivalents - PRs welcome) + * `generic_gpio_libgpiod` driver using `libgpiod` backend was added + (defaults to be required on Linux, optional on other platforms) + + - Added support for `make install` of PyNUT module and NUT-Monitor desktop + application [#1462, #1504] + + - Regular CI coverage for NUT codebase enhanced with CircleCI running some + scenarios on MacOS, might add Windows in the future. Fixed some build + issues for MacOS that had crept into NUT v2.8.0 release [#1415, #1421] + + - NUT software-only drivers (dummy-ups, clone, clone-outlet) separated from + serial drivers in respective Makefile and configure script options [#1446] + + - Fixed support for common USB matching options ("vendor", "device", "bus", + etc.) for `riello_usb` and `richcomm_usb` [#1763] and updated man pages + of all USB drivers using these options to include the same description + [#1766] + + - Added a "busport" USB matching option (if supported by the hardware, OS and + libusb on the particular deployment, it should allow to specify physical + port numbers on an USB hub, rather than logical "device" enumeration values, + and in turn -- this should be less volatile across reboots etc.) [#2043] + + - Added an `allow_duplicates` flag for common USB matching options which + may help monitor several related no-name devices (although without knowing + reliably which one is which... better than nothing) [#1756] + + - The `nut-scanner` program should now suggest same configuration fields as + those used by common USB matching options in (most of the) drivers, e.g. + adding "device" to the generated configuration section [#1790] + + - Stuck drivers that do not react to `SIGTERM` quickly are now retried with + `SIGKILL` [#1424] + + - Each driver should now report its `driver.state` to help readers determine + whether it is initializing, reconnecting, or running regular loops [#1767] + + - Code which resolves full paths to libraries should now consider the common + environment variable `LD_LIBRARY_PATH` as a preferred possible override + to built-in paths (note that most operating systems advise against setting + this variable unless troubleshooting, although other systems rely on it) + [#805] + + - Debug information tracing methods like `upsdebugx()` should now be less + limited in the sizes of messages that they can print, such as path names + that may be quite long. Note that the OS methods manipulating the strings, + and receivers such as logging systems, may still impose limits of their own. + + - The `nut-scanner` usage and debug printouts now include the loadable library + search paths, to help troubleshooting especially in multi-platform builds; + pre-filtering of the built-in paths was introduced (to walk only existing + and unique directory names) [#317] + + - The nut-scanner program was updated to fall back to loading unresolved + library filenames, hoping that `lt_dlopen()` implementation on the current + platform would find library files better [#805] + + - Detection of `libltdl` in `configure` script updated with fallback code to + find it on systems that deliver the library to `/usr/local/lib` (e.g. on + FreeBSD) [#1577] + + - An explicit `configure --with-nut-scanner` toggle was added, specifically + so that build environments requesting `--with-all` but lack `libltdl` would + abort and require either to install the dependency or explicitly forfeit + the tool (some distro packages missed it quietly in the past) [#1560] + + - The `nut-scanner` program should now by default warn about serial numbers + which do not make much sense (are duplicate, empty, all same character, etc) + [#1810] + + - Existing openssl-1.1.0 support added for NUT v2.8.0 release was tested to + be sufficient without deprecation warnings for builds against openssl-3.0.x + (but no real-time testing was done yet) [#1547] + + - upslog: Added support for logging multiple devices with one call to the + program [#1604] + + - Documentation to integrate NUT USB driver startup with `usb_resetter` script + has been contributed to `scripts/usb_resetter` (the script itself is tracked + externally on GitHub), along with a configuration example for Linux+systemd + [#1887] + + - Some fixes applied to Solaris/illumos packaging and SMF service support + [#1554, #1564] + + - Some fixes for builds on older OSes with less functional default system + shell interpreters - now `autogen.sh` supports a `CONFIG_SHELL` envvar + to inject its value into generated `configure` script [#1736] + * Note that you may have to install additional tools (possibly from + third-party FOSS packaging efforts) to prepare and build the NUT + codebase, and/or prefer non-default system provided implementations + (e.g. to use the XPG4 `grep` with `-E` support on Solaris as detailed + in https://github.com/networkupstools/nut/issues/1736 comments) + * Build environment configuration notes in link:docs/config-prereqs.txt[] + file refreshed to cover building of current NUT codebase in CentOS 6 + [#1804] and Solaris 8 [#1736, #1738] + + - `configure` script, reference init-script and packaging templates updated + to eradicate `@PIDPATH@/nut` ambiguity in favor of `@ALTPIDPATH@` for the + unprivileged processes vs. `@PIDPATH@` for those running as root [#1719] + + - `configure` script enhanced: `--with-unmapped-data-points` option allows + to build SNMP and USB-HID subdrivers with entries discovered by the scripts + which generated them from data walks, but developers did not rename yet + to NUT mappings conforming to link:docs/nut-names.txt[] standards [#1699] + + - PyNUT.py version bumped to 1.5.0 with some improvements: + * `ListClients()` method fixed (was broken in many ways), and is now + CI-tested [#549] + * `DeviceLogin()` method added (mostly as aid to CI-test `ListClients()` + in a practically relevant manner, so far) + + - nutclient C++ library: + * added `listDeviceClients()` and `deviceGetClients(dev)` to `Client` + classes, and `Device::getClients()` to match PyNUT capabilities [#549] + * published artifacts may include a `libnutclientstub` which is an + implementation of a NUT TCP client in C++ with in-memory data store. + + - upsclient C library: + * added support for `NUT_QUIET_INIT_SSL` environment variable to hide + the infamous "Init SSL without certificate database" warning [#1662] + + - The `upsd.conf` listing of `LISTEN` addresses was previously inverted + (the last listed address was applied first), which was counter-intuitive + and fixed for this release [#2012] + + - The `upsd` configured to listen on IPv6 addresses should handle only + IPv6 (and not IPv4-mappings) to avoid surprises and insecurity; it + will now warn if a host name resolves to several addresses (and will only + listen on the first hit, as before in such cases) [#2012] + + - A definitive behavior for `LISTEN *` directives became specified, to try + handling both IPv4 and IPv6 "any" address (subject to `upsd` CLI options + to only choose one, and to OS abilities). When both address families are + enabled, the `upsd` data server will first try to open an IPv6 socket + asking for disabled IPv4-mapped IPv6 address support (if the OS honors + that), and then an IPv4 socket (which may fail if the IPv6 socket already + covers it anyway); in other words, you can end up with one or two separate + listening sockets. [#2012] + + - sstate (server state, e.g. upsd) should now "PING" drivers also if they + last reported themselves as "stale" (and might later crash) so their + connections would be terminated if really no longer active [#1626] + + - Clarified documentation in codebase according to end-user feedback [#1721, + #1750 and others over time] + + - upsmon client changes include: + * Several fixes for `upsmon` behavior [#1761, #1680...], including new + ability to configure default POWERDOWNFLAG location -- packagers are + encouraged to pick optimal location for their distributions (which + remains mounted at least read-only late in shutdown) and a new optional + POLLFAIL_LOG_THROTTLE_MAX setting [#529, #506] + * Also `upsmon` should now recognize `OFF` and `BYPASS` flags in `ups.status` + and report that these states begin or end. The `OFF` state usually means + than an administrative action happened to power off the load, but the UPS + device is still alive and communicating (USB, SNMP, etc.); corresponding + `MONITOR`'ed amount of power sources are considered not being "fed" for + the power value calculation purposes. The `BYPASS` state is now treated + similarly to `ONBATT`: currently this UPS "feeds" its load, but if later + communications fail, it is considered dead. This may have unintended + consequences for devices (or NUT drivers) that do not report these modes + correctly (e.g. an APC calibration routine seems to start with a few + seconds of "OFF" state), so the reported status is only considered as a + loss of feed if it persists for more than `OFFDURATION` seconds. [#2044, + #2104] + * Introduced `SHUTDOWNEXIT no` configuration toggle for systems which + require a long time to stop their workload such as virtual machines. + Since the disconnection of a "secondary" client is treated by the + "primary" system as permission to proceed with its own shutdown and + power-off for the UPS, the original (now merely default) behavior to + call `SHUTDOWNCMD` and immediately exit could be counter-productive. + An optional delay can also be introduced. [#2133] + * Note there were other changes detailed below which impacted several NUT + programs, including `upsmon`. + + - Extended Linux systemd support with optional notifications about daemon + state (READY, RELOADING, STOPPING) and watchdog keep-alive messages [#1590] + * Normally *inability* to send such notifications (e.g. lack of systemd + or similar framework on the particular platform) would be reported once + per daemon uptime on its console log, to help troubleshooting situations + where such lack of notifications can cause automated service restarts. + These messages can be hidden by setting `NUT_QUIET_INIT_UPSNOTIFY=true` + environment variable in init-scripts on platforms where such frameworks + are not expected. [#2136] + + - Extended Linux systemd units with aliases named after the daemons: + `nut-server.service` as `upsd.service`, and `nut-monitor.service` as + `upsmon.service` (so simple `systemctl reload upsd` can work) [#1777] + + - Extended driver-server socket protocol with `BROADCAST (num)` keyword, + and a `NOBROADCAST` as a shortcut for `BROADCAST 0`. This allows clients + to toggle whether they want to receive `send_to_all()` updates from a + driver, or only answers to requests they send [#1914] + + - Added support for `make sockdebug` for easier developer access to the tool; + also if `configure --with-dev` is in effect, it would now be installed to + the configured `libexec` location. A man page was also added. [#1936] + + - Numerous daemons (`upsd`, `upsmon`, drivers, `upsdrvctl`, `upssched`) + which accepted `-D` option for debug setting previously, now can also + honour a `NUT_DEBUG_LEVEL=NUM` environment variable if no `-D` arguments + were provided. Unlike those arguments, the environment variable does + not enforce that daemons run in foreground mode by default [#1915] + * Note that unlike some other NUT daemons, `upssched` with enabled + debug does not stop reporting on `stderr`! [#1965] + + - A bug in `upssched` was discovered and fixed, where it ran a tight loop + stressing the CPU; it was presumably introduced between NUT v2.7.4 and + v2.8.0 releases [#1964, #1965] + + - Implemented generic support for INSTCMD and SETVAR use-cases shared by + all drivers, and in particular to see and change active debug verbosity + using the driver-server and server-client protocol (at higher priority + than CLI or config file choices) per [#1285], e.g. +------ +# Set verbosity level 6: +:; upsrw -s driver.debug=6 UPS + +# Set verbosity level 0 to disable the noise (even if debug_min is set): +:; upsrw -s driver.debug=0 UPS1@localhost + +# Un-set the protocol override, honour CLI or config-file settings again: +:; upsrw -s driver.debug=-1 remoteUPS@1.2.3.4 +------ ++ +and a `driver.killpower` instant command (for safety, must be unlocked by + `driver.flag.allow_killpower` protocol setting or `allow_killpower` + configuration flag), which is now the first choice for `driver -k` + operations [#1917, #1923] + + - Implemented basic support for `ups.conf` reloading in NUT drivers, + with a `driver.reload-with-error` instant command (more commands and + signal handling may be available depending on platform), with a goal + of changing inconsequential settings like `debug_min` for a running + driver. This can also benefit the drivers on systems managed by real-time + `nut-driver-enumerator` and for simpler changes the drivers get only + reloaded and not redefined and restarted. Reload signals should also + be reasonably supported with `upsdrvctl` tool. Relevant CLI options + for `-c CMD` handing were added to drivers and `upsdrvctl`, although + their availability may vary between operating systems [#1903, #1914, #1924] + + - Drivers should now accept `SIGURG` (or `SIGWINCH` on systems that lack + the former) on POSIX platforms to dump their current state information + and move on. Such report goes to `stdout` of the driver process (may + be disconnected when background mode is used) -- this can help with + troubleshooting [#1907] + + - Recipes and `main.c` code were enhanced to produce a `libdummy_mockdrv.la` + helper library during build (not intended to be installed nor distributed), + in order to facilitate creation of test programs which behave like a driver + [#1855] + + - Further revision of public headers delivered by NUT was done, particularly + to address lack of common data types (`size_t`, `ssize_t`, `uint16_t`, + `time_t` etc.) in third-party client code that earlier sufficed to only + include NUT headers. Sort of regression by NUT 2.8.0 (note those consumers + still have to re-declare some numeric variable types used) [#1638, #1615] + + - The `COPYING` file was updated with licenses and attribution for certain + source code files and blocks coming from the Internet originally [#1758] + + - The `tools/gitlog2changelog.py.in` script was revised, in particular to + generate the `ChangeLog` file more consistently with different versions + of Python interpreter, and without breaking the long file paths in the + resulting mark-up text [#1945, #1955] + + - The "NUT client for VMware ESXi" project (by RenĆ© Garcia) got its build + recipes published on GitHub at https://github.com/rgc2000/NutClient-ESXi + [#1961] + + +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 +765,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 +811,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 +846,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 +890,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 +913,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 +942,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 +1002,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 +1026,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 +1037,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 +1065,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,19 +1139,14 @@ 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. - - The nutconf tool (and C++ library for it), interactive installation - and other helper scripts provided with Eaton IPSS Unix (aka IPP Unix - or UPP), a freely available value-added packaging of NUT distributed - for OSes where their more complex UPS monitoring/management tools had - not been ported, was contributed to the NUT upstream project by Eaton. ---------------------------------------------------------------------------- -Release notes for NUT 2.7.4 - what's new since 2.7.3: +Release notes for NUT 2.7.4 - what's new since 2.7.3 +---------------------------------------------------- - New class of device supported: ATS - Automatic Transfer Switch are now supported in NUT. Eaton ATS are supported, and APC ones should be too. Users @@ -348,22 +1159,22 @@ Release notes for NUT 2.7.4 - what's new since 2.7.3: input, output, outlet and outlet.group) - support for new devices: - AEG PROTECT B / NAS - APC ATS AP7724 (should be supported) - Asium P700 - Eaton ATS - Eaton 5E 1100iUSB - Eaton E Series DX UPS 1-20 kVA - Eaton Powerware 9125-5000g - Electrys UPS 2500 - Fideltronic INIGO Viper 1200 - Legrand Keor Multiplug - LYONN CTB-800V - Micropower LCD 1000 - NHS Laser Senoidal 5000VA - Sweex model P220 - TS Shara - Various APCUPSD-controlled APC devices + * AEG PROTECT B / NAS + * APC ATS AP7724 (should be supported) + * Asium P700 + * Eaton ATS + * Eaton 5E 1100iUSB + * Eaton E Series DX UPS 1-20 kVA + * Eaton Powerware 9125-5000g + * Electrys UPS 2500 + * Fideltronic INIGO Viper 1200 + * Legrand Keor Multiplug + * LYONN CTB-800V + * Micropower LCD 1000 + * NHS Laser Senoidal 5000VA + * Sweex model P220 + * TS Shara + * Various APCUPSD-controlled APC devices - snmp-ups: * Improve automatic detection algorithm @@ -447,18 +1258,20 @@ Release notes for NUT 2.7.4 - what's new since 2.7.3: - Network protocol information: default to type NUMBER for variables that are not flagged as STRING . This point is subject to improvements or change in - the next release 2.7.5. Refer to docs/net-protocol.txt for more information + the next release 2.7.5. Refer to link:docs/net-protocol.txt[] for more + information - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. ---------------------------------------------------------------------------- -Release notes for NUT 2.7.3 - what's new since 2.7.2: + +Release notes for NUT 2.7.3 - what's new since 2.7.2 +---------------------------------------------------- - reverted POWERDOWNFLAG to /etc/killpower as in 2.6.5 (packagers may want to put this in another filesystem, though) - - configure/make fixes for ${systemdsystemunitdir} + - configure/make fixes for `systemdsystemunitdir` - apcsmart: fix command set parsing for protocol version 4 (e.g. Smart-UPS RT 10000 XL) @@ -473,8 +1286,9 @@ Release notes for NUT 2.7.3 - what's new since 2.7.2: - USB core: do not call usb_set_altinterface(0) by default - - nutdrv_qx: added fabula, fuji USB and Voltronic-QS-HEX subdrivers; add - bestups subdriver to supersede the old standalone bestups driver + - nutdrv_qx: + * added fabula, fuji USB and Voltronic-QS-HEX subdrivers + * add bestups subdriver to supersede the old standalone bestups driver - NUT Monitor: added FreeDesktop AppData file (including screenshots) @@ -486,9 +1300,10 @@ Release notes for NUT 2.7.3 - what's new since 2.7.2: - solis: updated to support APC Microsol units sold in Brazil - - tripplite_usb: updated to use dv/dq charge calculation for all models (also - exposes battery_min and battery max as configuration variables); added - binary 3005 protocol support (such as for SMART500RT1U) + - tripplite_usb: + * updated to use dv/dq charge calculation for all models (also + exposes battery_min and battery max as configuration variables) + * added binary 3005 protocol support (such as for SMART500RT1U) - genericups: better debugging while parsing the cable description flags @@ -500,30 +1315,30 @@ Release notes for NUT 2.7.3 - what's new since 2.7.2: XCP USB/serial) * Fix and complete Eaton ePDUs G2/G3 support * ABM (Advanced Battery Monitoring) support through battery.charger.status - in HID (USB and SHUT), XCP (USB and serial) and SNMP (Powerware XUPS - MIB) + in HID (USB and SHUT), XCP (USB and serial) and SNMP (Powerware XUPS MIB) - support for new devices: - APC Back-UPS 1200BR and Back-UPS BZ2200BI-BR (Microsol) - ASEM SPA PB1300 UPS - Belkin Regulator PRO-USB - Cyber Power Systems Value 1500ELCD-RU - EUROCASE EA200N 2000VA - Fideltronik LUPUS 500 - Flight Technic & International (FTUPS) FT-1000BS and FT-1000BS(T) - Grafenthal PR-3000-HS - JAWAN JW-UPSLC02 - Lacerda New Orion 800VA - Mecer ME-1000-WTU - NHS Sistemas de Energia Expert C Online 6000/8000/10000 - NHS Sistemas de Energia Expert S Online 6000/8000/10000 - Powercom BNT-xxxAP (USB product id: 0001) - Rucelf UPOII-3000-96-EL - Tripp Lite OMNIVSINT800 - Voltronic Power Apex 1KVA and Imperial 1KVA - ---------------------------------------------------------------------------- -Release notes for NUT 2.7.2 - what's new since 2.7.1: + * APC Back-UPS 1200BR and Back-UPS BZ2200BI-BR (Microsol) + * ASEM SPA PB1300 UPS + * Belkin Regulator PRO-USB + * Cyber Power Systems Value 1500ELCD-RU + * EUROCASE EA200N 2000VA + * Fideltronik LUPUS 500 + * Flight Technic & International (FTUPS) FT-1000BS and FT-1000BS(T) + * Grafenthal PR-3000-HS + * JAWAN JW-UPSLC02 + * Lacerda New Orion 800VA + * Mecer ME-1000-WTU + * NHS Sistemas de Energia Expert C Online 6000/8000/10000 + * NHS Sistemas de Energia Expert S Online 6000/8000/10000 + * Powercom BNT-xxxAP (USB product id: 0001) + * Rucelf UPOII-3000-96-EL + * Tripp Lite OMNIVSINT800 + * Voltronic Power Apex 1KVA and Imperial 1KVA + + +Release notes for NUT 2.7.2 - what's new since 2.7.1 +---------------------------------------------------- - This release is the second interim release of the 2.7 testing series. @@ -531,13 +1346,13 @@ Release notes for NUT 2.7.2 - what's new since 2.7.1: This issue was reported on Debian (bug #731156) and is now fixed - support for new devices: - CABAC UPS-1700DV2 - Eaton Powerware 3105 - Emerson Network Power Liebert PSI 1440 - MicroDowell B.Box LP 500 - Numeric Digital 800 plus - OptiUPS VS 575C - Tripp Lite SU10KRT3/1X + * CABAC UPS-1700DV2 + * Eaton Powerware 3105 + * Emerson Network Power Liebert PSI 1440 + * MicroDowell B.Box LP 500 + * Numeric Digital 800 plus + * OptiUPS VS 575C + * Tripp Lite SU10KRT3/1X - FreeDesktop Hardware Abstraction Layer (HAL) support was removed. @@ -549,7 +1364,7 @@ Release notes for NUT 2.7.2 - what's new since 2.7.1: - snmp-ups: add support for XPPC-MIB and Tripp Lite SU10KRT3/1X. Also fix erroneous status in HP/Compaq SNMP MIB (with the most recent HP - firmware (1.76) ; improved various MIBs (APC, HP/Compaq, ...) + firmware (1.76); improved various MIBs (APC, HP/Compaq, ...) - nutdrv_qx: add new 'fallback' Q1 subdriver, with minimal 'Q1' support. General improvements on all subdrivers. @@ -567,14 +1382,17 @@ Release notes for NUT 2.7.2 - what's new since 2.7.1: - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. ---------------------------------------------------------------------------- -Release notes for NUT 2.7.1 - what's new since 2.6.5: + +Release notes for NUT 2.7.1 - what's new since 2.6.5 +---------------------------------------------------- + +NOTE: There was no public NUT 2.7.0 release. - This release is an interim release, part of the testing series, and the first release after the transition from Subversion to Git. The last release (2.6.5) is almost a year old. A lot of work has been done, but a good amount remains to achieve 2.8.0 goals. - Please read the UPGRADING notes. + Please read the link:UPGRADING.adoc[] notes. - Added support for SSL via the Mozilla NSS library, in addition to the existing OpenSSL support. @@ -635,8 +1453,8 @@ Release notes for NUT 2.7.1 - what's new since 2.6.5: using Aspell. This includes an interactive build target (make spellcheck-interactive), and an automated one (make spellcheck), mainly for QA / Buildbot purpose. Note that a base NUT dictionary - is also available (docs/nut.dict), providing a glossary of terms - related to power devices and management + is also available (link:docs/nut.dict[]), providing a glossary of + terms related to power devices and management - Improve systemd integration @@ -644,8 +1462,9 @@ Release notes for NUT 2.7.1 - what's new since 2.6.5: support. Also fixed mappings for upsBypassVoltage, upsBypassCurrent, and upsBypassPower in three-phase IETF MIB. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.5 - what's new since 2.6.4: + +Release notes for NUT 2.6.5 - what's new since 2.6.4 +---------------------------------------------------- - This release fixes an important regression in upssched: any upssched.conf command that takes a second argument resulted in @@ -653,29 +1472,35 @@ Release notes for NUT 2.6.5 - what's new since 2.6.4: not executed (report and patch from Oliver Schonefeld) - Website hosting: free NUT from Eaton website hosting - NUT website (http://www.networkupstools.org) is no longer hosted by Eaton. + + + + + NUT website (https://www.networkupstools.org) is no longer hosted by Eaton. Arnaud Quette (NUT project leader) has taken over NUT hosting on his own, to give NUT back some independence. + + + + This effort is also part of a logic to stop crediting Eaton for contributions from others (especially Arnaud Quette, as an individual). The new hosting service is located, as for Arnaud's blog - (http://arnaud.quette.fr) on Gandi servers, using PaaS. This will allow - more flexibility and automation of the release process + (http://arnaud.quette.fr) on Gandi servers, using PaaS. + + + + + This will allow more flexibility and automation of the release process. - macosx-ups: new OS X Power Sources meta-driver - Mac OS X provides UPS status information in a format similar to - what is shown for laptop batteries. This driver will convert that - information into a format compatible with NUT (Charles Lepple). + * Mac OS X provides UPS status information in a format similar to + what is shown for laptop batteries. This driver will convert that + information into a format compatible with NUT (Charles Lepple). - support for new devices: - Eaton ePDU Switched - Online Zinto A (USB ID 0x06da:0x0601) - REDi Blazer 400VA / 600VA / 800VA - UNITEK Alpha650ipF and Alpha650ipE (USB ID 0x0f03:0x0001) + * Eaton ePDU Switched + * Online Zinto A (USB ID 0x06da:0x0601) + * REDi Blazer 400VA / 600VA / 800VA + * UNITEK Alpha650ipF and Alpha650ipE (USB ID 0x0f03:0x0001) - mge-shut driver has been replaced by a new implementation (newmge-shut). In case of issue with this new version, users can revert to oldmge-shut. - UPDATE: oldmge-shut was dropped between 2.7.4 and 2.7.5 releases. + UPDATE: oldmge-shut was dropped between 2.7.4 and 2.8.0 releases. - First NUT virtualization package: NUT now supports integration with VMware ESXI 5.0, through a native VIB package. This is, for the time @@ -694,25 +1519,33 @@ Release notes for NUT 2.6.5 - what's new since 2.6.4: - more bugfixes, cleanup and improvements, on both source code and documentation. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.4 - what's new since 2.6.3: - - This release Fix an important vulnerability in upsd - (CVE-2012-2944: upsd can be remotely crashed) +Release notes for NUT 2.6.4 - what's new since 2.6.3 +---------------------------------------------------- + - This release fixes an important vulnerability in upsd + (CVE-2012-2944: upsd can be remotely crashed) + + + + NUT server (upsd), from versions 2.4.0 to 2.6.3, are exposed to crashes when receiving random data from the network. + + + + This issue is related to the way NUT parses characters, especially from the network. Non printable characters were missed from strings operation (such as strlen), but still copied to the buffer, causing an overflow. + + + + Thus, fix NUT parser, to only allow the subset of ASCII charset from - Space to ~ + `Space` to `~` (Reported by Sebastian Pohle, Alioth bug #313636, CVE-2012-2944) - + + + + A separate patch, which applies to any faulty version, is also available: http://trac.networkupstools.org/projects/nut/changeset/3633 - + + + + For more information, refer to the Common Vulnerabilities and Exposures: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2944 @@ -734,29 +1567,29 @@ Release notes for NUT 2.6.4 - what's new since 2.6.3: LIST CLIENTS, FSD, HELP and VER (Rene MartĆ­n RodrĆ­guez) - support for new devices: - AEG Power Solutions PROTECT HOME - more APC SNMP cards - ATEK Defensor range - all Borri models - all COVER ENERGY SA - CyberPower OR700LCDRM1U, PR6000LCDRTXL5U and CP1000PFCLCD - Dell UPS Network Management Card - Dynamix 1000VA USB - Eaton Management Card Contact (ref 66104) - EVER POWERLINE RT / 11 / 31 and DUO II Pro - GE Digital Energy GT Series 1000-3000 VA - Gtec models - all recent HP serial / USB UPS (G2, G3 and R/T models, ) and HP UPS - Management Module - Ippon INNOVA RT - KOLFF BLACK NOVA - Lexis X-power Tigra 1kVA - Microline C-Lion Innova - Online Yunto YQ450 - PowerShield Defender 1200VA - PowerWalker Online VFI LCD, Line-Interactive VI LCD and Line-Interactive VI - Riello Netman Plus 102 SNMP Card - Tripp-Lite OMNISMART500 + * AEG Power Solutions PROTECT HOME + * more APC SNMP cards + * ATEK Defensor range + * all Borri models + * all COVER ENERGY SA + * CyberPower OR700LCDRM1U, PR6000LCDRTXL5U and CP1000PFCLCD + * Dell UPS Network Management Card + * Dynamix 1000VA USB + * Eaton Management Card Contact (ref 66104) + * EVER POWERLINE RT / 11 / 31 and DUO II Pro + * GE Digital Energy GT Series 1000-3000 VA + * Gtec models + * all recent HP serial / USB UPS (G2, G3 and R/T models, ) and HP UPS + Management Module + * Ippon INNOVA RT + * KOLFF BLACK NOVA + * Lexis X-power Tigra 1kVA + * Microline C-Lion Innova + * Online Yunto YQ450 + * PowerShield Defender 1200VA + * PowerWalker Online VFI LCD, Line-Interactive VI LCD and Line-Interactive VI + * Riello Netman Plus 102 SNMP Card + * Tripp-Lite OMNISMART500 - apcsmart has received some fixes to work better on Mac OS X, and in general @@ -766,8 +1599,8 @@ Release notes for NUT 2.6.4 - what's new since 2.6.3: - bestfortress has improved Best Fortress LI675VA support - blazer_ser and blazer_usb now try to automatically estimate high and low - voltages, to be able to calculate battery charge ; support for online - Innova UPS (T, RT and 3/1 T) has been added ; Best UPS support has been + voltages, to be able to calculate battery charge; support for online + Innova UPS (T, RT and 3/1 T) has been added; Best UPS support has been improved, to prepare for superseding bestups driver - bestups has also received some care, though users are encouraged to switch @@ -781,25 +1614,25 @@ Release notes for NUT 2.6.4 - what's new since 2.6.3: including more data and instant commands (Bill Elliot). - usbhid-ups: for Eaton devices, ups.start.auto is now automatically adjusted - for shutdown.{return,stayoff} to behave as expected ; Liebert firmwares with + for shutdown.{return,stayoff} to behave as expected; Liebert firmwares with incorrect exponents have also been addressed. - snmp-ups now provides support for UPS shutdown, based on usbhid-ups mechanisms (composite commands and fallback). Composite commands are also supported now. This means, for example, that if 'shutdown.return' is not - supported, a combination of 'load.off' + 'load.on' may be used ; - Actual validity of instant commands is now tested before commands addition ; - Eaton/MGE MIB has been cleaned and completed ; 3-phases support has been - added to Socomec Netvision MIB ; HP/Compaq MIB has been completed, with + supported, a combination of 'load.off' + 'load.on' may be used; + Actual validity of instant commands is now tested before commands addition; + Eaton/MGE MIB has been cleaned and completed; 3-phases support has been + added to Socomec Netvision MIB; HP/Compaq MIB has been completed, with thresholds, nominal values and more commands. - - nut-scanner now also has libupsclient has a weak runtime dependency ; more + - nut-scanner now also has libupsclient has a weak runtime dependency; more docs and bugfixes have also happened. - Provide an Uncomplicated Firewall (UFW) profile (nut.ufw.profile) - Riello protocols have been officially published in NUT protocols library: - http://www.networkupstools.org/ups-protocols.html#_riello + https://www.networkupstools.org/ups-protocols.html#_riello - Duplicate instances of upsd / upsmon are now detected upon startup @@ -813,8 +1646,9 @@ Release notes for NUT 2.6.4 - what's new since 2.6.3: - more bugfixes, cleanup and improvements, on both source code and documentation, with a good bunch from Greg A. Woods. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.3 - what's new since 2.6.2: + +Release notes for NUT 2.6.3 - what's new since 2.6.2 +---------------------------------------------------- - nut-scanner is now more portable, and provides more coherent option names. IPMI support has also been added, to discover local power supplies. @@ -826,24 +1660,27 @@ Release notes for NUT 2.6.3 - what's new since 2.6.2: distributed. Some documentation is also available in the developer guide and manual pages have been updated and completed. - - support for new devices: Cyber Power Systems with SNMP RMCARD (100, 201, - 202 and 301) ; Dynamix 650VA USB ; LDLC UPS-1200D ; Tecnoware UPS ERA LCD - 0.65 ; Powercom BNT-xxxAP (USB ID 0d9f:0004) ; Various USB devices using - UPSilon 2000 software. + - support for new devices: + * Cyber Power Systems with SNMP RMCARD (100, 201, 202 and 301) + * Dynamix 650VA USB + * LDLC UPS-1200D + * Tecnoware UPS ERA LCD 0.65 + * Powercom BNT-xxxAP (USB ID 0d9f:0004) + * Various USB devices using UPSilon 2000 software - apcsmart has received minor correction. - bcmxcp_usb now handles disconnection issues and reconnection mechanism. - blazer_usb enables again inclusion of buggy USB Device and Vendor IDs in - udev rules file ; language ID support has been added for USB units from + udev rules file; language ID support has been added for USB units from LDLC, Dynamix and other no names. - nut-ipmipsu has also received some improvements. - - snmp-ups has fixed outlets reported current in aphel_genesisII MIB ; - MGE 3 phases handles better low battery condition ; support for Cyber Power - Systems with SNMP RMCARD has been added ; support of the newer Eaton ePDUs + - snmp-ups has fixed outlets reported current in aphel_genesisII MIB; + MGE 3 phases handles better low battery condition; support for Cyber Power + Systems with SNMP RMCARD has been added; support of the newer Eaton ePDUs has been improved. - upsd doesn't anymore fail to start if at least one of the listening @@ -853,7 +1690,7 @@ Release notes for NUT 2.6.3 - what's new since 2.6.2: - Avahi support is now automatically enabled, upon detection - jNut (NUT Java interface) adds device discovery support, through a - nut-scanner wrapper ; jNutWebAPI, a HTTP/JSON web service interface, has + nut-scanner wrapper; jNutWebAPI, a HTTP/JSON web service interface, has also been added to interact with upsd and nut-scanner. - Base files for HPUX packaging have been added. This is still a work in @@ -864,8 +1701,9 @@ Release notes for NUT 2.6.3 - what's new since 2.6.2: - more bugfixes, cleanup and improvements, on both source code and documentation. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.2 - what's new since 2.6.1: + +Release notes for NUT 2.6.2 - what's new since 2.6.1 +---------------------------------------------------- - NUT can now monitor power supply units (PSU) in servers, through IPMI, using the new experimental 'nut-ipmipsu' driver. Users are encouraged to test it, @@ -877,13 +1715,18 @@ Release notes for NUT 2.6.2 - what's new since 2.6.1: - NUT now provides a tool, called 'nut-scanner', to discover supported devices, both local and remote. nut-scanner will help to ease the configuration step, and power infrastructure discovery. + + + + This development, sponsored by Eaton, supports the following methods: * USB, * SNMP, * XML/HTTP (from Eaton), * NUT servers, using the classic connect or Avahi / mDNS methods. - + + + + IPMI support will be added in the next release. + + + + A separate library, called 'libnutscan', is also available to provide these feature. Future NUT releases will provides binding for the supported languages (Perl, Python and Java). @@ -892,14 +1735,21 @@ Release notes for NUT 2.6.2 - what's new since 2.6.1: This development, sponsored by Eaton, is currently limited to the client interface. But it will be broaden to device discovery and configuration in the future. + + + + For more info, refer to nut/scripts/java/README, or the developer guide (chapter 'Creating new client'). Javadoc documentation is also provided, along with Java archives (.jar) in the Download section. - - support for new devices: Eaton 3S ; Cyber Power Systems CP1000AVRLCD ; - various APC models equipped with APC AP9618 management card, including APC - Smart-UPS RT XL ; Orvaldi 750 / 900SP ; POWEREX VI 1000 LED ; PowerWalker - VI 850 LCD ; SVEN Power Pro+ series (USB ID ffff:0000). + - support for new devices: + * Eaton 3S + * Cyber Power Systems CP1000AVRLCD + * various APC models equipped with APC AP9618 management card, including + APC Smart-UPS RT XL + * Orvaldi 750 / 900SP + * POWEREX VI 1000 LED + * PowerWalker VI 850 LCD + * SVEN Power Pro+ series (USB ID ffff:0000) - A regression has been fixed in udev rules file. This previously caused permission issues to owners of some USB devices. @@ -908,16 +1758,16 @@ Release notes for NUT 2.6.2 - what's new since 2.6.1: service file (nut/scripts/avahi/nut.service). - usbhid-ups has had Eaton completion: some features have been improved, such - as 'output.voltage.nominal' ; 3S Eco control support has been added, along + as 'output.voltage.nominal'; 3S Eco control support has been added, along with battery.runtime.low and end of battery life (life cycle monitoring) - support ; new measurements for 5 PX are also supported now (outlet power + support; new measurements for 5 PX are also supported now (outlet power factor, power, real power and current). - - apcsmart has been updated to support more variables and features ; the + - apcsmart has been updated to support more variables and features; the previous driver is however still available as 'apcsmart-old', in case of issues. - - bcmxcp now supports per outlet startup and shutdown delays setting ; shutdown + - bcmxcp now supports per outlet startup and shutdown delays setting; shutdown delay is also used, when available, for outlet.n.shutdown.return instead of the default 3 seconds. @@ -937,8 +1787,9 @@ Release notes for NUT 2.6.2 - what's new since 2.6.1: - Finally, after years of dedication to NUT, Arjen de Korte is now retired. Sincere thanks to you Arjen from us all. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.1 - what's new since 2.6.0: + +Release notes for NUT 2.6.1 - what's new since 2.6.0 +---------------------------------------------------- - the various recent USB regressions have been definitely fixed. @@ -948,10 +1799,16 @@ Release notes for NUT 2.6.1 - what's new since 2.6.0: - the Perl module from Gabor Kiss (rewritten from Kit Peters') is now distributed with NUT source code. - - support for new devices: Eaton Ellipse ECO, Powerware 9140, Eaton 5 PX, and - ambient sensor on Eaton ePDU managed ; GE EP series ; Inform Sinus SS 210 ; - IPAR Mini Energy ME 800 ; Mustek Yukai PowerMust 1000 USB ; Numeric 3000 SW ; - SVEN Power Pro+ series (recent models) ; Vivaldi EA200 LED. + - support for new devices: + * Eaton Ellipse ECO, Powerware 9140, Eaton 5 PX, and ambient sensor + on Eaton ePDU managed + * GE EP series + * Inform Sinus SS 210 + * IPAR Mini Energy ME 800 + * Mustek Yukai PowerMust 1000 USB + * Numeric 3000 SW + * SVEN Power Pro+ series (recent models) + * Vivaldi EA200 LED - liebert-esp2: Improved Liebert ESP II support, including UPS shutdown (poweroff), 1 and 3-phase input and output variables, and most @@ -982,27 +1839,42 @@ Release notes for NUT 2.6.1 - what's new since 2.6.0: - more bugfixes, cleanup and improvements, on both source code and documentation. ---------------------------------------------------------------------------- -Release notes for NUT 2.6.0 - what's new since 2.4.3: + +Release notes for NUT 2.6.0 - what's new since 2.4.3 +---------------------------------------------------- + +NOTE: Per original semantic versioning, there were no public NUT 2.5.x releases. - the main focus of this release is the complete documentation revamping, using AsciiDoc. This includes a new website, user manual, developer guide, packager guide and manual pages, available in various formats (single and multiple pages HTML, and PDF at the moment). - Be sure to check the --with-doc configure option help, and - docs/configure.txt for more information. + + + + + Be sure to check the `--with-doc` option help of `configure` script, and + link:docs/configure.txt[] for more information. - Add Augeas support, to provide easy NUT configuration management, through tools and development APIs. For more information, refer to the developer - guide, or scripts/augeas/README in the source directory. + guide, or link:scripts/augeas/README.adoc[] in the source directory. - - support for new devices: APC 5G; Eaton PowerWare 5119 RM (smart mode using - upscode2 driver), Eaton Best Ferrups (using older ConnectUPS card), - Eaton 9395 (serial interface), Eaton ConnectUPS X / BD / E Slot; - HP T1000 INTL, HP T1500 INTL, HP T750 G2, HP R1500 G2 INTL; iDowell iBox UPS; - Tripp Lite SmartOnline SU1000XLA, Tripp Lite Smart1000LCD, and some - more USB/HID devices IDs; CyberPower CP1500AVRLCD and CP1350AVRLCD; - PowerWalker Line-Interactive VI 1400 ; Rocketfish RF-1000VA / RF-1025VA. + - support for new devices: + * APC 5G + * Eaton PowerWare 5119 RM (smart mode using upscode2 driver) + * Eaton Best Ferrups (using older ConnectUPS card) + * Eaton 9395 (serial interface) + * Eaton ConnectUPS X / BD / E Slot + * HP T1000 INTL + * HP T1500 INTL + * HP T750 G2 + * HP R1500 G2 INTL + * iDowell iBox UPS + * Tripp Lite SmartOnline SU1000XLA + * Tripp Lite Smart1000LCD + * and some more USB/HID devices IDs + * CyberPower CP1500AVRLCD and CP1350AVRLCD + * PowerWalker Line-Interactive VI 1400 + * Rocketfish RF-1000VA / RF-1025VA - usbhid-ups has better support for shutting down APC SmartUPS RM series, and finally fix the "buffer size" issue, which was breaking some @@ -1043,13 +1915,15 @@ Release notes for NUT 2.6.0 - what's new since 2.4.3: - many bugfixes, cleanup and improvements. ---------------------------------------------------------------------------- -Release notes for NUT 2.4.3 - what's new since 2.4.2: + +Release notes for NUT 2.4.3 - what's new since 2.4.2 +---------------------------------------------------- - this is a bugfix release that only solves the regression on IPv6 activation. ---------------------------------------------------------------------------- -Release notes for NUT 2.4.2 - what's new since 2.4.1: + +Release notes for NUT 2.4.2 - what's new since 2.4.1 +---------------------------------------------------- - the general USB support has been vastly improved, including many bug fixes, better OS support, new features and devices. @@ -1075,25 +1949,34 @@ Release notes for NUT 2.4.2 - what's new since 2.4.1: - the UPower (previously known as DeviceKit-power) rules file is now generated by NUT. - - support for new devices: Apollo 1000A and 1000F; various Baytech RPC; old - Best Power Fortress; Cyber Power Systems PR3000E, CP 1500C and OR2200LCDRM2U; - all the new Dell UPS range (serial, USB and network); Eaton E Series NV and - DX UPS, and Powerware 9130; older HP T500 and T750, newer T750 INTL (USB) and - R1500 G2 (serial); Inform Informer Compact 1000VA; many serial and USB - devices from Ippon, like Back Comfo Pro, Smart Power Pro and Smart Winner; - IVT SCD series; Liebert GXT2-3000RT230 and PowerSure PSA; Mustek PowerMust - 424 / 636 / 848 USB; all new PowerCOM USB devices with HID PDC interface; - Tripp-Lite INTERNETOFFICE700, SMART700USB and ECO550UPS; UPSonic DS-800 - (USB). - ---------------------------------------------------------------------------- -Release notes for NUT 2.4.1 - what's new since 2.4.0: + - support for new devices: + * Apollo 1000A and 1000F + * various Baytech RPC + * old Best Power Fortress + * Cyber Power Systems PR3000E, CP 1500C and OR2200LCDRM2U + * all the new Dell UPS range (serial, USB and network) + * Eaton E Series NV and DX UPS, and Powerware 9130 + * older HP T500 and T750, newer T750 INTL (USB) and R1500 G2 (serial) + * Inform Informer Compact 1000VA + * many serial and USB devices from Ippon, like Back Comfo Pro, + Smart Power Pro and Smart Winner + * IVT SCD series + * Liebert GXT2-3000RT230 and PowerSure PSA + * Mustek PowerMust 424 / 636 / 848 USB + * all new PowerCOM USB devices with HID PDC interface + * Tripp-Lite INTERNETOFFICE700, SMART700USB and ECO550UPS + * UPSonic DS-800 (USB) + + +Release notes for NUT 2.4.1 - what's new since 2.4.0 +---------------------------------------------------- - the microdowell driver has appeared to support various MicroDowell Enterprise units (see the "new devices" list below). - - support for new devices: MicroDowell Enterprise B8, B10, N8, N11, N15, N20, - N22, N30, N40, N50, N60 and HiBox ST. + - support for new devices: + * MicroDowell Enterprise B8, B10, N8, N11, N15, N20, + N22, N30, N40, N50, N60 and HiBox ST. - NUT-Monitor now better handles the ups.status field, and has switched to version 1.1. @@ -1102,33 +1985,53 @@ Release notes for NUT 2.4.1 - what's new since 2.4.0: "make clean" target and the wrongly removed generated USB files. This broke further configure call. ---------------------------------------------------------------------------- -Release notes for NUT 2.4.0 - what's new since 2.2.2: - - preliminary support for Power Distribution Units (PDUs): NUT now support +Release notes for NUT 2.4.0 - what's new since 2.2.2 +---------------------------------------------------- + +NOTE: Per original semantic versioning, there were no public NUT 2.3.x releases. + + - preliminary support for Power Distribution Units (PDUs): NUT can now support PDUs, either natively (ie using NUT snmp-ups driver), or through a binding to the Powerman daemon. The list of supported PDUs is already quite long, - including: Eaton ePDUs (Managed and Monitored), some Aphel models, some - Raritan PDUs, and the whole list of Powerman supported devices: - http://powerman.sourceforge.net/supported.html - - - support for new devices: the various PDUs cited above, Chloride Desk Power - 650, Cyber Power Systems Value 400E/600E/800E (USB models), Delta GES602N, - Digitus DN-170020, the whole Eaton ranges (mostly composed of MGE Office - Protection Systems and Powerware units) including BladeUPS, Forza Power - Technologies SL-1001, HP PowerTrust 2997A, HP R/T 2200 G2, Infosec XP 1000 - and XP 500, Ippon Back Power Pro (serial and USB), Kebo 1200D/D Series, - Liebert PowerSure Personal XT, MGE Office Protection Systems Protection - Station, Neus 400va and 600va, Phasak 400VA and 600VA, Plexus 500VA, Powercom - Black Knight PRO / King PRO and Imperial, PowerKinetics BlackOut Buster, - Sweex 1000 USB, UNITEK Alpha 500, WinPower CPM-800. + including: + * Eaton ePDUs (Managed and Monitored), + * some Aphel models, + * some Raritan PDUs, + * and the whole list of Powerman supported devices: + http://powerman.sourceforge.net/supported.html + + - support for new devices: + * the various PDUs cited above + * Chloride Desk Power 650 + * Cyber Power Systems Value 400E/600E/800E (USB models) + * Delta GES602N + * Digitus DN-170020 + * the whole Eaton ranges (mostly composed of MGE Office Protection Systems + and Powerware units) including BladeUPS + * Forza Power Technologies SL-1001 + * HP PowerTrust 2997A + * HP R/T 2200 G2 + * Infosec XP 1000 and XP 500 + * Ippon Back Power Pro (serial and USB) + * Kebo 1200D/D Series + * Liebert PowerSure Personal XT + * MGE Office Protection Systems Protection Station + * Neus 400va and 600va + * Phasak 400VA and 600VA + * Plexus 500VA + * Powercom Black Knight PRO / King PRO and Imperial + * PowerKinetics BlackOut Buster + * Sweex 1000 USB + * UNITEK Alpha 500 + * WinPower CPM-800 - NUT now embeds Python client support through the PyNUTClient module and the NUT-Monitor application. Both are from David Goncalves, and are still available from http://www.lestat.st. - For more information, refer to scripts/python/README. + For more information, refer to link:scripts/python/README.adoc[]. - - the dummy-ups driver now support a "repeater" mode. This allows it to act as + - the dummy-ups driver now supports a "repeater" mode. This allows it to act as a NUT client, and to forward data. This can be useful for supervision and load sharing purposes. @@ -1153,8 +2056,9 @@ Release notes for NUT 2.4.0 - what's new since 2.2.2: - tons of bugfixes, cleanup and improvements to make NUT stronger than ever! ---------------------------------------------------------------------------- -Release notes for NUT 2.2.2 - what's new since 2.2.1: + +Release notes for NUT 2.2.2 - what's new since 2.2.1 +---------------------------------------------------- - support for new devices: APC BACK-UPS XS LCD, Atlantis Land, Mustek Powermust Office 650, Oneac XAU models, Powerware PW5115 and @@ -1189,14 +2093,23 @@ Release notes for NUT 2.2.2 - what's new since 2.2.1: - many changes, cleanup and fixes to the NUT core and various drivers. ---------------------------------------------------------------------------- -Release notes for NUT 2.2.1 - what's new since 2.2.0: - - support for new devices: all MGE Office Protection Systems units, - Advice TopGuard 2000, Belkin F6H375-USB, Dynamix UPS1700D, Effekta RM2000MH, - Jageson Technology Jasuny USPS, Powercom SMK-1500A and SXL-1500A, - PowerWalker Line-Interactive VI 400/800 and 600, Powerware 9110, - UNITEK Alpha 2600, UPSonic CXR1000, some vintage serial APC UPSs. +Release notes for NUT 2.2.1 - what's new since 2.2.0 +---------------------------------------------------- + + - support for new devices: + * all MGE Office Protection Systems units + * Advice TopGuard 2000 + * Belkin F6H375-USB + * Dynamix UPS1700D + * Effekta RM2000MH, + * Jageson Technology Jasuny USPS + * Powercom SMK-1500A and SXL-1500A + * PowerWalker Line-Interactive VI 400/800 and 600 + * Powerware 9110 + * UNITEK Alpha 2600 + * UPSonic CXR1000 + * some vintage serial APC UPSs - the usbhid-ups driver has been improved, and fixed in many areas, through a backport of the development (trunk) version. @@ -1211,28 +2124,42 @@ Release notes for NUT 2.2.1 - what's new since 2.2.0: the upsclient pkg-config file has been fixed, and the upsclient.h file allows older NUT clients to continue using the UPSCONN structure. ---------------------------------------------------------------------------- -Release notes for NUT 2.2.0 - what's new since 2.0.5: + +Release notes for NUT 2.2.0 - what's new since 2.0.5 +---------------------------------------------------- + +NOTE: Per original semantic versioning, there were no public NUT 2.1.x releases. - The new build infrastructure, using automake, is now used. This has major impact on the compilation and installation procedures, and thus on the NUT packaging. - For more information, refer to UPGRADING and packaging/debian/ for + For more information, refer to link:UPGRADING.adoc[] and packaging/debian/ for an example of migration. - NUT now provides support for FreeDesktop Hardware Abstraction Layer (HAL) which brings full Plug And Play experience to USB UPS owners. - For more information, refer to docs/nut-hal.txt. - - - support for new devices: Ablerex 625L, ActivePower 400VA, 2000VA; - Belkin Home Office F6H350-SER, F6H500-SER, F6H650-SER; Belkin Office - Series F6C550-AVR; Belkin Universal UPS F6C100-UNV (USB), F6C1100-UNV - (USB), F6C1200-UNV (USB), F6H350deUNV (serial), F6H350ukUNV (serial), - F6H650ukUNV (serial); Compaq R3000h; Cyber Power Systems PR2200; - Dynex DX-800U; Geek Squad GS1285U; Krauler UP-M500VA; Mecer ME-2000; - MGE UPS SYSTEMS Ellipse MAX; Online Zinto D; PowerTech SMK-800; SVEN - Power Pro+ series, Power Smart RM 2000; Tripp-Lite SmartOnline - SU1500RTXL2ua, smart2200RMXL2U. + For more information, refer to link:docs/nut-hal.txt[]. + + - support for new devices: + * Ablerex 625L + * ActivePower 400VA, 2000VA; + * Belkin Home Office F6H350-SER, F6H500-SER, F6H650-SER + * Belkin Office Series F6C550-AVR + * Belkin Universal UPS F6C100-UNV (USB), F6C1100-UNV (USB), + F6C1200-UNV (USB), F6H350deUNV (serial), + F6H350ukUNV (serial), F6H650ukUNV (serial) + * Compaq R3000h + * Cyber Power Systems PR2200 + * Dynex DX-800U + * Geek Squad GS1285U + * Krauler UP-M500VA + * Mecer ME-2000 + * MGE UPS SYSTEMS Ellipse MAX + * Online Zinto D + * PowerTech SMK-800 + * SVEN Power Pro+ series + * Power Smart RM 2000 + * Tripp-Lite SmartOnline SU1500RTXL2ua, smart2200RMXL2U. - added IPv6 support, @@ -1253,21 +2180,30 @@ Release notes for NUT 2.2.0 - what's new since 2.0.5: - more generally, the NUT core and documentation, including the manpages, have been improved and updated. ---------------------------------------------------------------------------- -Release notes for NUT 2.0.5 - what's new since 2.0.4: - This release is a backport of the development version. Many changes - have already been backported previously. Thus it is more a - synchronization release, though it includes many bugfixes and support - for new models. +Release notes for NUT 2.0.5 - what's new since 2.0.4 +---------------------------------------------------- - - support for new devices: APC Smart-UPS with 6TI firmware; Belkin - Small Enterprise F6C1500-TW-RK; Compaq R3000 XR, R5500 XR; Cyber - Power 550SL, 725SL, 685AVR, 800AVR, 1200AVR, AE550; Eltek; Inform - GUARD; Microsol Rhino; Opti-UPS PowerES 420E; PowerMan RealSmart, - BackPro; Powerware PW9315 3-phase; SOLA 305; Tripp-Lite - SMART550USB, SMART2200RMXL2U, OMNI1000LCD, OMNI900LCD, OMNI650LCD, - 1500 LCD, AVR550U; Viewsonic PowerES 420E. +This release is a backport of the development version. Many changes +have already been backported previously. Thus it is more a +synchronization release, though it includes many bugfixes and support +for new models. + + - support for new devices: + * APC Smart-UPS with 6TI firmware + * Belkin Small Enterprise F6C1500-TW-RK + * Compaq R3000 XR, R5500 XR + * Cyber Power 550SL, 725SL, 685AVR, 800AVR, 1200AVR, AE550 + * Eltek + * Inform GUARD + * Microsol Rhino + * Opti-UPS PowerES 420E + * PowerMan RealSmart, BackPro + * Powerware PW9315 3-phase + * SOLA 305 + * Tripp-Lite SMART550USB, SMART2200RMXL2U, OMNI1000LCD, OMNI900LCD, + OMNI650LCD, 1500 LCD, AVR550U + * Viewsonic PowerES 420E - bcmxcp: added 3-phase support @@ -1288,8 +2224,9 @@ Release notes for NUT 2.0.5 - what's new since 2.0.4: - added HTML interface for access to CGI scripts ---------------------------------------------------------------------------- -Release notes for NUT 2.0.4 - what's new since 2.0.3: + +Release notes for NUT 2.0.4 - what's new since 2.0.3 +---------------------------------------------------- - The newhidups critical bug (segmentation fault) has been fixed. It has also received some more care, like bugfixes and new models support and @@ -1314,9 +2251,9 @@ Release notes for NUT 2.0.4 - what's new since 2.0.3: [David Kaufman] - The new megatec driver, which will replace a bunch of drivers by nut 2.2 - (refer to docs/megatec.txt and UPGRADING) has been backported from the - trunk (Development tree). The powermust driver has also received some - attention. + (refer to link:docs/megatec.txt[] and link:UPGRADING.adoc[]) has been + backported from the trunk (Development tree). The powermust driver has + also received some attention. [Carlos Rodrigues] - The new rhino driver was added to support Microsol Rhino UPS hardware @@ -1329,8 +2266,9 @@ Release notes for NUT 2.0.4 - what's new since 2.0.3: cpsups, tripplite_usb and the FAQ. [Arjen de Korte and Charles Lepple] ---------------------------------------------------------------------------- -Release notes for NUT 2.0.3 - what's new since 2.0.2: + +Release notes for NUT 2.0.3 - what's new since 2.0.2 +---------------------------------------------------- - The recent and major newhidups changes have been backported from the Development tree. It now: @@ -1387,8 +2325,9 @@ Release notes for NUT 2.0.3 - what's new since 2.0.2: - The snmp-ups driver has corrected the problem when exposing certain time data. ---------------------------------------------------------------------------- -Release notes for NUT 2.0.2 - what's new since 2.0.1: + +Release notes for NUT 2.0.2 - what's new since 2.0.1 +---------------------------------------------------- - the newhidups USB driver has been improved a lot and is no more experimental. It also now has a basic APC support, which will @@ -1443,8 +2382,9 @@ Release notes for NUT 2.0.2 - what's new since 2.0.1: - The tripplite driver has fixed a battery charge bug [Cedric Tefft] ---------------------------------------------------------------------------- -Release notes for NUT 2.0.1 - what's new since 2.0.0: + +Release notes for NUT 2.0.1 - what's new since 2.0.0 +---------------------------------------------------- - The bestuferrups driver has been forked into the new bestfcom driver which has better handling of the inverter status alarm messages and @@ -1525,9 +2465,8 @@ Release notes for NUT 2.0.1 - what's new since 2.0.0: - The fentonups driver now recognizes several more Megatec protocol units: - - SuperPower HP360, Hope-550 [Denis Zaika] - Unitek Alpha 1000is [Antoine Cuvellard] + * SuperPower HP360, Hope-550 [Denis Zaika] + * Unitek Alpha 1000is [Antoine Cuvellard] - Some variables like uc_sigmask were renamed to avoid clashes with symbols on systems like HP/UX. @@ -1545,21 +2484,24 @@ Release notes for NUT 2.0.1 - what's new since 2.0.0: snmp-ups and upsd. [Ulf Harnhammar] ---------------------------------------------------------------------------- -Release notes for NUT 2.0.0 - what's new since 1.4.x: + +Release notes for NUT 2.0.0 - what's new since 1.4.x +---------------------------------------------------- - The new naming scheme for variables and commands (introduced in 1.4) is now mandatory. The 1.4 tree supported both the old (STATUS) and the new (ups.status) as a transitional release, and now that time is over. - + + + + This means that 2.0 is generally smaller than 1.4 code, since the interim compatibility hacks have been removed. - New serial handling code has been added, with greatly simplified operations. The old mess involving repeated calls to sigaction, alarm, and read has been condensed to a select-read loop. - + + + + This change allows drivers which don't do any serial communications at all (hidups, snmp-ups) to drop that baggage, so they are a bit smaller when compiled. @@ -1572,7 +2514,8 @@ Release notes for NUT 2.0.0 - what's new since 1.4.x: POWERDOWNFLAG file. If it exists and contains the magic string, then upsmon will exit(EXIT_SUCCESS). Otherwise, it will exit(EXIT_FAILURE). - + + + + This feature can be used to simplify shutdown scripts, since now you don't have to keep the script in sync with the upsmon.conf. @@ -1584,10 +2527,11 @@ Release notes for NUT 2.0.0 - what's new since 1.4.x: - The access control system in upsd.conf has been reworked and simplified. Since access levels have become meaningless in recent releases, the new system is just ACCEPT or REJECT . - + + + + If you are upgrading from a previous version of the software, you will have to edit your upsd.conf to use this method. See - the UPGRADING file for more details. + the link:UPGRADING.adoc[] file for more details. - The build process now halts when make fails in one of the subdirectories. @@ -1669,14 +2613,16 @@ Release notes for NUT 2.0.0 - what's new since 1.4.x: code. Any failure during a requested operation will result in a nonzero value (specifically EXIT_FAILURE). ---------------------------------------------------------------------------- -Release notes for NUT 1.4.0 - what's new since 1.2.x: + +Release notes for NUT 1.4.0 - what's new since 1.2.x +---------------------------------------------------- - The drivers and upsd now communicate over Unix domain sockets instead of state files, shared memory, or state files with mmap. This change makes many things possible, including the new dynamic variable and command naming scheme described below. - + + + + There is a new development tool called sockdebug in the server directory for debugging driver-server communications on the sockets. @@ -1684,10 +2630,12 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: implementation. Vague names have been turned into meaningful names that fit into an organized system. UTILITY is now input.voltage. OUTVOLT is now output.voltage. - + + + + This also applies to the names of instant commands. BTEST1 is test.battery.start, and BTEST0 is test.battery.stop. - + + + + The old names are still supported for compatibility with older clients. This compatibility mode will be maintained throughout the 1.4 series, and will be gone by the release of 2.0. Users @@ -1701,7 +2649,8 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: - The drivers and server (upsd) can now change their user ids and chroot themselves with the new -u and -r arguments. This lets you create a "chroot jail" with the bare minimum components. - + + + + This technique is used to provide a higher degree of security. If someone exploited upsd to get a shell somehow, they would be stuck in the jail. @@ -1714,7 +2663,8 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: - upslog has been reworked to use standard getopt parsing to provide the monitoring settings. The old way of specifying arguments is still supported for backwards compatibility. - + + + + upslog has also been changed to only parse the format string once, rather than doing it every time through the loop. This should provide a minuscule drop in CPU utilization. @@ -1722,8 +2672,9 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: - Usernames are now required in upsmon and upsd. This means that you must add a username to your MONITOR lines in upsmon.conf and then create a matching user in upsd.users. - - Installations from the 1.2 era probably already use usernames, so + + + + + Installations from the 1.2 era probably already used usernames, so this mostly affects those from 1.0 and before. - Drivers are now pinged regularly by upsd when they aren't posting @@ -1736,17 +2687,20 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: - upsstats now reuses connections to upsd when cycling through multiple entries in the hosts.conf. This makes things a bit faster and avoids some of the noise in the syslog. - + + + + This only applies to entries that are adjacent. To take advantage - of this feature, you may have to rearrange them. - + of this feature, you may have to rearrange them per example below. + + + + + Connection reuse for nonadjacent entries may be considered in the + future. +---- MONITOR ups-1@host-1 ... MONITOR ups-1@host-2 ... MONITOR ups-2@host-2 ... MONITOR ups-3@host-3 ... - - Connection reuse for nonadjacent entries may be considered in the - future. +---- - upsd now warns about insecure configuration files at startup. These files (upsd.conf, upsd.users, and the certfile) should @@ -1767,7 +2721,8 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: --with-group, and the programs no longer setgid() to a hardcoded value. They now setgid() to the primary group of whatever the user value may be. - + + + + This may be compiled in with --with-user as before, and many programs accept -u to override it at runtime. @@ -1809,7 +2764,8 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: - The mge-utalk driver had trouble establishing communications in some cases due to the RTS line being set. This has been fixed. - + + + + The mge-shut driver has been added to the tree, and has replaced the older mge-ellipse driver. [Arnaud Quette, Philippe Marzouk] @@ -1818,7 +2774,8 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: be added to drivers where the hardware supports it. This can be used to shut down some components earlier than others to prolong your runtime on battery. - + + + + This is supported in the mge-shut driver now, and may show up in others before long. [Arnaud Quette] @@ -1903,13 +2860,15 @@ Release notes for NUT 1.4.0 - what's new since 1.2.x: compiles on some systems. [Petter Reinholdtsen] ---------------------------------------------------------------------------- -Release notes for NUT 1.2.2 - what's new since 1.2.1: + +Release notes for NUT 1.2.2 - what's new since 1.2.1 +---------------------------------------------------- - The snmp-ups driver has been upgraded and expanded. It now supports multiple MIBs, meaning it can handle RFC 1628, APCC, and MGE equipment. You can pick the right one with "mibs=" in ups.conf. - + + + + Support for setting variable and instant commands is also available. [Arnaud Quette and Dmitry Frolov] @@ -1943,7 +2902,8 @@ Release notes for NUT 1.2.2 - what's new since 1.2.1: shutdown or just annoy users with excessive onbatt/online notify messages. The new code forces the status to settle down for 3 polls before accepting the new value. - + + + + This means that very short power events may not be detected. The alternative is having your machine shut down just because it decided to wiggle over to OB LB for a few seconds. @@ -1956,8 +2916,9 @@ Release notes for NUT 1.2.2 - what's new since 1.2.1: - upssched now uses a lock file to prevent a race where two could start at the same time. The second upssched would "win", and the first one would be unreachable. This had the side-effect of not - being able to cancel timers on the first one. - + being able to cancel timers on the first one. + + + + If you use upssched, you must define the LOCKFN directive when upgrading to this version, or it will not work. [Gaspar Bakos] @@ -1991,15 +2952,15 @@ Release notes for NUT 1.2.2 - what's new since 1.2.1: depending on what areas were accessed. [Patrik Schindler] ---------------------------------------------------------------------------- -Release notes for NUT 1.2.1 - what's new since 1.2.0: +Release notes for NUT 1.2.1 - what's new since 1.2.0 +---------------------------------------------------- - The sms driver is back, with support for Microlink Manager III hardware. [Marcio Gomes] - - Fideltronik Ares Series hardware is now supported as genericups type - 19. [Tomek Orzechowski and Arkadiusz Mikiewicz] + - Fideltronik Ares Series hardware is now supported as genericups + type 19. [Tomek Orzechowski and Arkadiusz Mikiewicz] - The drivers no longer silently drop instant commands or set commands from upsd that happen to get fragmented in transit. @@ -2017,4 +2978,10 @@ Release notes for NUT 1.2.1 - what's new since 1.2.0: - Shutdowns on FreeBSD using the genericups driver should work again. [Petri Riihikallio] ---------------------------------------------------------------------------- + +Historic releases +----------------- + +More ancient history is not covered in detail here. + +You can see link:docs/history.txt[] for more details. diff --git a/README b/README deleted file mode 100644 index 9fe1c00fc5..0000000000 --- a/README +++ /dev/null @@ -1,588 +0,0 @@ -Network UPS Tools Overview -=========================== - -Description ------------ - -Network UPS Tools is a collection of programs which provide a common -interface for monitoring and administering UPS, PDU and SCD hardware. -It uses a layered approach to connect all of the parts. - -Drivers are provided for a wide assortment of equipment. They -understand the specific language of each device and map it back to a -compatibility layer. This means both an expensive high end UPS, a simple -"power strip" PDU, or any other power device can be handled transparently with -a uniform management interface. - -This information is cached by the network server `upsd`, which then -answers queries from the clients. upsd contains a number of access -control features to limit the abilities of the clients. Only authorized -hosts may monitor or control your hardware if you wish. Since the -notion of monitoring over the network is built into the software, you -can hang many systems off one large UPS, and they will all shut down -together. You can also use NUT to power on, off or cycle your data center -nodes, individually or globally through PDU outlets. - -Clients such as `upsmon` check on the status of the hardware and do things -when necessary. The most important task is shutting down the operating -system cleanly before the UPS runs out of power. Other programs are -also provided to log information regularly, monitor status through your -web browser, and more. - - -Installing ----------- - -If you are installing these programs for the first time, go read the -<<_installation_instructions,installation instructions>> -to find out how to do that. This document contains more information on what all -of this stuff does. - - -Upgrading ---------- - -When upgrading from an older version, always check the -<> to see what may have -changed. Compatibility issues and other changes will be listed there to ease -the process. - - -Configuring and using ---------------------- - -Once NUT is installed, refer to the -<> for directions. - - -Documentation -------------- - -This is just an overview of the software. You should read the man pages, -included example configuration files, and auxiliary documentation for the parts -that you intend to use. - - -Network Information -------------------- - -These programs are designed to share information over the network. In -the examples below, `localhost` is used as the hostname. This can also -be an IP address or a fully qualified domain name. You can specify a -port number if your upsd process runs on another port. - -In the case of the program `upsc`, to view the variables on the UPS called -sparky on the `upsd` server running on the local machine, you'd do this: - - /usr/local/ups/bin/upsc sparky@localhost - -The default port number is 3493. You can change this with -"configure --with-port" at compile-time. To make a client talk to upsd -on a specific port, add it after the hostname with a colon, like this: - - /usr/local/ups/bin/upsc sparky@localhost:1234 - -This is handy when you have a mixed environment and some of the systems -are on different ports. - -The general form for UPS identifiers is this: - - [@[:]] - -Keep this in mind when viewing the examples below. - - -Manifest --------- - -This package is broken down into several categories: - -- *drivers* - These programs talk directly to your UPS hardware. -- *server* - upsd serves data from the drivers to the network. -- *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. - -Drivers -------- - -These programs provide support for specific UPS models. They understand -the protocols and port specifications which define status information -and convert it to a form that upsd can understand. - -To configure drivers, edit ups.conf. For this example, we'll have a UPS -called "sparky" that uses the apcsmart driver and is connected to -`/dev/ttyS1`. That's the second serial port on most Linux-based systems. -The entry in `ups.conf` looks like this: - - [sparky] - driver = apcsmart - port = /dev/ttyS1 - -To start and stop drivers, use upsdrvctl of upsdrvsvcctl (installed on -operating systems with a service management framework supported by NUT). -By default, it will start or stop every UPS in the config file: - - /usr/local/ups/sbin/upsdrvctl start - /usr/local/ups/sbin/upsdrvctl stop - -However, you can also just start or stop one by adding its name: - - /usr/local/ups/sbin/upsdrvctl start sparky - /usr/local/ups/sbin/upsdrvctl stop sparky - -On operating systems with a supported service management framework, -you might wrap your NUT drivers into individual services instances -with: - - /usr/local/ups/sbin/upsdrvsvcctl resync - -and then manage those service instances with commands like: - - /usr/local/ups/sbin/upsdrvsvcctl start sparky - /usr/local/ups/sbin/upsdrvsvcctl stop sparky - -To find the driver name for your device, refer to the section below -called "HARDWARE SUPPORT TABLE". - -Extra Settings -~~~~~~~~~~~~~~ - -Some drivers may require additional settings to properly communicate -with your hardware. If it doesn't detect your UPS by default, check the -driver's man page or help (-h) to see which options are available. - -For example, the usbhid-ups driver allows you to use USB serial numbers to -distinguish between units via the "serial" configuration option. To use this -feature, just add another line to your ups.conf section for that UPS: - - [sparky] - driver = usbhid-ups - port = auto - serial = 1234567890 - -Hardware Compatibility List -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The <> is available in the source directory -('nut-X.Y.Z/data/driver.list'), and is generally distributed with packages. -For example, it is available on Debian systems as: - - /usr/share/nut/driver.list - -This table is also available link:http://www.networkupstools.org/stable-hcl.html[online]. - - -If your driver has vanished, see the link:FAQ.html[FAQ] and -<>. - -Generic Device Drivers -~~~~~~~~~~~~~~~~~~~~~~ - -NUT provides several generic drivers that support a variety of very similar models. - -- The `genericups` driver supports many serial models that use the same basic -principle to communicate with the computer. This is known as "contact -closure", and basically involves raising or lowering signals to indicate -power status. -+ -This type of UPS tends to be cheaper, and only provides the very simplest -data about power and battery status. Advanced features like battery -charge readings and such require a "smart" UPS and a driver which -supports it. -+ -See the linkman:genericups[8] man page for more information. - -- The `usbhid-ups` driver attempts to communicate with USB HID Power Device -Class (PDC) UPSes. These units generally implement the same basic protocol, -with minor variations in the exact set of supported attributes. This driver -also applies several correction factors when the UPS firmware reports values -with incorrect scale factors. -+ -See the linkman:usbhid-ups[8] man page for more information. - -- The `blazer_ser` and `blazer_usb` drivers supports the Megatec / Q1 -protocol that is used in many brands (Blazer, Energy Sistem, Fenton -Technologies, Mustek and many others). -+ -See the linkman:blazer[8] man page for more information. - -- The `snmp-ups` driver handles various SNMP enabled devices, from many -different manufacturers. In SNMP terms, `snmp-ups` is a manager, that -monitors SNMP agents. -+ -See the linkman:snmp-ups[8] man page for more information. - -- The `powerman-pdu` is a bridge to the PowerMan daemon, thus handling all -PowerMan supported devices. The PowerMan project supports several serial -and networked PDU, along with Blade and IPMI enabled servers. -+ -See the linkman:powerman-pdu[8] man page for more -information. - -- The `apcupsd-ups` driver is a bridge to the Apcupsd daemon, thus handling -all Apcupsd supported devices. The Apcupsd project supports many serial, -USB and networked APC UPS. -+ -See the linkman:apcupsd-ups[8] man page for more information. - -UPS Shutdowns -~~~~~~~~~~~~~ - -upsdrvctl can also shut down (power down) all of your UPS hardware. - -WARNING: if you play around with this command, expect your filesystems -to die. Don't power off your computers unless they're ready for it: - - /usr/local/ups/sbin/upsdrvctl shutdown - /usr/local/ups/sbin/upsdrvctl shutdown sparky - -You should read the <> -chapter to learn more about when to use this feature. If called at the wrong -time, you may cause data loss by turning off a system with a filesystem -mounted read-write. - -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. - -Network Server --------------- - -`upsd` is responsible for passing data from the drivers to the client -programs via the network. It should be run immediately after `upsdrvctl` -in your system's startup scripts. - -`upsd` should be kept running whenever possible, as it is the only source -of status information for the monitoring clients like `upsmon`. - - -Monitoring client ------------------ - -`upsmon` provides the essential feature that you expect to find in UPS -monitoring software: safe shutdowns when the power fails. - -In the layered scheme of NUT software, it is a client. It has this -separate section in the documentation since it is so important. - -You configure it by telling it about UPSes that you want to monitor in -upsmon.conf. Each UPS can be defined as one of two possible types: -a "primary" or "secondary". - -Primary -~~~~~~~ - -The monitored UPS possibly supplies power to this system running `upsmon`, -but more importantly -- this system can manage the UPS (typically, this -instance of `upsmon` runs on the same system as the `upsd` and driver(s)): -it is capable and responsible for shutting it down when the battery is -depleted (or in another approach, lingering to deplete it or to tell the -UPS to reboot its load after too much time has elapsed and this system -is still alive -- meaning wall power returned at a "wrong" moment). - -The shutdown of this (primary) system itself, as well as eventually an -UPS shutdown, occurs after any secondary systems ordered to shut down -first have disconnected, or a critical urgency threshold was passed. - -If your UPS is plugged directly into a system's serial or USB port, the -`upsmon` process on that system should define its relation to that UPS -as a primary. It may be more complicated for higher-end UPSes with a -shared network management capability (typically via SNMP) or several -serial/USB ports that can be used simultaneously, and depends on what -vendors and drivers implement. Setups with several competing primaries -(for redundancy) are technically possible, if each one runs its own -full stack of NUT, but results can be random (currently NUT does not -provide a way to coordinate several entities managing the same device). - -For a typical home user, there's one computer connected to one UPS. -That means you would run on the same computer the whole NUT stack -- -a suitable driver, `upsd`, and `upsmon` in primary mode. - -Secondary -~~~~~~~~~ - -The monitored UPS may supply power to the system running `upsmon` (or -alternatively, it may be a monitoring station with zero PSUs fed by -that UPS), but more importantly, this system can't manage the UPS -- -e.g. shut it down directly (through a locally running NUT driver). - -Use this mode when you run multiple computers on the same UPS. -Obviously, only one can be connected to the serial or USB port -on a typical UPS, and that system is the primary. Everything -else is a secondary. - -For a typical home user, there's one computer connected to one UPS. -That means you run a driver, `upsd`, and `upsmon` in primary mode. - -Additional Information -~~~~~~~~~~~~~~~~~~~~~~ - -More information on configuring upsmon can be found in these places: - -- The linkman:upsmon[8] man page -- <> -- <> chapter -- The stock `upsmon.conf` that comes with the package - - -Clients -------- - -Clients talk to upsd over the network and do useful things with the data -from the drivers. There are tools for command line access, and a few -special clients which can be run through your web server as CGI -programs. - -For more details on specific programs, refer to their man pages. - -upsc -~~~~ - -`upsc` is a simple client that will display the values of variables known -to `upsd` and your UPS drivers. It will list every variable by default, -or just one if you specify an additional argument. This can be useful -in shell scripts for monitoring something without writing your own -network code. - -`upsc` is a quick way to find out if your driver(s) and upsd are working -together properly. Just run `upsc ` to see what's going on, i.e.: - - morbo:~$ upsc sparky@localhost - ambient.humidity: 035.6 - ambient.humidity.alarm.maximum: NO,NO - ambient.humidity.alarm.minimum: NO,NO - ambient.temperature: 25.14 - ... - -If you are interested in writing a simple client that monitors `upsd`, -the source code for `upsc` is a good way to learn about using the -upsclient functions. - -See the linkman:upsc[8] man page and -<> for more information. - -upslog -~~~~~~ - -`upslog` will write status information from `upsd` to a file at set -intervals. You can use this to generate graphs or reports with other -programs such as `gnuplot`. - -upsrw -~~~~~ - -`upsrw` allows you to display and change the read/write variables in your -UPS hardware. Not all devices or drivers implement this, so this may -not have any effect on your system. - -A driver that supports read/write variables will give results like this: - - $ upsrw sparky@localhost - - ( many skipped ) - - [ups.test.interval] - Interval between self tests - Type: ENUM - Option: "1209600" - Option: "604800" SELECTED - Option: "0" - - ( more skipped ) - -On the other hand, one that doesn't support them won't print anything: - - $ upsrw fenton@gearbox - - ( nothing ) - -`upsrw` requires administrator powers to change settings in the hardware. -Refer to linkman:upsd.users[5] for information on defining -users in `upsd`. - -upscmd -~~~~~~ - -Some UPS hardware and drivers support the notion of an instant command - -a feature such as starting a battery test, or powering off the load. -You can use upscmd to list or invoke instant commands if your -hardware/drivers support them. - -Use the -l command to list them, like this: - - $ upscmd -l sparky@localhost - Instant commands supported on UPS [sparky@localhost]: - - load.on - Turn on the load immediately - test.panel.start - Start testing the UPS panel - calibrate.start - Start run time calibration - calibrate.stop - Stop run time calibration - ... - -`upscmd` requires administrator powers to start instant commands. -To define users and passwords in `upsd`, see -linkman:upsd.users[5]. - - -CGI Programs ------------- - -The CGI programs are clients that run through your web server. They -allow you to see UPS status and perform certain administrative commands -from any web browser. Javascript and cookies are not required. - -These programs are not installed or compiled by default. To compile -and install them, first run `configure --with-cgi`, then do `make` and -`make install`. If you receive errors about "gd" during configure, go -get it and install it before continuing. - -You can get the source here: - - http://www.libgd.org/ - -In the event that you need libpng or zlib in order to compile gd, -they can be found at these URLs: - - http://www.libpng.org/pub/png/pngcode.html - - http://www.gzip.org/zlib/ - - -Access Restrictions -~~~~~~~~~~~~~~~~~~~ - -The CGI programs use hosts.conf to see if they are allowed to talk to a -host. This keeps malicious visitors from creating queries from your web -server to random hosts on the Internet. - -If you get error messages that say "Access to that host is not -authorized", you're probably missing an entry in your hosts.conf. - -upsstats -~~~~~~~~ - -`upsstats` generates web pages from HTML templates, and plugs in status -information in the right places. It looks like a distant relative of -APC's old Powerchute interface. You can use it to monitor several -systems or just focus on one. - -It also can generate IMG references to `upsimage`. - -upsimage -~~~~~~~~ - -This is usually called by upsstats via IMG SRC tags to draw either the -utility or outgoing voltage, battery charge percent, or load percent. - -upsset -~~~~~~ - -`upsset` provides several useful administration functions through a web -interface. You can use `upsset` to kick off instant commands on your UPS -hardware like running a battery test. You can also use it to change -variables in your UPS that accept user-specified values. - -Essentially, `upsset` provides the functions of `upsrw` and `upscmd`, but -with a happy pointy-clicky interface. - -`upsset` will not run until you convince it that you have secured your -system. You *must* secure your CGI path so that random interlopers -can't run this program remotely. See the `upsset.conf` file. Once you -have secured the directory, you can enable this program in that -configuration file. It is not active by default. - - -Version Numbering ------------------ - -The version numbers work like this: if the middle number is odd, it's a -development tree, otherwise it is the stable tree. - -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. - -Major release jumps are mostly due to large changes to the features -list. There have also been a number of architectural changes which -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 -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 -so many new variables and commands. The resulting mess grew out of -control over the years. - -During the 1.3 development cycle, all variables and instant commands -were renamed to fit into a tree-like structure. There are major groups, -like input, output and battery. Members of those groups have been -arranged to make sense - input.voltage and output.voltage compliment -each other. The old names were UTILITY and OUTVOLT. The benefits in -this change are obvious. - -The 1.4 clients can talk to either type of server, and can handle either -naming scheme. 1.4 servers have a compatibility mode where they can -answer queries for both names, even though the drivers are internally -using the new format. - -When 1.4 clients talk to 1.4 or 2.0 (or more recent) servers, they will -use the new names. - -Here's a table to make it easier to visualize: - -[options="header"] -|============================================= -| 4+| Server version -| *Client version* | 1.0 | 1.2 | 1.4 | 2.0+ -| 1.0 | yes | yes | yes | no -| 1.2 | yes | yes | yes | no -| 1.4 | yes | yes | yes | yes -| 2.0+ | no | no | yes | yes -|============================================= - -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" -instead. - -Authors of separate monitoring programs should have used the 1.4 series -to write support for the new variables and command names. Client -software can easily support both versions as long as they like. If upsd -returns 'ERR UNKNOWN-COMMAND' to a GET request, you need to use REQ. - - -Support / Help / etc. ---------------------- - -If you are in need of help, refer to the -<> in the user manual. - - -Hacking / Development Info --------------------------- - -Additional documentation can be found in: - -- the linkdoc:developer-guide[Developer Guide], -- the linkdoc:packager-guide[Packager Guide]. - - -Acknowledgements / Contributions --------------------------------- - -The many people who have participated in creating and improving NUT are -listed in the user manual <>. diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000000..2164db7f4a --- /dev/null +++ b/README.adoc @@ -0,0 +1,838 @@ +Network UPS Tools Overview +========================== +// NOTE: No blank line here, document-header include processing should kick in! +//GH_MARKUP_1095//ifdef::top_srcdir[] +//GH_MARKUP_1095//include::{top_srcdir}docs/asciidoc-vars.conf[] +//GH_MARKUP_1095//endif::top_srcdir[] +//GH_MARKUP_1095//ifndef::top_srcdir[] +//GH_MARKUP_1095//include::docs/asciidoc-vars.conf[] +//GH_MARKUP_1095//endif::top_srcdir[] +//GH_MARKUP_1095_INCLUDE_BEGIN//7c5e90132 (2023-09-13) docs/asciidoc-vars.conf: fence against re-definitions of website-url and (top_)(src|build)dir attributes +ifndef::asciidoc-vars-nut-included[] +:asciidoc-vars-nut-included: true +// NOTE: The big block of comments and definitions below comes from +// NUT::docs/asciidoc-vars.conf and is included into top-level document +// sources by maintenance recipes directly (`make maintainer-asciidocs`), +// due to current limitations of the GitHub Web UI asciidoc renderer. +// Hopefully it can be dropped in favor of compact include definitions +// (see README.adoc for anticipated example) after this issue is resolved +// on their side: +// * https://github.com/github/markup/issues/1095 +// +// This file should be included into NUT documentation sources to consistently +// define certain expandable attributes, with contents defined based on the +// rendition target (e.g. GitHub Web UI, plain text, locally built HTML/PDF...) +// Note that currently GitHub Web UI references lead to nut-website (as of +// last built and published revision), not to neighboring documents in the +// source browser (which would make sense for branch revisions, etc.) due +// to certain complexity about referencing other-document sections with a +// partially functional rendering engine there. Exploration and fixes are +// welcome (actually working links like +// https://github.com/networkupstools/nut/tree/master#installing or +// https://github.com/networkupstools/nut/blob/master/UPGRADING.adoc#changes-from-274-to-280 +// do seem promising)! +// +// Since the GitHub UI does not allow use of custom asciidoc configuration +// files, or generally does not process the `include:` requests at this time, +// clumsy expandable attributes had to be used (usually a set including a +// prefix with meaningful name, and one or more separators and/or a suffix +// with shortened names). For our classic documentation renditions, they +// should resolve to properly defined macros from `docs/asciidoc.conf` +// (usually named same as the variables defined here, for simplicity): +// * `linkdoc` allows to refer to a file under `docs/` directory (or +// its nut-website rendition). +// * `xref` substitutes the asciidoc shorthand '<< >>' syntax with +// attributes that conditionally expand to: +// - links on GitHub (references can point at most to a section of +// level docs/common.xsl's ), or +// - xref asciidoc macros when generating docs. +// * `linksingledoc` guarantees that, when chunked HTML is generated, +// the link always points to a non-chunked file. +// * `linkman2` allows to support different names for the manpage and +// the command shown. This is also needed to properly display links +// to manpages in both GitHub and generated docs without defining an +// attribute for each manpage. +// +// Optional attributes set by callers: +// * `website-url` (defaulted below) may be used for "historic website" +// snapshot builds... hopefully +// * `website` is used as a boolean toggle in our recipes for nut-website +// vs. offline documentation renditions +// * `env-github` is used as a boolean toggle, set by GitHub Web-UI renderer +// * `(top_)srcdir` and `(top_)builddir` can be set by `Makefile.am` +// calling the `a2x` tool, since some of the files with the asciidoc +// mark-up are only generated or post-processed during build and +// (due to `make dist` restrictions) being build products, they may +// not reside in same directory as static source text files which +// reference or include them. Note that the non-`top` paths would +// normally differ based on location of the `Makefile` involved +// (e.g. workspace root, or the `docs`, or `docs/man` directories). +// These variables are expected to be absolute paths, or ones relative +// to asciidoc-selected `:base_dir`, and to end with a relevant path +// separator, or be empty -- so in all cases letting the resulting +// string resolve meaningfully in the filesystem during docs build. +// +// Please keep the remaining comments and definitions as one big block +// so it does not become a series of empty paragraphs in the rendered +// documents! +// +ifndef::website-url[] +:website-url: https://www.networkupstools.org/ +endif::website-url[] +// +ifndef::srcdir[] +:srcdir: +endif::srcdir[] +// +ifndef::builddir[] +:builddir: +endif::builddir[] +// +ifndef::top_srcdir[] +:top_srcdir: +endif::top_srcdir[] +// +ifndef::top_builddir[] +:top_builddir: +endif::top_builddir[] +// +// +// Address links on GitHub vs docs +// (note: 'env-github' attribute is set on GitHub) +// +// - when generating docs: +ifndef::env-github[] +// * xref -> xref +// syntax: {xref}{x-s}[] +// -> xref:[] +:xref: xref: +:x-s: +// * link to doc -> our macro +// syntax: {linkdoc}{ld-s}[] +// -> linkdoc:[] +:linkdoc: linkdoc: +:ld-s: +// * link to single doc -> our macro +// syntax: {linksingledoc}{lsd-s}[] +// -> linksingledoc:[] +:linksingledoc: linksingledoc: +:lsd-s: +// * link to manpage -> our macro +// syntax: {linkman2}{lm-s}{lm-c}{lm-e} +// -> linkman2:[,] +:linkman2: linkman2: +:lm-s: [ +:lm-c: , +:lm-e: ] +endif::env-github[] +// +// - on GitHub: +ifdef::env-github[] +// In our normal builds, Makefile variables convey the needed paths +// (used relatively below as `image:images/ci/...png` etc.) +:imagesdir: docs +// * xref -> link +// syntax: {xref}{x-s}[] +// In order for it to work, can reference at most a section of +// level docs/common.xsl's +// -> {website-url}docs/user-manual.chunked/.html[] +:xref: {website-url}docs/user-manual.chunked/ +:x-s: .html +// * link to doc -> link +// syntax: {linkdoc}{ld-s}[] +// -> {website-url}docs/.chunked/index.html[] +:linkdoc: {website-url}docs/ +:ld-s: .chunked/index.html +// * link to single doc -> link +// syntax: {linksingledoc}{lsd-s}[] +// -> {website-url}docs/.html[] +:linksingledoc: {website-url}docs/ +:lsd-s: .html +// * link to manpage -> link +// syntax: {linkman2}{lm-s}{lm-c}{lm-e} +// All the fields are mandatory. +// -> {website-url}docs/man/.html[()] +:linkman2: {website-url}docs/man/ +:lm-s: .html[ +:lm-c: ( +:lm-e: )] +endif::env-github[] +endif::asciidoc-vars-nut-included[] +// +//GH_MARKUP_1095_INCLUDE_END// + + +Description +----------- + +Network UPS Tools is a collection of programs which provide a common +interface for monitoring and administering UPS, PDU and SCD hardware. +It uses a layered approach to connect all of the parts. + +Drivers are provided for a wide assortment of equipment. They +understand the specific language of each device and map it back to a +compatibility layer. This means both an expensive high end UPS, a simple +"power strip" PDU, or any other power device can be handled transparently +with a uniform management interface. + +This information is cached by the network server `upsd`, which then +answers queries from the clients. upsd contains a number of access +control features to limit the abilities of the clients. Only authorized +hosts may monitor or control your hardware if you wish. Since the +notion of monitoring over the network is built into the software, you +can hang many systems off one large UPS, and they will all shut down +together. You can also use NUT to power on, off or cycle your data center +nodes, individually or globally through PDU outlets. + +Clients such as `upsmon` check on the status of the hardware and do things +when necessary. The most important task is shutting down the operating +system cleanly before the UPS runs out of power. Other programs are +also provided to log information regularly, monitor status through your +web browser, and more. + + +NUT and the ecosystem +--------------------- + +NUT comes pre-packaged for many operating systems and embedded in storage, +automation or virtualization appliances, and is also often shipped as the +software companion by several UPS vendors. Of course, it is quite normal +and supported to build your own -- whether for an operating system which +lacks it yet, or for an older distribution which lacks the current NUT +version; whether to take advantage of new features or to troubleshoot a +new UPS deployment with a debugger in hand. + +Given its core position at the heart of your systems' lifecycle, we make +it a point to have current NUT building and running anywhere, especially +where older releases did work before (including "abandonware" like the +servers and OSes from the turn of millennium): if those boxes are still +alive and in need of power protection, they should be able to get it. + +[TIP] +===== +If you like how the NUT project helps protect your systems from power +outages, please consider sponsoring or at least "starring" it on GitHub at +https://github.com/networkupstools/nut/ - these stars are among metrics +which the larger potential sponsors consider when choosing how to help +FOSS projects. Keeping the lights shining in such a large non-regression +build matrix is a big undertaking! + +See <> for an overview of the shared effort. +===== + +As a FOSS project, for over a quarter of a century we welcome contributions +of both core code (drivers and other features), build recipes and other +integration elements to make it work on your favourite system, documentation +revisions to make it more accessible to newcomers, as well as hardware vendor +cooperation with first-hand driver and protocol submissions, and just about +anything else you can think of. + + +Installing +---------- + +If you are installing these programs for the first time, go read the +{xref}_installation_instructions{x-s}[installation instructions] +to find out how to do that. This document contains more information +on what all of this stuff does. + + +Upgrading +--------- + +When upgrading from an older version, always check the +{xref}Upgrading_notes{x-s}[upgrading notes] to see what may have +changed. Compatibility issues and other changes will be listed there +to ease the process. + + +Configuring and using +--------------------- + +Once NUT is installed, refer to the +{xref}Configuration_notes{x-s}[configuration notes] for directions. + + +Documentation +------------- + +This is just an overview of the software. You should read the man pages, +included example configuration files, and auxiliary documentation for the +parts that you intend to use. + + +Network Information +------------------- + +These programs are designed to share information over the network. In +the examples below, `localhost` is used as the hostname. This can also +be an IP address or a fully qualified domain name. You can specify a +port number if your upsd process runs on another port. + +In the case of the program `upsc`, to view the variables on the UPS called +sparky on the `upsd` server running on the local machine, you'd do this: + + /usr/local/ups/bin/upsc sparky@localhost + +The default port number is 3493. You can change this with +"configure --with-port" at compile-time. To make a client talk to upsd +on a specific port, add it after the hostname with a colon, like this: + + /usr/local/ups/bin/upsc sparky@localhost:1234 + +This is handy when you have a mixed environment and some of the systems +are on different ports. + +The general form for UPS identifiers is this: + + [@[:]] + +Keep this in mind when viewing the examples below. + + +Manifest +-------- + +This package is broken down into several categories: + +- *drivers* - These programs talk directly to your UPS hardware. +- *server* - upsd serves data from the drivers to the network. +- *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. + +Drivers +------- + +These programs provide support for specific UPS models. They understand +the protocols and port specifications which define status information +and convert it to a form that upsd can understand. + +To configure drivers, edit ups.conf. For this example, we'll have a UPS +called "sparky" that uses the apcsmart driver and is connected to +`/dev/ttyS1`. That's the second serial port on most Linux-based systems. +The entry in `ups.conf` looks like this: + + [sparky] + driver = apcsmart + port = /dev/ttyS1 + +To start and stop drivers, use upsdrvctl of upsdrvsvcctl (installed on +operating systems with a service management framework supported by NUT). +By default, it will start or stop every UPS in the config file: + + /usr/local/ups/sbin/upsdrvctl start + /usr/local/ups/sbin/upsdrvctl stop + +However, you can also just start or stop one by adding its name: + + /usr/local/ups/sbin/upsdrvctl start sparky + /usr/local/ups/sbin/upsdrvctl stop sparky + +On operating systems with a supported service management framework, +you might wrap your NUT drivers into individual services instances +with: + + /usr/local/ups/sbin/upsdrvsvcctl resync + +and then manage those service instances with commands like: + + /usr/local/ups/sbin/upsdrvsvcctl start sparky + /usr/local/ups/sbin/upsdrvsvcctl stop sparky + +To find the driver name for your device, refer to the section below +called "HARDWARE SUPPORT TABLE". + +Extra Settings +~~~~~~~~~~~~~~ + +Some drivers may require additional settings to properly communicate +with your hardware. If it doesn't detect your UPS by default, check the +driver's man page or help (-h) to see which options are available. + +For example, the usbhid-ups driver allows you to use USB serial numbers to +distinguish between units via the "serial" configuration option. To use this +feature, just add another line to your ups.conf section for that UPS: + + [sparky] + driver = usbhid-ups + port = auto + serial = 1234567890 + +Hardware Compatibility List +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The {xref}HCL{x-s}[Hardware Compatibility List] is available in the source directory +('nut-X.Y.Z/data/driver.list'), and is generally distributed with packages. +For example, it is available on Debian systems as: + + /usr/share/nut/driver.list + +This table is also available link:{website-url}stable-hcl.html[online]. + + +If your driver has vanished, see the {linksingledoc}FAQ{lsd-s}[FAQ] and +{xref}Upgrading_notes{x-s}[Upgrading notes]. + +Generic Device Drivers +~~~~~~~~~~~~~~~~~~~~~~ + +NUT provides several generic drivers that support a variety of very similar +models. + +- The `genericups` driver supports many serial models that use the same basic +principle to communicate with the computer. This is known as "contact +closure", and basically involves raising or lowering signals to indicate +power status. ++ +This type of UPS tends to be cheaper, and only provides the very simplest +data about power and battery status. Advanced features like battery +charge readings and such require a "smart" UPS and a driver which +supports it. ++ +See the {linkman2}genericups{lm-s}genericups{lm-c}8{lm-e} man page for more information. + +- The `usbhid-ups` driver attempts to communicate with USB HID Power Device +Class (PDC) UPSes. These units generally implement the same basic protocol, +with minor variations in the exact set of supported attributes. This driver +also applies several correction factors when the UPS firmware reports values +with incorrect scale factors. ++ +See the {linkman2}usbhid-ups{lm-s}usbhid-ups{lm-c}8{lm-e} man page for more information. + +- The `nutdrv_qx` driver supports the Megatec / Q1 protocol that is used in +many brands (Blazer, Energy Sistem, Fenton Technologies, Mustek, Voltronic +Power and many others). ++ +See the {linkman2}nutdrv_qx{lm-s}nutdrv_qx{lm-c}8{lm-e} man page for more information. + +- The `snmp-ups` driver handles various SNMP enabled devices, from many +different manufacturers. In SNMP terms, `snmp-ups` is a manager, that +monitors SNMP agents. ++ +See the {linkman2}snmp-ups{lm-s}snmp-ups{lm-c}8{lm-e} man page for more information. + +- The `powerman-pdu` is a bridge to the PowerMan daemon, thus handling all +PowerMan supported devices. The PowerMan project supports several serial +and networked PDU, along with Blade and IPMI enabled servers. ++ +See the {linkman2}powerman-pdu{lm-s}powerman-pdu{lm-c}8{lm-e} man page for more +information. + +- The `apcupsd-ups` driver is a bridge to the Apcupsd daemon, thus handling +all Apcupsd supported devices. The Apcupsd project supports many serial, +USB and networked APC UPS. ++ +See the {linkman2}apcupsd-ups{lm-s}apcupsd-ups{lm-c}8{lm-e} man page for more information. + +UPS Shutdowns +~~~~~~~~~~~~~ + +upsdrvctl can also shut down (power down) all of your UPS hardware. + +WARNING: if you play around with this command, expect your filesystems +to die. Don't power off your computers unless they're ready for it: + + /usr/local/ups/sbin/upsdrvctl shutdown + /usr/local/ups/sbin/upsdrvctl shutdown sparky + +You should read the {xref}UPS_shutdown{x-s}[Configuring automatic UPS shutdowns] +chapter to learn more about when to use this feature. If called at the wrong +time, you may cause data loss by turning off a system with a filesystem +mounted read-write. + +Power distribution unit management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +NUT also provides an advanced support for power distribution units. + +You should read the +{xref}outlet_management{x-s}[NUT outlets management and PDU notes] +chapter to learn more about when to use this feature. + +Network Server +-------------- + +`upsd` is responsible for passing data from the drivers to the client +programs via the network. It should be run immediately after `upsdrvctl` +in your system's startup scripts. + +`upsd` should be kept running whenever possible, as it is the only source +of status information for the monitoring clients like `upsmon`. + + +Monitoring client +----------------- + +`upsmon` provides the essential feature that you expect to find in UPS +monitoring software: safe shutdowns when the power fails. + +In the layered scheme of NUT software, it is a client. It has this +separate section in the documentation since it is so important. + +You configure it by telling it about UPSes that you want to monitor in +upsmon.conf. Each UPS can be defined as one of two possible types: +a "primary" or "secondary". + +Primary +~~~~~~~ + +The monitored UPS possibly supplies power to this system running `upsmon`, +but more importantly -- this system can manage the UPS (typically, this +instance of `upsmon` runs on the same system as the `upsd` and driver(s)): +it is capable and responsible for shutting it down when the battery is +depleted (or in another approach, lingering to deplete it or to tell the +UPS to reboot its load after too much time has elapsed and this system +is still alive -- meaning wall power returned at a "wrong" moment). + +The shutdown of this (primary) system itself, as well as eventually an +UPS shutdown, occurs after any secondary systems ordered to shut down +first have disconnected, or a critical urgency threshold was passed. + +If your UPS is plugged directly into a system's serial or USB port, the +`upsmon` process on that system should define its relation to that UPS +as a primary. It may be more complicated for higher-end UPSes with a +shared network management capability (typically via SNMP) or several +serial/USB ports that can be used simultaneously, and depends on what +vendors and drivers implement. Setups with several competing primaries +(for redundancy) are technically possible, if each one runs its own +full stack of NUT, but results can be random (currently NUT does not +provide a way to coordinate several entities managing the same device). + +For a typical home user, there's one computer connected to one UPS. +That means you would run on the same computer the whole NUT stack -- +a suitable driver, `upsd`, and `upsmon` in primary mode. + +Secondary +~~~~~~~~~ + +The monitored UPS may supply power to the system running `upsmon` (or +alternatively, it may be a monitoring station with zero PSUs fed by +that UPS), but more importantly, this system can't manage the UPS -- +e.g. shut it down directly (through a locally running NUT driver). + +Use this mode when you run multiple computers on the same UPS. +Obviously, only one can be connected to the serial or USB port +on a typical UPS, and that system is the primary. Everything +else is a secondary. + +For a typical home user, there's one computer connected to one UPS. +That means you run a driver, `upsd`, and `upsmon` in primary mode. + +Additional Information +~~~~~~~~~~~~~~~~~~~~~~ + +More information on configuring upsmon can be found in these places: + +- The {linkman2}upsmon{lm-s}upsmon{lm-c}8{lm-e} man page +- {xref}BigServers{x-s}[Typical setups for big servers] +- {xref}UPS_shutdown{x-s}[Configuring automatic UPS shutdowns] chapter +- The stock `upsmon.conf` that comes with the package + + +Clients +------- + +Clients talk to upsd over the network and do useful things with the data +from the drivers. There are tools for command line access, and a few +special clients which can be run through your web server as CGI +programs. + +For more details on specific programs, refer to their man pages. + +upsc +~~~~ + +`upsc` is a simple client that will display the values of variables known +to `upsd` and your UPS drivers. It will list every variable by default, +or just one if you specify an additional argument. This can be useful +in shell scripts for monitoring something without writing your own +network code. + +`upsc` is a quick way to find out if your driver(s) and upsd are working +together properly. Just run `upsc ` to see what's going on, i.e.: + + morbo:~$ upsc sparky@localhost + ambient.humidity: 035.6 + ambient.humidity.alarm.maximum: NO,NO + ambient.humidity.alarm.minimum: NO,NO + ambient.temperature: 25.14 + ... + +If you are interested in writing a simple client that monitors `upsd`, +the source code for `upsc` is a good way to learn about using the +upsclient functions. + +See the {linkman2}upsc{lm-s}upsc{lm-c}8{lm-e} man page and +{xref}nut-names{x-s}[NUT command and variable naming scheme] for more information. + +upslog +~~~~~~ + +`upslog` will write status information from `upsd` to a file at set +intervals. You can use this to generate graphs or reports with other +programs such as `gnuplot`. + +upsrw +~~~~~ + +`upsrw` allows you to display and change the read/write variables in your +UPS hardware. Not all devices or drivers implement this, so this may +not have any effect on your system. + +A driver that supports read/write variables will give results like this: + + $ upsrw sparky@localhost + + ( many skipped ) + + [ups.test.interval] + Interval between self tests + Type: ENUM + Option: "1209600" + Option: "604800" SELECTED + Option: "0" + + ( more skipped ) + +On the other hand, one that doesn't support them won't print anything: + + $ upsrw fenton@gearbox + + ( nothing ) + +`upsrw` requires administrator powers to change settings in the hardware. +Refer to {linkman2}upsd.users{lm-s}upsd.users{lm-c}5{lm-e} for information on defining +users in `upsd`. + +upscmd +~~~~~~ + +Some UPS hardware and drivers support the notion of an instant command - +a feature such as starting a battery test, or powering off the load. +You can use upscmd to list or invoke instant commands if your +hardware/drivers support them. + +Use the -l command to list them, like this: + + $ upscmd -l sparky@localhost + Instant commands supported on UPS [sparky@localhost]: + + load.on - Turn on the load immediately + test.panel.start - Start testing the UPS panel + calibrate.start - Start run time calibration + calibrate.stop - Stop run time calibration + ... + +`upscmd` requires administrator powers to start instant commands. +To define users and passwords in `upsd`, see +{linkman2}upsd.users{lm-s}upsd.users{lm-c}5{lm-e}. + + +CGI Programs +------------ + +The CGI programs are clients that run through your web server. They +allow you to see UPS status and perform certain administrative commands +from any web browser. Javascript and cookies are not required. + +These programs are not installed or compiled by default. To compile +and install them, first run `configure --with-cgi`, then do `make` and +`make install`. If you receive errors about "gd" during configure, go +get it and install it before continuing. + +You can get the source here: + +- http://www.libgd.org/ + +In the event that you need libpng or zlib in order to compile gd, +they can be found at these URLs: + +- http://www.libpng.org/pub/png/pngcode.html +- http://www.zlib.net/ + + +Access Restrictions +~~~~~~~~~~~~~~~~~~~ + +The CGI programs use hosts.conf to see if they are allowed to talk to a +host. This keeps malicious visitors from creating queries from your web +server to random hosts on the Internet. + +If you get error messages that say "Access to that host is not +authorized", you're probably missing an entry in your hosts.conf. + +upsstats +~~~~~~~~ + +`upsstats` generates web pages from HTML templates, and plugs in status +information in the right places. It looks like a distant relative of +APC's old Powerchute interface. You can use it to monitor several +systems or just focus on one. + +It also can generate IMG references to `upsimage`. + +upsimage +~~~~~~~~ + +This is usually called by upsstats via IMG SRC tags to draw either the +utility or outgoing voltage, battery charge percent, or load percent. + +upsset +~~~~~~ + +`upsset` provides several useful administration functions through a web +interface. You can use `upsset` to kick off instant commands on your UPS +hardware like running a battery test. You can also use it to change +variables in your UPS that accept user-specified values. + +Essentially, `upsset` provides the functions of `upsrw` and `upscmd`, but +with a happy pointy-clicky interface. + +`upsset` will not run until you convince it that you have secured your +system. You *must* secure your CGI path so that random interlopers +can't run this program remotely. See the `upsset.conf` file. Once you +have secured the directory, you can enable this program in that +configuration file. It is not active by default. + + +Version Numbering +----------------- + +The version numbers work like this: if the middle number is odd, it's a +development tree, otherwise it is the stable tree. + +The past stable trees were 1.0, 1.2, 1.4, 2.0, 2.2 and 2.4, with the +latest such stable tree designated 2.6. The development trees were 1.1, +1.3, 1.5, 2.1 and 2.3. Since the 2.4 release, there is no real separate +development branch anymore since the code is available through a revision +control system (namely, Git -- or actually Subversion back then) and its +snapshots become published releases. + +Since 2.7 line of releases, sources are tracked in Git revision control +system, with the project ecosystem being hosted on GitHub, and any code +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 +may not be noticeable to most users, but which can impact developers. + +Backwards and Forwards Compatibility +------------------------------------ + +The network protocol for the current version of NUT should be +backwards-compatible all the way back to version 1.4. A newer client should +fail gracefully when querying an older server. + +If you need more details about cross-compatibility of older NUT releases +(1.x vs. 2.x), please see the {xref}Project_History{x-s}[Project history] chapter. + +Support / Help / etc. +--------------------- + +If you are in need of help, refer to the +{xref}Support_Request{x-s}[Support instructions] in the user manual. + + +Hacking / Development Info +-------------------------- + +Additional documentation can be found in: + +- the {linkdoc}developer-guide{ld-s}[Developer Guide], +- the {linkdoc}packager-guide{ld-s}[Packager Guide]. + + +Acknowledgements / Contributions +-------------------------------- + +The many people who have participated in creating and improving NUT are +listed in the user manual {xref}Acknowledgements{x-s}[acknowledgements appendix]. + +[[acknowledgements-ci-ops]] + +We would like to highlight some organizations which provide continuous +support to the NUT project (and many other FOSS projects) on technological +and organizational sides, such as helping keep the donations transparent, +NUT CI farm afloat, and public resources visible. Thanks for keeping the +clocks ticking, day and night: + +//////////// +FIXME: Use different (better-resolution) images for PDF rendering? + +FIXME: PDF cells seem to align weirdly, like setting the bottom of the first +line of text to be on the same level as bottom of the image, or similar to that. + +NOTE: GitHub renderer (or CSS stack?) ignores style settings and squashes the +logo column into a fixed-width monster with either our specified heights, or +with teeny-tiny thumbnail magnitude images, so it is prettier to leave it as +a "single-column table" by default. Grid/Frame settings are also ignored, but +we can try our best anyway. + +NOTE: The classic asciidoc/a2x renderer seems to not support link/url options, +but at least does not complain about them either. +//////////// + +ifndef::env-github[] +[frame="none",grid="none",cols="^.<1,<.<2"] +endif::env-github[] +ifdef::env-github[] +[frame="none",grid="none",cols="<1*"] +endif::env-github[] +|=== +| image:images/ci/GitHub-Mark-140pxW.png[alt="GitHub logo",width="140",height="140",link="https://github.com/"] +| The link:https://github.com/networkupstools/["NetworkUPSTools" organization + on GitHub] arranges a lot of things, including source code hosting for NUT + itself and several related projects, team management, projects, issue and + pull request discussions, sponsorship, nut-website rendering and hosting, + some automated actions, and more... + +| image:images/ci/jenkins-nut-transparent-bg-140pxW.png[alt="Jenkins and NUT logo",width="139",height="104",link="https://www.jenkins.io/"] +| The link:https://www.jenkins.io/[Jenkins CI] project and its huge plugin + ecosystem provides the technological foundation for the largest island of + the link:https://ci.networkupstools.org/[self-hosted NUT CI farm]. + There is a fair amount of cross-pollination between the upstream project + and community, and the development done originally for the NUT CI farm. + + See more at link:https://stories.jenkins.io/user-story/jenkins-is-the-way-for-networkupstools/[Jenkins + is the way to build multi-platform NUT] article. + +| image:images/ci/fosshost_org_Host_Light_38px.png[alt="Fosshost logo",width="112",height="38"] +| Fosshost provided virtual machines where the multi-platform NUT CI farm with + a link:https://github.com/networkupstools/jenkins-dynamatrix/[jenkins-dynamatrix] + link:https://github.com/networkupstools/nut/blob/master/Jenkinsfile-dynamatrix[setup] + runs to arrange builds in numerous operating environments and a lot of toolkit + versions and implementations. Some workers running on NUT community members' + machines can also dial in to provide an example of their favourite platforms. + Literally hundreds of NUT builds run for each iteration, to make sure NUT can + always build and work everywhere. + + This allows us to ensure that NUT remains portable across two decades' worth + of operating systems, compilers, script interpreters, tools and third-party + dependencies. + +| image:images/ci/CircleCI_vertical_black_logo.png[alt="CircleCI logo",width="130",height="107",link="https://circleci.com/"] +| The + link:https://app.circleci.com/pipelines/github/networkupstools/nut/[CircleCI + NUT pipeline] allows us to test NUT CI builds on MacOS. + +| image:images/ci/AppVeyor_logo-ar21.png[alt="AppVeyor logo",width="120",height="60",link="https://www.appveyor.com/"] +| The link:https://ci.appveyor.com/project/nut-travis/nut/[AppVeyor + NUT pipeline] allows us to test NUT CI builds on Windows (and publish + preview tarballs with binaries). + +| image:images/ci/DO_Powered_by_Badge_blue_140pxW.png[alt="DigitalOcean logo",width="140",height="29",link="https://www.digitalocean.com/?refcode=d2fbf2b9e082&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"] +| The link:https://www.digitalocean.com/?refcode=d2fbf2b9e082&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge[DigitalOcean] + droplets allow us to host NUT CI farm Jenkins controller and the build agents + for multiple operating systems. + +| image:images/ci/gandi-ar21.png[alt="Gandi.Net logo",width="120",height="60",link="https://www.gandi.net/"] +| link:https://www.gandi.net/[Gandi.Net] took up the costs of NUT DNS hosting. + +| image:images/ci/OC_logo_merged_140x26.png[alt="Open Collective logo",width="140",height="26",link="https://opencollective.com/"] +| https://opencollective.com/networkupstools allows us to arrange monetary + donations and spending, with public transparency of everything that happens. +|=== diff --git a/TODO b/TODO.adoc similarity index 98% rename from TODO rename to TODO.adoc index 20cbbded5b..e81d278122 100644 --- a/TODO +++ b/TODO.adoc @@ -13,12 +13,12 @@ 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 ^^^ -This branch will focus on configuration and user interface improvements. +This branch will focus on configuration and user interface improvements. 3.0 ^^^ diff --git a/UPGRADING b/UPGRADING deleted file mode 100644 index ba120d90ef..0000000000 --- a/UPGRADING +++ /dev/null @@ -1,403 +0,0 @@ -ifdef::txt[] -Upgrading notes -=============== -endif::txt[] - -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 ---------------------------- - -- Note to distribution packagers: this version hopefully learns from many - past mistakes, so many custom patches may be no longer needed. If some - 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). - -- Due to changes needed to resolve build warnings, mostly about mismatching - data types for some variables, some structure definitions and API signatures - of several routines had to be changed for argument types, return types, - or both. Primarily this change concerns internal implementation details - (may impact update of NUT forks with custom drivers using those), but a - 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. - -- 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 - libusb-0.1 if not. Please report any regressions. - -- apcupsd-ups: When monitoring a remote apcupsd server, interpret "SHUTTING - DOWN" as a NUT "LB" status. If you were relying on the previous behavior - (for instance, in a monitor-only situation), please adjust your upsmon - settings. Reference: https://github.com/networkupstools/nut/issues/460 - -- Packagers: the AsciiDoc detection has been reworked to allow NUT to be built - from source without requiring asciidoc/a2x (using pre-built man pages from - the distribution tarball, for instance). Please double-check that we did not - break anything (see docs/configure.txt for options). - -- Driver core: options added for standalone mode (scanning for devices without - requiring ups.conf) - see docs/man/nutupsdrv.txt for details. - -- 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". - -- liebert-esp2: some multi-phase variable names have been updated to match the - rest of NUT. - -- netxml-ups: if you have old firmware, or were relying on values being off by - a factor of 10, consider the `do_convert_deci` flag. See - docs/man/netxml-ups.txt for details. - -- snmp-ups: detection of Net-SNMP has been updated to use `pkg-config` by - default (if present), rather than `net-snmp-config(-32|-64)` script(s) as - the only option available previously. The scripts tend to specify a lot - of options (sometimes platform-specific) in suggested `CFLAGS` and `LIBS` - compared to the packaged `pkg-config` information which also works and is - more portable. If this change bites your distribution, please bring it up - in https://github.com/networkupstools/nut/issues or better yet, post a PR. - Also note that `./configure --with-netsnmp-config(=yes)` should set up the - preference of the detected script over `pkg-config` information, if both - are available, and `--with-netsnmp-config=/path/name` would as well. - -- snmp-ups: bit mask values for flags in earlier codebase were defined in a - way that caused logically different items to have same numeric values. - This was fixed to surely use different definitions (so changing numbers - behind some of those macro symbols), and testing with UPS, ePDU and ATS - hardware which was available did not expose any practical differences. - -- usbhid-ups: numeric data conversion from wire protocol to CPU representation - in GetValue() was completely reworked, aiming to be correct on all CPU types. - That said, regressions are possible and feedback is welcome. - -- nut-scanner: Packagers, take note of the changes to the library - search code in common/common.c. Please file an issue if this does not work - with your platform. - -- Python: scripts have been updated to work with Python 3 as well as 2. - -- apcsmart: updates to CS "hack" (see docs/man/apcsmart.txt for details) - -- upsdebugx(): added `[D#]` prefix to log entries with level > 0 - so if any scripts or other tools relied on parsing those messages - making some assumptions, they should be updated - -- upsdebugx() and related methods are now macros, optionally calling similarly - named implementations like s_upsdebugx() as a slight optimization; this may - show up in linking of binaries for some customized build scenarios - -- libraries, tools and protocol now support a `TRACKING` ID to be used with - an `INSTCMD` or `SET VAR` requests; for details see docs/net-protocol.txt - and docs/sock-protocol.txt - -- upsrw: display the variable type beside ENUM / RANGE - -- Augeas: new `--with-augeas-lenses-dir` configure option. - -Changes from 2.7.3 to 2.7.4 ---------------------------- - -- scripts/systemd/nut-server.service.in: Restore systemd relationship since it - was preventing upsd from starting whenever one or more drivers, among several, - was failing to start - -- Fix UPower device matching for recent kernels, since hiddev* devices now have - class "usbmisc", rather than "usb" - -- macosx-ups: the "port" driver option no longer has any effect - -- Network protocol information: default to type NUMBER for variables that are - not flagged as STRING . This point is subject to improvements or change in - the next release 2.7.5. Refer to docs/net-protocol.txt for more information - -Changes from 2.7.2 to 2.7.3 ---------------------------- - -- The linkman:nutdrv_qx[8] driver will eventually supersede linkman:bestups[8]. - It has been tested on a U-series Patriot Pro II. Please test the new driver - on your hardware during your next maintenance window, and report any bugs. - -- If you are upgrading from a new install of 2.7.1 or 2.7.2, double-check the - value of POWERDOWNFLAG in $prefix/etc/upsmon.conf - it has been restored to - /etc/killpower as in 2.6.5 and earlier. - -- If you use upslog with a large sleep value, you may be interested in adding - `killall -SIGUSR1 upslog` to any OB/OL script actions. This will force - upslog to write a log entry to catch short power transients. - -- Be sure that your SSL keys are readable by the NUT system user. The SSL - subsystem is now initialized after `upsd` forks, to work around issues in the - NSS library. - -- The systemd nut-server.service does not Require nut-driver to be started - successfully. This was previously preventing upsd startup, even for just - one driver failure among many. This also matches the behavior of sysV - initscripts. - -Changes from 2.7.1 to 2.7.2 ---------------------------- - -- upsdrvctl is now installed to $prefix/sbin rather than $driverexec. - This usually means moving from /bin to /sbin, apart from few exceptions. - In all cases, please adapt your scripts. - -- FreeDesktop Hardware Abstraction Layer (HAL) support was removed. - Please adapt your packaging files, if you used to distribute the - nut-hal-drivers package. - -- This is a good time to point out that for stricter packaging systems, it may - be beneficial to add "--enable-option-checking=fatal" to the ./configure - command line, in order to quickly pick up any other removed option flags. - -Changes from 2.6.5 to 2.7.1 ---------------------------- - -- The linkman:apcsmart[8] driver has been replaced by a new implementation. There is a new - parameter, 'ttymode', which may help if you have a non-standard serial port, - or Windows. In case of issues with this new version, users can revert to - apcsmart-old. - -- The linkman:nutdrv_qx[8] driver will eventually supersede blazer_ser and blazer_usb. - Options are not exactly the same, but are documented in the nutdrv_qx man - page. - -- Mozilla NSS support has been added. The OpenSSL configuration options should - be unchanged, but please refer to the linkman:upsd.conf[5] and - linkman:upsmon.conf[5] documentation in case we missed something. - -- linkman:upsrw[8] now prints out the maximum size of variables. Hopefully you - are not parsing the output of upsrw - it would be easier to use one of the - NUT libraries, or implement the network protocol yourself. - -- The jNut source is now here: https://github.com/networkupstools/jNut - -Changes from 2.6.4 to 2.6.5 ---------------------------- - -- users are encouraged to update to NUT 2.6.5, to fix a regression in -upssched. -- mge-shut driver has been replaced by a new implementation (newmge-shut). -In case of issue with this new version, users can revert to oldmge-shut. -UPDATE: oldmge-shut was dropped between 2.7.4 and 2.7.5 releases. - -Changes from 2.6.3 to 2.6.4 ---------------------------- - -- users are encouraged to update to NUT 2.6.4, to fix upsd vulnerability -(CVE-2012-2944: upsd can be remotely crashed). -- users of the bestups driver are encouraged to switch to blazer_ser, -since bestups will soon be deprecated. - -Changes from 2.6.2 to 2.6.3 ---------------------------- - -- nothing that affects upgraded systems. - -Changes from 2.6.1 to 2.6.2 ---------------------------- - -- apcsmart driver has been replaced by a new implementation. In case of issue -with this new version, users can revert to apcsmart-old. - -Changes from 2.6.0 to 2.6.1 ---------------------------- - -- nothing that affects upgraded systems. - -Changes from 2.4.3 to 2.6.0 ---------------------------- - -- users of the megatec and megatec_usb drivers must respectively switch to -blazer_ser and blazer_usb. -- users of the liebertgxt2 driver are advised that the driver name has changed -to liebert-esp2. - -Changes from 2.4.2 to 2.4.3 ---------------------------- - -- nothing that affects upgraded systems. - -Changes from 2.4.1 to 2.4.2 ---------------------------- - -- The default subdriver for the blazer_usb driver USB id 06da:0003 has changed. -If you use such a device and it is no longer working with this driver, override -the 'subdriver' default in 'ups.conf' (see man 8 blazer). -- NUT ACL and the allowfrom mechanism has been replaced in 2.4.0 by the LISTEN -directive and tcp-wrappers respectively. This information was missing below, so -a double note has been added. - -Changes from 2.4.0 to 2.4.1 ---------------------------- - -- nothing that affects upgraded systems. - -Changes from 2.2.2 to 2.4.0 ---------------------------- - -- The nut.conf file has been introduced to standardize startup configuration -across the various systems. -- The cpsups and nitram drivers have been replaced by the powerpanel driver, -and removed from the tree. The cyberpower driver may suffer the same in the -future. -- The al175 and energizerups drivers have been removed from the tree, since -these were tagged broken for a long time. -- Developers of external client application using libupsclient must rename -their "UPSCONN" client structure to "UPSCONN_t". -- The upsd server will now disconnect clients that remain silent for more than -60 seconds. -- The files under scripts/python/client are distributed under GPL 3+, whereas -the rest of the files are distributed under GPL 2+. Refer to COPYING for more -information. -- The generated udev rules file has been renamed with dash only, no underscore -anymore (ie 52-nut-usbups.rules instead of 52_nut-usbups.rules) - -Changes from 2.2.1 to 2.2.2 ---------------------------- - -- The configure option "--with-lib" has been replaced by "--with-dev". -This enable the additional build and distribution of the static -version of libupsclient, along with the pkg-config helper and manual -pages. The default configure option is to distribute only the shared -version of libupsclient. This can be overridden by using the -"--disable-shared" configure option (distribute static only binaries). -- The UPS poweroff handling of the usbhid-ups driver has been reworked. -Though regression is not expected, users of this driver are -encouraged to test this feature by calling "upsmon -c fsd" and -report any issue on the NUT mailing lists. - -Changes from 2.2.0 to 2.2.1 ---------------------------- - -- nothing that affects upgraded systems. -(The below message is repeated due to previous omission) -- Developers of external client application using libupsclient are -encouraged to rename their "UPSCONN" client structure to "UPSCONN_t" -since the former will disappear by the release of NUT 2.4. - -Changes from 2.0.5 to 2.2.0 ---------------------------- - -- users of the newhidups driver are advised that the driver name has changed -to usbhid-ups. -- users of the hidups driver must switch to usbhid-ups. -- users of the following drivers (powermust, blazer, fentonups, mustek, -esupssmart, ippon, sms) must switch to megatec, which replaces -all these drivers. Please refer to doc/megatec.txt for details. -- users of the mge-shut driver are encouraged to test newmge-shut, which -is an alternate driver scheduled to replace mge-shut, -- users of the cpsups driver are encouraged to switch to powerpanel which -is scheduled to replace cpsups, -- packagers will have to rework the whole nut packaging due to the -major changes in the build system (completely modified, and now using -automake). Refer to packaging/debian/ for an example of migration. -- specifying '-a ' is now mandatory when starting a driver manually, -ie not using upsdrvctl. -- Developers of external client application using libupsclient are -encouraged to rename the "UPSCONN" client structure to "UPSCONN_t" -since the former will disappear by the release of NUT 2.4. - -Changes from 2.0.4 to 2.0.5 ---------------------------- - -- users of the newhidups driver: the driver is now more strict about -refusing to connect to unknown devices. If your device was -previously supported, but fails to be recognized now, add -'productid=XXXX' to ups.conf. Please report the device to the NUT -developer's mailing list. - -Changes from 2.0.3 to 2.0.4 ---------------------------- - -- nothing that affects upgraded systems. -- users of the following drivers (powermust, blazer, fentonups, mustek, -esupssmart, ippon, sms, masterguard) are encouraged to switch to megatec, -which should replace all these drivers by nut 2.2. For more information, -please refer to doc/megatec.txt - -Changes from 2.0.2 to 2.0.3 ---------------------------- - -- nothing that affects upgraded systems. -- hidups users are encouraged to switch to newhidups, as hidups will be -removed by nut 2.2. - -Changes from 2.0.1 to 2.0.2 ---------------------------- - -- The newhidups driver, which is the long run USB support approach, -needs hotplug files installed to setup the right permissions on -device file to operate. Check newhidups manual page for more information. - -Changes from 2.0.0 to 2.0.1 ---------------------------- - -- The cyberpower1100 driver is now called cpsups since it supports -more than just one model. If you use this driver, be sure to remove -the old binary and update your ups.conf 'driver=' setting with the -new name. - -- The upsstats.html template page has been changed slightly to reflect -better HTML compliance, so you may want to update your installed copy -accordingly. If you've customized your file, don't just copy the new -one over it, or your changes will be lost! - -Changes from 1.4.0 to 2.0.0 ---------------------------- - -- The sample config files are no longer installed by default. If you -want to install them, use 'make install-conf' for the main programs, -and 'make install-cgi-conf' for the CGI programs. - -- ACCESS is no longer supported in upsd.conf. Use ACCEPT and REJECT. -Old way: - - ACCESS grant all adminbox - ACCESS grant all webserver - ACCESS deny all all - -New way: - - ACCEPT adminbox - ACCEPT webserver - REJECT all - -Note that ACCEPT and REJECT can take multiple arguments, so this -will also work: - - ACCEPT adminbox webserver - REJECT all - -- The drivers no longer support sddelay in ups.conf or -d on the -command line. If you need a delay after calling 'upsdrvctl -shutdown', add a call to sleep in your shutdown script. - -- The templates used by upsstats have changed considerably to reflect -the new variable names. If you use upsstats, you will need to -install new copies or edit your existing files to use the new names. - -- Nobody needed UDP mode, so it has been removed. The only users -seemed to be a few people like me with ancient asapm-ups binaries. -If you really want to run asapm-ups again, bug me for the new patch -which makes it work with upsclient. - -- 'make install-misc' is now 'make install-lib'. The misc directory -has been gone for a long time, and the target was ambiguous. - -- The newapc driver has been renamed to apcsmart. If you previously -used newapc, make sure you delete the old binary and fix your -ups.conf. Otherwise, you may run the old driver from 1.4. - - -*** File trimmed here on changes from 1.2.2 to 1.4.0 *** - -For information before this point, start with version 2.4.1 and work back. diff --git a/UPGRADING.adoc b/UPGRADING.adoc new file mode 100644 index 0000000000..cb89c907dd --- /dev/null +++ b/UPGRADING.adoc @@ -0,0 +1,655 @@ +ifdef::txt[] +Upgrading notes +=============== +endif::txt[] + +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. + +[NOTE] +====== +For packaging (OS distribution or in-house) it is recommended to +primarily `./configure --with-all` and then excise `--without-something` +explicitly for items not supported on your platform, so you do not miss +out on new NUT features as they come with new releases. Some may require +that you update your build environment with new third-party dependencies, +so a broken build of a new NUT release would let you know how to act. + +This is a good time to point out that for stricter packaging systems, it may +be beneficial to add `--enable-option-checking=fatal` to the `./configure` +command line, in order to quickly pick up any other removed option flags. +====== + +Changes from 2.8.2 to 2.8.3 +--------------------------- + +- PLANNED: Keep track of any further API clean-up? + + +Changes from 2.8.1 to 2.8.2 +--------------------------- + +- Builds requested with a specific C/C++ language standard revision via + `CFLAGS` and `CXXFLAGS` should again be honoured. There was a mishap + with the `m4` scripting for `autoconf` which could have caused use of + C11/C++11 if compiler supported it, regardless of a request. [PR #2306] + +- Added generation of FreeBSD/pfSense quirks for USB devices supported + by NUT (may get installed to `$datadir` e.g. `/usr/local/share/nut` + and need to be pasted into your `/boot/loader.conf.local`). [#2159] + +- nut-scanner now does not propose active `bus`, `busport` and `device` + values when generating device configurations by default. They may + appear as comments, or enabled by specifying the `-U` command-line + option several times. [#2221] + +Changes from 2.8.0 to 2.8.1 +--------------------------- + +- NUT documentation recipes were revised, so many of the text source files + were renamed to `*.adoc` pattern. Newly, a `release-notes.pdf` and HTML + equivalents are generated. Packages which deliver documentation may need + to update the lists of files to ship. [#1953] Developers may be impacted + by new `configure --enable-spellcheck` toggle (should add spelling checks + to `make check` by default, if tools are available) to facilitate quicker + acceptance of contributions. Packaging systems may now want to explicitly + disable it, if it blocks package building (pull requests to update the + `docs/nut.dict` are a better and welcome solution). [#2067] + +- Several improvements regarding simultaneous support of USB devices that + were previously deemed "identical" and so NUT driver instances did not + start for all of them: + + * Some more drivers should now use the common USB device matching logic + and the 7 `ups.conf` options for that [#1763], and man pages were + updated to reflect that [#1766]; + + * The `nut-scanner` tool should suggest these options in its generated + device configuration [#1790]: hopefully these would now suffice for + sufficiently unique combinations; + + * The `nut-scanner` tool should also suggest sanity-check violations + as comments in its generated device configuration [#1810], e.g. bogus + or duplicate serial number values; + + * The common USB matching logic was updated with an `allow_duplicates` + flag (caveat emptor!) which may help monitor several related no-name + devices on systems that do not discern "bus" and "device" values + (although without knowing reliably which one is which... sometimes it + is better than nothing) [#1756]. + +- Work on NUT for Windows branch led to situation-specific definitions of + what in POSIX code was all "file descriptors" (an `int` type). Now such + entities are named `TYPE_FD`, `TYPE_FD_SER` or `TYPE_FD_SOCK` with some + helper macros to name and determine "invalid" values (closed file, etc.) + Some of these changes happened in NUT header files, and at this time it + was not investigated whether the set of files delivered for third-party + code integration (e.g. C/C++ projects binding with `libnutclient` or + `libupsclient) is consistent or requires additional definitions/files. + If something gets broken by this, it is a bug to address in future [#1556] + +- Further revision of public headers delivered by NUT was done, particularly + to address lack of common data types (`size_t`, `ssize_t`, `uint16_t`, + `time_t` etc.) in third-party client code that earlier sufficed to only + include NUT headers. Sort of regression by NUT 2.8.0 (note those consumers + still have to re-declare some numeric variable types used) [#1638] + + * For practical example of NUT consumer adaptation (to cater to both old and + new API types) please see https://github.com/collectd/collectd/pull/4043 + +- Added support for `make install` of PyNUT module and NUT-Monitor desktop + application -- such activity was earlier done by packages directly; now + the packaging recipes may use NUT source-code facilities and package just + symlinks as relevant for each distro separately [#1462, #1504] + +- The `upsd.conf` listing of `LISTEN` addresses was previously inverted + (the last listed address was applied first), which was counter-intuitive + and fixed for this release. If user configurations somehow relied on this + order (e.g. to prioritize IPv6 vs IPv4 listeners), configuration changes + may be needed. [#2012] + +- The `upsd` configured to listen on IPv6 addresses should handle only + IPv6 (and not IPv4-mappings like it might have done before) to avoid + surprises and insecurity -- if user configurations somehow relied on + this dual support, configuration changes may be needed to specify both + desired IP addresses. Note that the daemon logs will now warn if a + host name resolves to several addresses (and will only listen on the + first hit, as it did before in such cases). [#2012] + +- A definitive behavior for `LISTEN *` directives became specified, to try + handling both IPv4 and IPv6 "any" address (subject to `upsd` CLI options + to only choose one, and to OS abilities). This use-case may be practically + implemented as a single IPv6 socket on systems with enabled and required + IPv4-mapped IPv6 address support, or as two separate listening sockets - + logged messages to this effect (e.g. inability to listen on IPv4 after + opening IPv6) are expected on some platforms. End-users may also want to + reconfigure their `upsd.conf` files to remove some now-redundant `LISTEN` + lines. [#2012] + +- Added support for `make sockdebug` for easier developer access to the tool; + also if `configure --with-dev` is in effect, it would now be installed to + the configured `libexec` location. A man page was also added. [#1936] + +- NUT software-only drivers (dummy-ups, clone, clone-outlet) separated from + serial drivers in respective Makefile and configure script options - this + may impact packaging decisions on some distributions going forward [#1446] + +- GPIO category of drivers was added (`--with-gpio` configure script option) - + this may impact packaging decisions on some (currently Linux released 2018+) + distributions going forward [#1855] + +- An explicit `configure --with-nut-scanner` toggle was added, specifically + so that build environments requesting `--with-all` but lacking `libltdl` + would abort and require the packager either to install the dependency + or explicitly forfeit building the tool (some distro packages missed it + quietly in the past) [#1560] + +- An `upsdebugx_report_search_paths()` method in NUT common code was added, + and exposed in `libnutscan.so` builds in particular - API version for the + public library was bumped [#317] + +- Some environment variable support was added to NUT programs, primarily + aimed at wrappers such as init scripts and service unit definitions, + allowing to tweak what (and whether) they write into debug traces, and + so "make noise" or "bring invaluable insights" to logs or terminal: + * A `NUT_DEBUG_LEVEL=NUM` envvar allows to temporarily boost debugging + of many daemons (`upsd`, `upsmon`, drivers, `upsdrvctl`, `upssched`) + without changes to configuration files or scripted command lines. [#1915] + * A `NUT_DEBUG_PID` envvar (presence) support was added to add current + process ID to tags with debug-level identifiers. This may be useful + when many NUT daemons write to the same console or log file, such as + in containers/plugins for Home Assistant, storage appliances, etc. [#2118] + * A `NUT_QUIET_INIT_SSL` envvar (presence or "true" value) prevents + `libupsclient` consumers (notoriously `upsc`) from reporting whether + they have initialized SSL support. [#1662] + * A `NUT_QUIET_INIT_UPSNOTIFY` envvar (presence or "true" value) + prevents daemons which can notify service management frameworks (such + as systemd) about passing their lifecycle milestones, to not report + loudly if they could not do so (e.g. running on a system without a + framework, or misconfigured so they could not report and the OS would + restart the false-positively "unresponsive" service). [#2136] + +- `configure` script, reference init-script and packaging templates updated + to eradicate `@PIDPATH@/nut` ambiguity in favor of `@ALTPIDPATH@` for the + unprivileged processes vs. `@PIDPATH@` for those running as root [#1719] + +- The "layman report" of NUT configuration options displayed after the run + of `configure` script can now be retained and installed by using the + `--enable-keep_nut_report_feature` option; packagers are welcome to make + use of this, to better keep track of their deliveries [#1826, #1708] + +- Renamed generated nut-common.tmpfiles(.in) => nut-common-tmpfiles.conf(.in) + to install a /usr/lib/systemd-tmpfiles/*.conf pattern [#1755] + + * If earlier NUT v2.8.0 package recipes for your Linux distribution dealt + with this file, you may have to adjust its name for newer releases. + + * Several other issues have been fixed related to this file and its content, + including #1030, #1037, #1117 and #1712 + +- Extended Linux systemd support with optional notifications about daemon + state (READY, RELOADING, STOPPING) and watchdog keep-alive messages. + Note that `WatchdogSec=` values are currently NOT pre-set into systemd + unit file templates provided by NUT, this is an exercise for end-users + based on sizing of their deployments and performance of monitoring station + [#1590, #1777] + +- snmp-ups: some subdrivers (addressed using the driver parameter `mibs`) + were renamed: `pw` is now `eaton_pw_nm2`, and `pxgx_ups` is `eaton_pxg_ups` + [#1715] + +- The `tools/gitlog2changelog.py.in` script was revised, in particular to + generate the `ChangeLog` file more consistently with different versions + of Python interpreter, and without breaking the long file paths in the + resulting mark-up text. Due to this, a copy of this file distributed with + NUT release archives is expected to considerably differ on first glance + from its earlier released versions (not just adding lines for the new + release, but changing lines in the older releases too) [#1945, #1955] + +Changes from 2.7.4 to 2.8.0 +--------------------------- + +- Note to distribution packagers: this version hopefully learns from many + past mistakes, so many custom patches may be no longer needed. If some + 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), + 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 + of several routines had to be changed for argument types, return types, + or both. Primarily this change concerns internal implementation details + (may impact update of NUT forks with custom drivers using those), but a + 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.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 + libusb-0.1 if not. Please report any regressions. + +- apcupsd-ups: When monitoring a remote apcupsd server, interpret "SHUTTING + DOWN" as a NUT "LB" status. If you were relying on the previous behavior + (for instance, in a monitor-only situation), please adjust your upsmon + settings. Reference: https://github.com/networkupstools/nut/issues/460 + +- Packagers: the AsciiDoc detection has been reworked to allow NUT to be built + from source without requiring asciidoc/a2x (using pre-built man pages from + the distribution tarball, for instance). Please double-check that we did not + break anything (see docs/configure.txt for options). + +- Driver core: options added for standalone mode (scanning for devices without + requiring ups.conf) - see docs/man/nutupsdrv.txt for details. + +- oldmge-shut has been removed, and replaced by mge-shut. + +- 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. + +- netxml-ups: if you have old firmware, or were relying on values being off by + a factor of 10, consider the `do_convert_deci` flag. See + docs/man/netxml-ups.txt for details. + +- snmp-ups: detection of Net-SNMP has been updated to use `pkg-config` by + default (if present), rather than `net-snmp-config(-32|-64)` script(s) as + the only option available previously. The scripts tend to specify a lot + of options (sometimes platform-specific) in suggested `CFLAGS` and `LIBS` + compared to the packaged `pkg-config` information which also works and is + more portable. If this change bites your distribution, please bring it up + in https://github.com/networkupstools/nut/issues or better yet, post a PR. + Also note that `./configure --with-netsnmp-config(=yes)` should set up the + preference of the detected script over `pkg-config` information, if both + are available, and `--with-netsnmp-config=/path/name` would as well. + +- snmp-ups: bit mask values for flags in earlier codebase were defined in a + way that caused logically different items to have same numeric values. + This was fixed to surely use different definitions (so changing numbers + behind some of those macro symbols), and testing with UPS, ePDU and ATS + hardware which was available did not expose any practical differences. + +- usbhid-ups: numeric data conversion from wire protocol to CPU representation + in GetValue() was completely reworked, aiming to be correct on all CPU types. + That said, regressions are possible and feedback is welcome. + +- nut-scanner: Packagers, take note of the changes to the library + 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 + so if any scripts or other tools relied on parsing those messages + making some assumptions, they should be updated + +- upsdebugx() and related methods are now macros, optionally calling similarly + named implementations like s_upsdebugx() as a slight optimization; this may + show up in linking of binaries for some customized build scenarios + +- libraries, tools and protocol now support a `TRACKING` ID to be used with + an `INSTCMD` or `SET VAR` requests; for details see docs/net-protocol.txt + and docs/sock-protocol.txt + +- upsrw: display the variable type beside ENUM / RANGE + +- Augeas: new `--with-augeas-lenses-dir` configure option. + +Changes from 2.7.3 to 2.7.4 +--------------------------- + +- scripts/systemd/nut-server.service.in: Restore systemd relationship since it + was preventing upsd from starting whenever one or more drivers, among several, + was failing to start + +- Fix UPower device matching for recent kernels, since hiddev* devices now have + class "usbmisc", rather than "usb" + +- macosx-ups: the "port" driver option no longer has any effect + +- Network protocol information: default to type NUMBER for variables that are + not flagged as STRING . This point is subject to improvements or change in + the next release 2.7.5. Refer to docs/net-protocol.txt for more information + +Changes from 2.7.2 to 2.7.3 +--------------------------- + +- The linkman:nutdrv_qx[8] driver will eventually supersede linkman:bestups[8]. + It has been tested on a U-series Patriot Pro II. Please test the new driver + on your hardware during your next maintenance window, and report any bugs. + +- If you are upgrading from a new install of 2.7.1 or 2.7.2, double-check the + value of POWERDOWNFLAG in $prefix/etc/upsmon.conf - it has been restored to + /etc/killpower as in 2.6.5 and earlier. + +- If you use upslog with a large sleep value, you may be interested in adding + `killall -SIGUSR1 upslog` to any OB/OL script actions. This will force + upslog to write a log entry to catch short power transients. + +- Be sure that your SSL keys are readable by the NUT system user. The SSL + subsystem is now initialized after `upsd` forks, to work around issues in the + NSS library. + +- The systemd nut-server.service does not Require nut-driver to be started + successfully. This was previously preventing upsd startup, even for just + one driver failure among many. This also matches the behavior of sysV + initscripts. + +Changes from 2.7.1 to 2.7.2 +--------------------------- + +- upsdrvctl is now installed to $prefix/sbin rather than $driverexec. + This usually means moving from /bin to /sbin, apart from few exceptions. + In all cases, please adapt your scripts. + +- FreeDesktop Hardware Abstraction Layer (HAL) support was removed. + Please adapt your packaging files, if you used to distribute the + nut-hal-drivers package. + +- This is a good time to point out that for stricter packaging systems, it may + be beneficial to add "--enable-option-checking=fatal" to the ./configure + command line, in order to quickly pick up any other removed option flags. + +Changes from 2.6.5 to 2.7.1 +--------------------------- + +- The linkman:apcsmart[8] driver has been replaced by a new implementation. There is a new + parameter, 'ttymode', which may help if you have a non-standard serial port, + or Windows. In case of issues with this new version, users can revert to + apcsmart-old. + +- The linkman:nutdrv_qx[8] driver will eventually supersede blazer_ser and blazer_usb. + Options are not exactly the same, but are documented in the nutdrv_qx man + page. + +- Mozilla NSS support has been added. The OpenSSL configuration options should + be unchanged, but please refer to the linkman:upsd.conf[5] and + linkman:upsmon.conf[5] documentation in case we missed something. + +- linkman:upsrw[8] now prints out the maximum size of variables. Hopefully you + are not parsing the output of upsrw - it would be easier to use one of the + NUT libraries, or implement the network protocol yourself. + +- The jNut source is now here: https://github.com/networkupstools/jNut + +Changes from 2.6.4 to 2.6.5 +--------------------------- + +- users are encouraged to update to NUT 2.6.5, to fix a regression in + upssched. + +- mge-shut driver has been replaced by a new implementation (newmge-shut). + In case of issue with this new version, users can revert to oldmge-shut. + UPDATE: oldmge-shut was dropped between 2.7.4 and 2.8.0 releases. + +Changes from 2.6.3 to 2.6.4 +--------------------------- + +- users are encouraged to update to NUT 2.6.4, to fix upsd vulnerability + (CVE-2012-2944: upsd can be remotely crashed). + +- users of the bestups driver are encouraged to switch to blazer_ser, + since bestups will soon be deprecated. + +Changes from 2.6.2 to 2.6.3 +--------------------------- + +- nothing that affects upgraded systems. + +Changes from 2.6.1 to 2.6.2 +--------------------------- + +- apcsmart driver has been replaced by a new implementation. In case of issue + with this new version, users can revert to apcsmart-old. + +Changes from 2.6.0 to 2.6.1 +--------------------------- + +- nothing that affects upgraded systems. + +Changes from 2.4.3 to 2.6.0 +--------------------------- + +- users of the megatec and megatec_usb drivers must respectively switch to + blazer_ser and blazer_usb. + +- users of the liebertgxt2 driver are advised that the driver name has changed + to liebert-esp2. + +Changes from 2.4.2 to 2.4.3 +--------------------------- + +- nothing that affects upgraded systems. + +Changes from 2.4.1 to 2.4.2 +--------------------------- + +- The default subdriver for the blazer_usb driver USB id 06da:0003 has changed. + If you use such a device and it is no longer working with this driver, + override the 'subdriver' default in 'ups.conf' (see man 8 blazer). + +- NUT ACL and the allowfrom mechanism has been replaced in 2.4.0 by the LISTEN + directive and tcp-wrappers respectively. This information was missing below, + so a double note has been added. + +Changes from 2.4.0 to 2.4.1 +--------------------------- + +- nothing that affects upgraded systems. + +Changes from 2.2.2 to 2.4.0 +--------------------------- + +- The nut.conf file has been introduced to standardize startup configuration + across the various systems. + +- The cpsups and nitram drivers have been replaced by the powerpanel driver, + and removed from the tree. The cyberpower driver may suffer the same in the + future. + +- The al175 and energizerups drivers have been removed from the tree, since + these were tagged broken for a long time. + +- Developers of external client application using libupsclient must rename + their "UPSCONN" client structure to "UPSCONN_t". + +- The upsd server will now disconnect clients that remain silent for more than + 60 seconds. + +- The files under scripts/python/client are distributed under GPL 3+, whereas + the rest of the files are distributed under GPL 2+. Refer to COPYING for more + information. + +- The generated udev rules file has been renamed with dash only, no underscore + anymore (i.e. 52-nut-usbups.rules instead of 52_nut-usbups.rules) + +Changes from 2.2.1 to 2.2.2 +--------------------------- + +- The configure option "--with-lib" has been replaced by "--with-dev". + This enable the additional build and distribution of the static + version of libupsclient, along with the pkg-config helper and manual + pages. The default configure option is to distribute only the shared + version of libupsclient. This can be overridden by using the + "--disable-shared" configure option (distribute static only binaries). + +- The UPS poweroff handling of the usbhid-ups driver has been reworked. + Though regression is not expected, users of this driver are + encouraged to test this feature by calling "upsmon -c fsd" and + report any issue on the NUT mailing lists. + +Changes from 2.2.0 to 2.2.1 +--------------------------- + +- nothing that affects upgraded systems. + (The below message is repeated due to previous omission) + +- Developers of external client application using libupsclient are + encouraged to rename their "UPSCONN" client structure to "UPSCONN_t" + since the former will disappear by the release of NUT 2.4. + +Changes from 2.0.5 to 2.2.0 +--------------------------- + +- users of the newhidups driver are advised that the driver name has changed + to usbhid-ups. + +- users of the hidups driver must switch to usbhid-ups. + +- users of the following drivers (powermust, blazer, fentonups, mustek, + esupssmart, ippon, sms) must switch to megatec, which replaces + all these drivers. Please refer to doc/megatec.txt for details. + +- users of the mge-shut driver are encouraged to test newmge-shut, which + is an alternate driver scheduled to replace mge-shut, + +- users of the cpsups driver are encouraged to switch to powerpanel which + is scheduled to replace cpsups, + +- packagers will have to rework the whole nut packaging due to the + major changes in the build system (completely modified, and now using + automake). Refer to packaging/debian/ for an example of migration. + +- specifying '-a ' is now mandatory when starting a driver manually, + i.e. not using upsdrvctl. + +- Developers of external client application using libupsclient are + encouraged to rename the "UPSCONN" client structure to "UPSCONN_t" + since the former will disappear by the release of NUT 2.4. + +Changes from 2.0.4 to 2.0.5 +--------------------------- + +- users of the newhidups driver: the driver is now more strict about + refusing to connect to unknown devices. If your device was + previously supported, but fails to be recognized now, add + 'productid=XXXX' to ups.conf. Please report the device to the NUT + developer's mailing list. + +Changes from 2.0.3 to 2.0.4 +--------------------------- + +- nothing that affects upgraded systems. + +- users of the following drivers (powermust, blazer, fentonups, mustek, + esupssmart, ippon, sms, masterguard) are encouraged to switch to megatec, + which should replace all these drivers by nut 2.2. For more information, + please refer to doc/megatec.txt + +Changes from 2.0.2 to 2.0.3 +--------------------------- + +- nothing that affects upgraded systems. + +- hidups users are encouraged to switch to newhidups, as hidups will be + removed by nut 2.2. + +Changes from 2.0.1 to 2.0.2 +--------------------------- + +- The newhidups driver, which is the long run USB support approach, + needs hotplug files installed to setup the right permissions on + device file to operate. Check newhidups manual page for more information. + +Changes from 2.0.0 to 2.0.1 +--------------------------- + +- The cyberpower1100 driver is now called cpsups since it supports + more than just one model. If you use this driver, be sure to remove + the old binary and update your ups.conf 'driver=' setting with the + new name. + +- The upsstats.html template page has been changed slightly to reflect + better HTML compliance, so you may want to update your installed copy + accordingly. If you've customized your file, don't just copy the new + one over it, or your changes will be lost! + +Changes from 1.4.0 to 2.0.0 +--------------------------- + +- The sample config files are no longer installed by default. If you + want to install them, use 'make install-conf' for the main programs, + and 'make install-cgi-conf' for the CGI programs. + +- ACCESS is no longer supported in upsd.conf. Use ACCEPT and REJECT. + + * Old way: ++ + ACCESS grant all adminbox + ACCESS grant all webserver + ACCESS deny all all + + * New way: ++ + ACCEPT adminbox + ACCEPT webserver + REJECT all + + * Note that ACCEPT and REJECT can take multiple arguments, so this + will also work: ++ + ACCEPT adminbox webserver + REJECT all + +- The drivers no longer support sddelay in ups.conf or -d on the + command line. If you need a delay after calling 'upsdrvctl + shutdown', add a call to sleep in your shutdown script. + +- The templates used by upsstats have changed considerably to reflect + the new variable names. If you use upsstats, you will need to + install new copies or edit your existing files to use the new names. + +- Nobody needed UDP mode, so it has been removed. The only users + seemed to be a few people like me with ancient asapm-ups binaries. + If you really want to run asapm-ups again, bug me for the new patch + which makes it work with upsclient. + +- 'make install-misc' is now 'make install-lib'. The misc directory + has been gone for a long time, and the target was ambiguous. + +- The newapc driver has been renamed to apcsmart. If you previously + used newapc, make sure you delete the old binary and fix your + ups.conf. Otherwise, you may run the old driver from 1.4. + +File trimmed here on changes from 1.2.2 to 1.4.0 +------------------------------------------------ + +For information before this point, start with version 2.4.1 and work back. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..ba5101aa18 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,157 @@ +# +# Network UPS Tools: AppVeyor CI build recipe: NUT for Windows with MSYS2/MinGW x64 +# +# https://www.msys2.org/docs/ci/ +# https://www.appveyor.com/docs/appveyor-yml/ +# https://www.appveyor.com/docs/build-configuration/ +# https://www.appveyor.com/docs/windows-images-software/ + +version: 2.8.1.{build}-{branch} + +# base image +image: Visual Studio 2022 + +# branches to build +branches: + # whitelist + only: + - master + - /Windows/ + +platform: x64 + +# https://www.appveyor.com/docs/build-cache/ +environment: + APPVEYOR_SAVE_CACHE_ON_ERROR: true + APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z -m0=lzma -mx=9 + CCACHE_DIR: /home/appveyor/.ccache + +# https://github.com/networkupstools/nut/blob/Windows-v2.8.0-1/docs/config-prereqs.txt#L951 +# or look for the chapter in nearby lines in later (current) revisions. +# Note: not using `time` in scripts currently - they did upset +# AppVeyor console log scanner with a /^sys.*/ match (apparently) +install: + - cmd: | + REM Do not give pacman cause for complaints: + C:\msys64\usr\bin\bash -lc "mkdir -p /var/cache/pacman/pkg; ls -la /" + - cmd: | + REM Core update (in case any core packages are outdated): + C:\msys64\usr\bin\bash -lc "date -u; pacman --noconfirm -Syuu" + - cmd: | + REM Normal update (same command again): + C:\msys64\usr\bin\bash -lc "date -u; pacman --noconfirm -Syuu" + - cmd: | + REM Prerequisites for NUT per https://github.com/networkupstools/nut/blob/master/docs/config-prereqs.txt : + C:\msys64\usr\bin\bash -lc "date -u; pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain autoconf-wrapper automake-wrapper libtool mingw-w64-x86_64-libltdl gcc ccache mingw-w64-x86_64-ccache git aspell aspell-en python mingw-w64-x86_64-python-pygments mingw-w64-x86_64-winpthreads-git mingw-w64-x86_64-libusb mingw-w64-x86_64-libusb-compat-git mingw-w64-x86_64-neon libneon-devel mingw-w64-x86_64-libmodbus-git mingw-w64-x86_64-libgd mingw-w64-x86_64-cppunit" + - cmd: | + REM Assorted stats after package processing: + C:\msys64\usr\bin\bash -lc "date -u; ls -la / ; du -ksx / ; date -u; du -ks /var/cache/pacman/pkg; date -u" + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -lc 'PATH="/mingw64/bin:$PATH" ; export PATH ; pwd ; ccache -sv || echo "SKIP: Could not query ccache stats" ; ccache -o sloppiness=file_macro || true ; ccache -o compression=true || true ' + + +before_build: + - cmd: | + REM Ensure we have a net-snmp to build against + REM Adapted from scripts/Windows/README.adoc document. + REM Here we hope to build it once, then use the + REM stashed version across Appveyor rebuilds. + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -lc "date -u; export MSYS2_PATH ; bash ./scripts/Windows/build-mingw-prereqs.sh" + + +build_script: + - cmd: | + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" CI_SKIP_CHECK=true ./ci_build.sh' + + +after_build: + - cmd: | + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" ; export PATH ; ccache -sv || ccache -s || echo "SKIP: Could not query ccache stats"' + + +test_script: + - cmd: | + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + REM Start Mingw-based integration and unit checks: + C:\msys64\usr\bin\bash -lc 'date -u; NUT_STATEPATH="C:\\Users\\appveyor\\AppData\\Local\\Temp\\nut-test"; mkdir -p "${NUT_STATEPATH}"; export NUT_STATEPATH; PATH="/mingw64/bin:$PATH" make -s check || bash -lc "for F in tests/*.log tests/*.trs ; do echo \"===---=== $F :\"; cat \"$F\"; done; exit 1;" ' + REM Start a Mingw-based documentation spellcheck: + C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" make -s -j 4 spellcheck' + + +after_test: + - cmd: | + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + REM Oh the joys of shell scripting with strings passed through CMD: + REM Note: currently Python installation path with MSYS is buggy [#1584] + C:\msys64\usr\bin\bash -lc 'date -u; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/bin:$PATH" make -s install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ; ln -fs "NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ./.inst/NUT-for-Windows-x86_64-SNAPSHOT ; ( cd .inst/NUT-for-Windows-x86_64-SNAPSHOT ; find . -ls ; )' + cd .inst + 7z a ../NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%.7z NUT* + - cmd: | + REM Preserve the current working directory: + set CHERE_INVOKING=yes + REM Start a 64 bit Mingw environment: + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" ; export PATH ; ccache -sv || ccache -s || echo "SKIP: Could not query ccache stats"' + C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" ; export PATH ; ccache -x || echo "SKIP: Could not query ccache compression stats"' + + +artifacts: + - path: 'NUT-for-Windows*.7z' + name: Bundle of binary files and FOSS dependencies of NUT for Windows + + - path: config.log + name: config.log of recent build of NUT for Windows + + - path: config.nut_report_feature.log + name: config.nut_report_feature.log of recent build of NUT for Windows + +# Example optional cache (depends on file change): +# - C:\msys64 -> appveyor.yml +cache: + - C:\msys64\home\appveyor\.ccache + - C:\msys64\home\appveyor\ccache # likely missing, no problem - but the name is reported in ccache status + - C:\Users\appveyor\AppData\Local\ccache # may be default in newer versions + - C:\Users\appveyor\AppData\Local\.ccache # may be missing, but for completeness like above + - C:\msys64\var\cache\pacman\pkg + - C:\msys64\home\appveyor\nut-win-deps + +# Below we tried to stash binaries of MSYS2 environment +# so VM deployment is faster on subsequent builds +# (update/install "from scratch" costs about 3 min), +# but unstashing the archive takes comparable time +# and often leads to conflicts in pacman book-keeping, +# while creating/updating the archive costs ~10 min. + #- C:\msys64\var\lib\pacman + #- C:\msys64\var\lib + #- C:\msys64\mingw64 + #- C:\msys64\mingw32 + #- C:\msys64\ucrt64 + #- C:\msys64\clang32 + #- C:\msys64\clang64 + #- C:\msys64\clangarm64 + #- C:\msys64\usr + #- C:\msys64\bin + #- C:\msys64\etc + #- C:\msys64\*.* + #- C:\msys64\installerResources diff --git a/autogen.sh b/autogen.sh index bcb2e915fd..41f15464db 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,6 +2,27 @@ # # Autoreconf wrapper script to ensure that the source tree is # in a buildable state +# NOTE: uses cumbersome dumbest-possible shell syntax for extra portability + +# perl tends to complain if locale is not set (or its files are absent) +if [ -z "${LANG-}" ]; then + LANG="C" + export LANG +fi + +if [ -z "${LC_ALL-}" ] ; then + LC_ALL="C" + export LC_ALL +fi + +VERBOSE_FLAG="" +if [ x"${DEBUG-}" = xtrue ] || [ x"${CI_DEBUG-}" = xtrue ] || [ x"$1" = x-v ] ; then + DEBUG=true + VERBOSE_FLAG="-v" + echo "NUT script $0 will call the tools with higher debug verbosity" +else + DEBUG=false +fi if [ -n "${PYTHON-}" ] ; then # May be a name/path of binary, or one with args - check both @@ -14,13 +35,30 @@ if [ -n "${PYTHON-}" ] ; then # Do not die just here, we may not need the interpreter } else + $DEBUG && echo "=== Picking usable Python version..." PYTHON="" - for P in python python3 python2 ; do - if (command -v "$P" >/dev/null) && $P -c "import re,glob,codecs" ; then + # FIXME: Use something like TAB-completion to find every name on PATH? + for P in python python3 python2 \ + python-3.14 python3.14 \ + python-3.13 python3.13 \ + python-3.12 python3.12 \ + python-3.11 python3.11 \ + 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 $VERBOSE_FLAG -c "import re,glob,codecs" ; then + $DEBUG && echo "=== Picked usable Python version: $P" PYTHON="$P" break fi done + if $DEBUG && [ -z "$PYTHON" ] ; then + echo "=== Did not pick any usable Python version" + fi fi rm -f *.in.AUTOGEN_WITHOUT || true @@ -28,12 +66,12 @@ rm -f *.in.AUTOGEN_WITHOUT || true # re-generate files needed by configure, and created otherwise at 'dist' time if [ ! -f scripts/augeas/nutupsconf.aug.in ] then - if [ -n "${PYTHON-}" ] && $PYTHON -c "import re,glob,codecs"; then + if [ -n "${PYTHON-}" ] && $PYTHON $VERBOSE_FLAG -c "import re,glob,codecs"; then echo "Regenerating Augeas ups.conf lens with '$PYTHON'..." ( # That script is templated; assume @PYTHON@ is the only # road-bump there cd scripts/augeas \ - && $PYTHON ./gen-nutupsconf-aug.py.in + && $PYTHON $VERBOSE_FLAG ./gen-nutupsconf-aug.py.in ) || exit 1 else echo "----------------------------------------------------------------------" @@ -45,18 +83,44 @@ 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 fi +# Keep in sync with tools/nut-usbinfo.pl outputs: +# * List actual file opens: +# grep -i '">' tools/nut-usbinfo.pl +# * List the names involved: +# grep -E 'output.*=' tools/nut-usbinfo.pl +# Also check that the last re-generation is newer than the sources involved +# (stay on top of CI rebuilds, development, Git branch switching...) +# Someone please tell me why GNU `find dir -newer X -name Y -o -name Z` does +# not filter away layer by layer, but rather finds the names Z and beyond +# (same for the other way around)? Anyway, dumbed down for the most trivial +# `find` implementations out there... if [ ! -f scripts/udev/nut-usbups.rules.in -o \ - ! -f scripts/devd/nut-usb.conf.in ] -then + ! -f scripts/hotplug/libhid.usermap -o \ + ! -f scripts/upower/95-upower-hid.hwdb -o \ + ! -f scripts/devd/nut-usb.conf.in -o \ + ! -f scripts/devd/nut-usb.quirks -o \ + ! -f tools/nut-scanner/nutscan-usb.h ] \ +|| [ -n "`find drivers -newer scripts/hotplug/libhid.usermap | grep -E '(-hid|nutdrv_qx|usb.*)\.c'`" ] \ +|| [ -n "`find drivers -not -newer tools/nut-usbinfo.pl | grep -E '(-hid|nutdrv_qx|usb.*)\.c'`" ] \ +; then if perl -e 1; then + VERBOSE_FLAG_PERL="" + if $DEBUG ; then + if perl -d:Devel::Trace -e 1 >/dev/null 2>/dev/null ; then + VERBOSE_FLAG_PERL="-d:Devel::Trace" + else + echo "=== Can not trace perl, try sudo cpan install 'Devel::Trace'" + fi + fi echo "Regenerating the USB helper files..." cd tools && { - ./nut-usbinfo.pl || exit 1 + perl $VERBOSE_FLAG_PERL ./nut-usbinfo.pl || exit 1 cd .. } else @@ -82,29 +146,95 @@ fi echo "Regenerating the list of legacy *-mib.c files in current codebase to produce DMFs later" ( cd scripts/DMF && ./gen-legacy-mibfiles-list.sh ) -if [ ! -e scripts/systemd/nut-common.tmpfiles.in ]; then - echo '# autoconf requires this file exists before generating configure script; it will be overwritten by configure during a build' > scripts/systemd/nut-common.tmpfiles.in +if [ ! -f scripts/systemd/nut-common-tmpfiles.conf.in ]; then + ( echo '# autoconf requires this file exists before generating configure script;' + echo '# it will be overwritten by running configure during an actual build' + ) > scripts/systemd/nut-common-tmpfiles.conf.in fi # now we can safely call autoreconf -if ( command -v dos2unix ) 2>/dev/null >/dev/null \ -&& ! ( dos2unix < configure.ac | cmp - configure.ac ) 2>/dev/null >/dev/null ; then - echo "WARNING: Did not confirm that configure.ac has Unix EOL markers;" - echo "this may cause issues for m4 parsing with autotools below." - if [ -e .git ] ; then - echo "You may want to enforce that Git uses 'lf' line endings and re-checkout:" - echo " :; git config core.autocrlf false && git config core.eol lf" +if ( command -v dos2unix ) 2>/dev/null >/dev/null ; then + if ( dos2unix < configure.ac | cmp - configure.ac ) 2>/dev/null >/dev/null ; then + : + else + echo "WARNING: Did not confirm that configure.ac has Unix EOL markers;" + echo "this may cause issues for m4 parsing with autotools below." + if [ -f .git ] || [ -d .git ] ; then + echo "You may want to enforce that Git uses 'lf' line endings and re-checkout:" + echo " :; git config core.autocrlf false && git config core.eol lf && rm .git/index && git checkout -f" + fi + echo "" fi - echo "" fi >&2 +# Required by autoconf for non-"foreign" projects; +# is tracked as a NEWS.adoc for us however. +[ -f NEWS ] || { echo "Please see NEWS.adoc for actual contents" > NEWS; } +[ -f README ] || { echo "Please see README.adoc for actual contents" > README; } + +echo "Calling autoreconf..." +AUTOTOOL_RES=0 +if $DEBUG ; then + LEGACY_MIBFILES_LIST=legacy-mibfiles-list.mk.in \ + autoreconf -iv --warnings=all -d || AUTOTOOL_RES=$? +else + # This tool's own verbosity is rather compact (whom it called) + # and not too useful for actual troubleshooting, while not too + # noisy to just disable. + LEGACY_MIBFILES_LIST=legacy-mibfiles-list.mk.in \ + autoreconf -iv || AUTOTOOL_RES=$? +fi + +[ "$AUTOTOOL_RES" = 0 ] && [ -s configure ] && [ -x configure ] \ +|| { cat << EOF +FAILED: did not generate an executable configure script! + # Note: on some systems "autoreconf", "automake" et al are dispatcher # scripts, and need you to explicitly say which version you want, e.g. -# export AUTOCONF_VERSION=2.65 AUTOMAKE_VERSION=1.10 +# export AUTOCONF_VERSION=2.65 AUTOMAKE_VERSION=1.13 # If you get issues with AC_DISABLE_STATIC make sure you have libtool. -echo "Calling autoreconf..." -LEGACY_MIBFILES_LIST=legacy-mibfiles-list.mk.in \ -autoreconf -iv && { - sh -n configure 2>/dev/null >/dev/null \ - || { echo "FAILED: configure script did not pass shell interpreter syntax checks" >&2 ; exit 1; } +# +# If it complains about "too few" or "excess" "arguments to builtin ifdef", +# check the configure.ac line it refers to and un-comment (or comment away) +# the third argument for AM_SILENT_RULES check, or comment away the whole +# "ifdef" block if your autotools still would not grok it. +EOF + exit 1 +} >&2 + +# Some autoconf versions may leave "/bin/sh" regardless of CONFIG_SHELL +# which originally was made for "recheck" operations +if [ -n "${CONFIG_SHELL-}" ]; then + case "${CONFIG_SHELL-}" in + */*) ;; # use as is, assume full path + *) + ENV_PROG="`command -v env`" 2>/dev/null + if [ -n "$ENV_PROG" -a -x "$ENV_PROG" ] ; then + echo "Using '$ENV_PROG' to call unqualified CONFIG_SHELL program name '$CONFIG_SHELL'" >&2 + CONFIG_SHELL="$ENV_PROG $CONFIG_SHELL" + fi + ;; + esac + + echo "Injecting caller-provided CONFIG_SHELL='$CONFIG_SHELL' into the script" >&2 + echo "#!${CONFIG_SHELL}" > configure.tmp + cat configure >> configure.tmp + # keep the original file rights intact + cat configure.tmp > configure + rm configure.tmp +else + CONFIG_SHELL="`head -1 configure | sed 's,^#!,,'`" +fi + +# NOTE: Unquoted, may be multi-token +$CONFIG_SHELL -n configure 2>/dev/null >/dev/null \ +|| { echo "FAILED: configure script did not pass shell interpreter syntax checks with $CONFIG_SHELL" >&2 + echo "NOTE: If you are using an older OS release, try executing the script with" >&2 + echo "a more functional shell implementation (dtksh, bash, dash...)" >&2 + echo "You can re-run this script with a CONFIG_SHELL in environment" >&2 + exit 1 } + +echo "The generated configure script passed shell interpreter syntax checks" +echo "Please proceed by running './configure --with-many-desired-options'" +echo "For details check './configure --help' or docs/configure.txt in NUT sources" diff --git a/ci_build.sh b/ci_build.sh index bf95c42c93..a9e80bc7f7 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -10,14 +10,48 @@ ################################################################################ set -e +SCRIPTDIR="`dirname "$0"`" +SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" + +SCRIPT_PATH="${SCRIPTDIR}/`basename $0`" +SCRIPT_ARGS=("$@") # 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++" @@ -27,7 +61,11 @@ case "$BUILD_TYPE" in fightwarn-clang) CC="clang" CXX="clang++" - CPP="clang-cpp" + if (command -v clang-cpp) >/dev/null 2>/dev/null ; then + CPP="clang-cpp" + else + CPP="clang -E" + fi BUILD_TYPE=fightwarn ;; esac @@ -36,22 +74,31 @@ 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 +# configure default is "no"; an "auto" value is "yes unless CFLAGS say something" +[ -n "${BUILD_DEBUGINFO-}" ] || BUILD_DEBUGINFO="" + # Set this to enable verbose profiling [ -n "${CI_TIME-}" ] || CI_TIME="" case "$CI_TIME" in @@ -82,11 +129,112 @@ esac # (allowing to rebuild interactively and investigate that set-up)? [ -n "${CI_FAILFAST-}" ] || CI_FAILFAST=false -[ -n "$MAKE" ] || MAKE=make +# We allow some CI setups to CI_SKIP_CHECK (avoiding it during single-process +# scripted build), so tests can be done as a visibly separate stage. +# This does not really apply to some build scenarios whose purpose is to +# loop and check many build scenarios (e.g. BUILD_TYPE="default-all-errors" +# and "fightwarn*" family), but it is up to caller when and why to set it. +# It is also a concern of the caller (for now) if actually passing the check +# relies on something this script does (set envvars, change paths...) +[ -n "${CI_SKIP_CHECK-}" ] || CI_SKIP_CHECK=false + +# 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 + +# Just in case we get blanks from CI - consider them as not-set: +if [ -z "`echo "${MAKE-}" | tr -d ' '`" ] ; then + if [ "$1" = spellcheck -o "$1" = spellcheck-interactive ] \ + && (command -v gmake) >/dev/null 2>/dev/null \ + ; then + # GNU make processes quiet mode better, which helps with spellcheck use-case + MAKE=gmake + else + # Use system default, there should be one (or fail eventually if not) + MAKE=make + fi + export MAKE +fi + [ -n "$GGREP" ] || GGREP=grep [ -n "$MAKE_FLAGS_QUIET" ] || MAKE_FLAGS_QUIET="VERBOSE=0 V=0 -s" -[ -n "$MAKE_FLAGS_VERBOSE" ] || MAKE_FLAGS_VERBOSE="VERBOSE=1 -s" +[ -n "$MAKE_FLAGS_VERBOSE" ] || MAKE_FLAGS_VERBOSE="VERBOSE=1 V=1 -s" +[ -n "$MAKE_FLAGS_CLEAN" ] || MAKE_FLAGS_CLEAN="${MAKE_FLAGS_QUIET}" + +# This is where many symlinks like "gcc -> ../bin/ccache" reside +# (note: a "-" value requests to NOT use a CI_CCACHE_SYMLINKDIR; +# ccache may still be used via prefixing if the tool is found in +# the PATH, unless you export CI_CCACHE_USE=no also): +if [ -z "${CI_CCACHE_SYMLINKDIR-}" ] ; then + for D in \ + "/usr/lib/ccache" \ + "/mingw64/lib/ccache/bin" \ + "/mingw32/lib/ccache/bin" \ + "/usr/lib64/ccache" \ + "/usr/libexec/ccache" \ + "/usr/lib/ccache/bin" \ + "/usr/local/lib/ccache" \ + ; do + if [ -d "$D" ] ; then + if ( ls -la "$D" | grep -e ' -> .*ccache' >/dev/null) \ + || ( test -n "`find "$D" -maxdepth 1 -type f -exec grep -li ccache '{}' \;`" ) \ + ; then + CI_CCACHE_SYMLINKDIR="$D" && break + else + echo "WARNING: Found potential CI_CCACHE_SYMLINKDIR='$D' but it did not host expected symlink patterns, skipped" >&2 + fi + fi + done + + if [ -n "${CI_CCACHE_SYMLINKDIR-}" ] ; then + echo "INFO: Detected CI_CCACHE_SYMLINKDIR='$CI_CCACHE_SYMLINKDIR'; specify another explicitly if desired" >&2 + else + echo "WARNING: Did not find any CI_CCACHE_SYMLINKDIR; specify one explicitly if desired" >&2 + fi +else + if [ x"${CI_CCACHE_SYMLINKDIR-}" = x- ] ; then + echo "INFO: Empty CI_CCACHE_SYMLINKDIR was explicitly requested" >&2 + CI_CCACHE_SYMLINKDIR="" + fi +fi + +if [ -z "$TMPDIR" ]; then + echo "WARNING: TMPDIR not set, trying to guess" + if [ -d /tmp -a -w /tmp ] ; then + TMPDIR=/tmp + export TMPDIR + fi +fi + +if [ -z "$TMPDIR" ]; then + echo "WARNING: TMPDIR still not set, some tools (notably clang) can fail" +fi # For two-phase builds (quick parallel make first, sequential retry if failed) # how verbose should that first phase be? Nothing, automake list of ops, CLIs? @@ -127,7 +275,7 @@ esac # for actual builds with parallel phases. Specify a whitespace to neuter. if [ -z "$PARMAKE_FLAGS" ]; then PARMAKE_FLAGS="-j $NPARMAKES" - if LANG=C LC_ALL=C "$MAKE" --version 2>&1 | egrep 'GNU Make|Free Software Foundation' > /dev/null ; then + if LANG=C LC_ALL=C "$MAKE" --version 2>&1 | grep -E 'GNU Make|Free Software Foundation' > /dev/null ; then PARMAKE_FLAGS="$PARMAKE_FLAGS -l $PARMAKE_LA_LIMIT" echo "Parallel builds would spawn up to $NPARMAKES jobs (detected $NCPUS CPUs), or peak out at $PARMAKE_LA_LIMIT system load average" >&2 else @@ -148,10 +296,9 @@ for L in $NODE_LABELS ; do "NUT_BUILD_CAPS=cppunit"|"NUT_BUILD_CAPS=cppunit=yes") [ -n "$CANBUILD_CPPUNIT_TESTS" ] || CANBUILD_CPPUNIT_TESTS=yes ;; - # Currently we don't build --with-nutconf" by default, unless desired - # explicitly by developers' local workflow (or NUT CI farm recipes), - # that codebase needs some clean-up first... This should cover both - # the tool and the cppunit tests for it (if active per above). + # This should cover both the --with-nutconf tool setting + # and the cppunit tests for it (if active per above). + # By default we would nowadays guess (requires C++11). "NUT_BUILD_CAPS=nutconf=no") [ -n "$CANBUILD_NUTCONF" ] || CANBUILD_NUTCONF=no ;; "NUT_BUILD_CAPS=nutconf=no-gcc") @@ -172,6 +319,16 @@ for L in $NODE_LABELS ; do "NUT_BUILD_CAPS=cppcheck"|"NUT_BUILD_CAPS=cppcheck=yes") [ -n "$CANBUILD_CPPCHECK_TESTS" ] || CANBUILD_CPPCHECK_TESTS=yes ;; + # Some workers (presumably where several executors or separate + # Jenkins agents) are enabled randomly fail NIT tests, once in + # a hundred runs or so. This option allows isolated workers to + # proclaim they are safe places to "make check-NIT" (and we can + # see if that is true, over time). + "NUT_BUILD_CAPS=NIT=no") + [ -n "$CANBUILD_NIT_TESTS" ] || CANBUILD_NIT_TESTS=no ;; + "NUT_BUILD_CAPS=NIT"|"NUT_BUILD_CAPS=NIT=yes") + [ -n "$CANBUILD_NIT_TESTS" ] || CANBUILD_NIT_TESTS=yes ;; + "NUT_BUILD_CAPS=docs:man=no") [ -n "$CANBUILD_DOCS_MAN" ] || CANBUILD_DOCS_MAN=no ;; "NUT_BUILD_CAPS=docs:man"|"NUT_BUILD_CAPS=docs:man=yes") @@ -196,21 +353,49 @@ for L in $NODE_LABELS ; do [ -n "$CANBUILD_LIBGD_CGI" ] || CANBUILD_LIBGD_CGI=no ;; "NUT_BUILD_CAPS=cgi"|"NUT_BUILD_CAPS=cgi=yes") [ -n "$CANBUILD_LIBGD_CGI" ] || CANBUILD_LIBGD_CGI=yes ;; + + # Currently for nut-scanner, might be more later - hence agnostic naming: + "NUT_BUILD_CAPS=libltdl=no") + [ -n "$CANBUILD_WITH_LIBLTDL" ] || CANBUILD_WITH_LIBLTDL=no ;; + "NUT_BUILD_CAPS=libltdl"|"NUT_BUILD_CAPS=libltdl=yes") + [ -n "$CANBUILD_WITH_LIBLTDL" ] || CANBUILD_WITH_LIBLTDL=yes ;; esac 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" ;; + *msys2*) + CI_OS_NAME="windows-msys2" ;; + *mingw*64*) + CI_OS_NAME="windows-mingw64" ;; + *mingw*32*) + CI_OS_NAME="windows-mingw32" ;; *windows*) CI_OS_NAME="windows" ;; *[Mm]ac*|*arwin*|*[Oo][Ss][Xx]*) @@ -240,6 +425,41 @@ fi # CI builds on Travis [ -n "$CI_OS_NAME" ] || CI_OS_NAME="$TRAVIS_OS_NAME" +case "${CI_OS_NAME}" in + windows*) + # At the moment WIN32 builds are quite particular in their + # desires, for headers to declare what is needed, and yet + # there is currently not much real variation in supportable + # build environment (mingw variants). Lest we hardcode + # stuff in configure script, define some here: + case "$CFLAGS" in + *-D_POSIX=*) ;; + *) CFLAGS="$CFLAGS -D_POSIX=1" ;; + esac + case "$CFLAGS" in + *-D_POSIX_C_SOURCE=*) ;; + *) CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=200112L" ;; + esac + case "$CFLAGS" in + *-D_WIN32_WINNT=*) ;; + *) CFLAGS="$CFLAGS -D_WIN32_WINNT=0xffff" ;; + esac + + case "$CXXFLAGS" in + *-D_POSIX=*) ;; + *) CXXFLAGS="$CXXFLAGS -D_POSIX=1" ;; + esac + case "$CXXFLAGS" in + *-D_POSIX_C_SOURCE=*) ;; + *) CXXFLAGS="$CXXFLAGS -D_POSIX_C_SOURCE=200112L" ;; + esac + case "$CXXFLAGS" in + *-D_WIN32_WINNT=*) ;; + *) CXXFLAGS="$CXXFLAGS -D_WIN32_WINNT=0xffff" ;; + esac + ;; +esac + # Analyze some environmental choices if [ -z "${CANBUILD_LIBGD_CGI-}" ]; then # No prereq dll and headers on win so far @@ -252,14 +472,30 @@ if [ -z "${CANBUILD_LIBGD_CGI-}" ]; then # See also below for some compiler-dependent decisions fi -configure_nut() { - local CONFIGURE_SCRIPT=./configure +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 + +# Would hold full path to the CONFIGURE_SCRIPT="${SCRIPTDIR}/${CONFIGURE_SCRIPT_FILENAME}" +CONFIGURE_SCRIPT="" +autogen_get_CONFIGURE_SCRIPT() { + # Autogen once (delete the file if some scenario ever requires to re-autogen) + if [ -n "${CONFIGURE_SCRIPT}" -a -s "${CONFIGURE_SCRIPT}" ] ; then return 0 ; fi + + pushd "${SCRIPTDIR}" || exit + if [[ "$CI_OS_NAME" == "windows" ]] ; then - find . -ls - CONFIGURE_SCRIPT=./configure.bat + # Debug once + [ -n "$CONFIGURE_SCRIPT" ] || find . -ls + CONFIGURE_SCRIPT="configure.bat" + else + CONFIGURE_SCRIPT="configure" fi - if [ ! -s "$CONFIGURE_SCRIPT" ]; then + if [ ! -s "./$CONFIGURE_SCRIPT" ]; then # Note: modern auto(re)conf requires pkg-config to generate the configure # script, so to stage the situation of building without one (as if on an # older system) we have to remove it when we already have the script. @@ -272,6 +508,36 @@ configure_nut() { fi || exit fi + # Retain the full path to configure script file + CONFIGURE_SCRIPT="${SCRIPTDIR}/${CONFIGURE_SCRIPT}" + + popd || exit +} + +configure_CI_BUILDDIR() { + autogen_get_CONFIGURE_SCRIPT + + 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 + fi +} + +configure_nut() { + configure_CI_BUILDDIR + + # Note: maintainer-clean checks remove this, and then some systems' + # build toolchains noisily complain about missing LD path candidate + if [ -n "$BUILD_PREFIX" ]; then + # tmp/lib/ + mkdir -p "$BUILD_PREFIX"/lib + fi + if [ -n "$INST_PREFIX" ]; then + # .inst/ + mkdir -p "$INST_PREFIX" + 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 +559,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 @@ -306,6 +572,7 @@ configure_nut() { build_to_only_catch_errors_target() { if [ $# = 0 ]; then + # Re-enter with an arg list build_to_only_catch_errors_target all ; return $? fi @@ -323,13 +590,22 @@ build_to_only_catch_errors_target() { default) $CI_TIME $MAKE -k $PARMAKE_FLAGS "$@" ;; esac ) && echo "`date`: SUCCESS" ; ) || \ - ( echo "`date`: Starting the sequential build attempt (to list remaining files with errors considered fatal for this build configuration) for '$@'..."; \ + ( RET=$? + if [ "$CI_FAILFAST" = true ]; then + echo "===== Aborting after parallel build attempt failure for '$*' because CI_FAILFAST=$CI_FAILFAST" >&2 + exit $RET + fi + echo "`date`: Starting the sequential build attempt (to list remaining files with errors considered fatal for this build configuration) for '$@'..."; \ $CI_TIME $MAKE $MAKE_FLAGS_VERBOSE "$@" -k ) || return $? return 0 } -build_to_only_catch_errors() { - build_to_only_catch_errors_target all || return $? +build_to_only_catch_errors_check() { + # Specifically run (an optional) "make check" + if [ "${CI_SKIP_CHECK}" = true ] ; then + echo "`date`: SKIP: not starting a '$MAKE check' for quick sanity test of the products built with the current compiler and standards, because caller requested CI_SKIP_CHECK=true; plain build has just succeeded however" + return 0 + fi echo "`date`: Starting a '$MAKE check' for quick sanity test of the products built with the current compiler and standards" $CI_TIME $MAKE $MAKE_FLAGS_QUIET check \ @@ -339,6 +615,12 @@ build_to_only_catch_errors() { return 0 } +build_to_only_catch_errors() { + build_to_only_catch_errors_target all || return $? + build_to_only_catch_errors_check || return $? + return 0 +} + ccache_stats() { local WHEN="$1" [ -n "$WHEN" ] || WHEN="some time around the" @@ -346,6 +628,12 @@ ccache_stats() { if [ -d "$CCACHE_DIR" ]; then echo "CCache stats $WHEN build:" ccache -s || true + # Some ccache versions support compression stats + # This may take time on slower systems however + # (and/or with larger cache contents) => off by default + if [ x"${CI_CCACHE_STATS_COMPRESSION-}" = xtrue ]; then + ccache -x 2>/dev/null || true + fi else echo "WARNING: CCache stats $WHEN build: tool is enabled, but CCACHE_DIR='$CCACHE_DIR' was not found now" >&2 fi @@ -381,20 +669,27 @@ check_gitignore() { return 0 fi - # One invocation should report to log: - git status $GIT_ARGS -s -- "${FILE_GLOB}" \ - | egrep -v '^.. \.ci.*\.log.*' \ - | egrep "${FILE_REGEX}" \ - || echo "WARNING: Could not query git repo while in `pwd`" >&2 + # One invocation should report to log if there was any discrepancy + # to report in the first place (GITOUT may be empty without error): + GITOUT="`git status $GIT_ARGS -s -- "${FILE_GLOB}"`" \ + || { echo "WARNING: Could not query git repo while in `pwd`" >&2 ; GITOUT=""; } + + if [ -n "${GITOUT-}" ] ; then + echo "$GITOUT" \ + | grep -E -v '^.. \.ci.*\.log.*' \ + | grep -E "${FILE_REGEX}" + else + echo "Got no output and no errors querying git repo while in `pwd`: seems clean" >&2 + fi echo "===" # Another invocation checks that there was nothing to complain about: - if [ -n "`git status $GIT_ARGS -s "${FILE_GLOB}" | egrep -v '^.. \.ci.*\.log.*' | egrep "^.. ${FILE_REGEX}"`" ] \ + if [ -n "`git status $GIT_ARGS -s "${FILE_GLOB}" | grep -E -v '^.. \.ci.*\.log.*' | grep -E "^.. ${FILE_REGEX}"`" ] \ && [ "$CI_REQUIRE_GOOD_GITIGNORE" != false ] \ ; 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 + 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! You can 'export CI_REQUIRE_GOOD_GITIGNORE=false' if appropriate." >&2 if [ "$GIT_DIFF_SHOW" = true ]; then - git diff -- "${FILE_GLOB}" || true + PAGER=cat git diff -- "${FILE_GLOB}" || true fi echo "===" return 1 @@ -430,10 +725,17 @@ optional_maintainer_clean_check() { [ -z "$CI_TIME" ] || echo "`date`: Starting maintainer-clean check of currently tested project..." # Note: currently Makefile.am has just a dummy "distcleancheck" rule - $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS maintainer-clean || return + case "$MAKE_FLAGS $DISTCHECK_FLAGS $PARMAKE_FLAGS $MAKE_FLAGS_CLEAN" in + *V=0*) + $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS $MAKE_FLAGS_CLEAN maintainer-clean > /dev/null || return + ;; + *) + $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS $MAKE_FLAGS_CLEAN maintainer-clean || return + esac GIT_ARGS="--ignored" check_gitignore "maintainer-clean" || return fi + return 0 } @@ -454,20 +756,48 @@ optional_dist_clean_check() { [ -z "$CI_TIME" ] || echo "`date`: Starting dist-clean check of currently tested project..." # Note: currently Makefile.am has just a dummy "distcleancheck" rule - $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS distclean || return + $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS $MAKE_FLAGS_CLEAN distclean || return check_gitignore "distclean" || return fi return 0 } -if [ "$1" = spellcheck ] && [ -z "$BUILD_TYPE" ] ; then +if [ "$1" = inplace ] && [ -z "$BUILD_TYPE" ] ; then + shift + BUILD_TYPE="inplace" +fi + +if [ "$1" = spellcheck -o "$1" = spellcheck-interactive ] && [ -z "$BUILD_TYPE" ] ; then # Note: this is a little hack to reduce typing # and scrolling in (docs) developer iterations. + case "$CI_OS_NAME" in + windows-msys2) + # https://github.com/msys2/MSYS2-packages/issues/2088 + echo "==========================================================================" + echo "WARNING: some MSYS2 builds of aspell are broken with 'tex' support" + echo "Are you sure you run this in a functional build environment? Ctrl+C if not" + echo "==========================================================================" + sleep 5 + ;; + *) if ! (command -v aspell) 2>/dev/null >/dev/null ; then + echo "==========================================================================" + echo "WARNING: Seems you do not have 'aspell' in PATH (but maybe NUT configure" + echo "script would find the spellchecking toolkit elsewhere)" + echo "Are you sure you run this in a functional build environment? Ctrl+C if not" + echo "==========================================================================" + sleep 5 + fi + ;; + esac >&2 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 + build_to_only_catch_errors_target $1 ; exit else + # TODO: Actually do it (default-spellcheck-interactive)? + if [ "$1" = spellcheck-interactive ] ; then + echo "Only CI-building 'spellcheck', please do the interactive part manually if needed" >&2 + fi BUILD_TYPE="default-spellcheck" shift fi @@ -476,7 +806,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 | grep -E '^(PATH|[^ ]*CCACHE[^ ]*|CI_[^ ]*|OS_[^ ]*|CANBUILD_[^ ]*|NODE_LABELS|MAKE|C[^ ]*FLAGS|LDFLAGS|ARCH[^ ]*|BITS[^ ]*|CC|CXX|CPP|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 @@ -493,7 +823,12 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ -d "./.inst/" ]; then rm -rf ./.inst/ fi - mkdir -p tmp/ .inst/ + + # Pre-create locations; tmp/lib in particular to avoid (on MacOS xcode): + # ld: warning: directory not found for option '-L/Users/distiller/project/tmp/lib' + # Note that maintainer-clean checks can remove these directory trees, + # so we re-create them just in case in the configure_nut() method too. + mkdir -p tmp/lib .inst/ BUILD_PREFIX="$PWD/tmp" INST_PREFIX="$PWD/.inst" @@ -511,15 +846,26 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp $CXX --version || true fi - PATH="`echo "$PATH" | sed -e 's,^/usr/lib/ccache/?:,,' -e 's,:/usr/lib/ccache/?:,,' -e 's,:/usr/lib/ccache/?$,,' -e 's,^/usr/lib/ccache/?$,,'`" - CCACHE_PATH="$PATH" - CCACHE_DIR="${HOME}/.ccache" - export CCACHE_PATH CCACHE_DIR PATH - HAVE_CCACHE=no - if (command -v ccache || which ccache) && ls -la /usr/lib/ccache ; then - HAVE_CCACHE=yes + if [ x"${CI_CCACHE_USE-}" = xno ]; then + HAVE_CCACHE=no + CI_CCACHE_SYMLINKDIR="" + echo "WARNING: Caller required to not use ccache even if available" >&2 + else + if [ -n "${CI_CCACHE_SYMLINKDIR}" ]; then + # Tell ccache the PATH without itself in it, to avoid loops processing + PATH="`echo "$PATH" | sed -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?$,,' -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?$,,'`" + fi + CCACHE_PATH="$PATH" + CCACHE_DIR="${HOME}/.ccache" + export CCACHE_PATH CCACHE_DIR PATH + HAVE_CCACHE=no + if (command -v ccache || which ccache) \ + && ( [ -z "${CI_CCACHE_SYMLINKDIR}" ] || ls -la "${CI_CCACHE_SYMLINKDIR}" ) \ + ; then + HAVE_CCACHE=yes + fi + mkdir -p "${CCACHE_DIR}"/ || HAVE_CCACHE=no fi - mkdir -p "${CCACHE_DIR}"/ || HAVE_CCACHE=no ccache_stats "before" @@ -530,11 +876,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 +907,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,25 +920,58 @@ 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 if [ -n "$CPP" ] ; then - [ -x "$CPP" ] && export CPP + # Note: can be a multi-token name like "clang -E" or just not a full pathname + ( [ -x "$CPP" ] || $CPP --help >/dev/null 2>/dev/null ) && export CPP else if is_gnucc "cpp" ; then CPP=cpp && export CPP + else + case "$COMPILER_FAMILY" in + CLANG*|GCC*) CPP="$CC -E" && export CPP ;; + esac fi fi @@ -647,16 +1042,44 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("PKG_CONFIG_PATH=${DEFAULT_PKG_CONFIG_PATH}") fi + CONFIG_OPTS+=("--enable-keep_nut_report_feature") CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}") CONFIG_OPTS+=("--sysconfdir=${BUILD_PREFIX}/etc/nut") CONFIG_OPTS+=("--with-udev-dir=${BUILD_PREFIX}/etc/udev") CONFIG_OPTS+=("--with-devd-dir=${BUILD_PREFIX}/etc/devd") CONFIG_OPTS+=("--with-hotplug-dir=${BUILD_PREFIX}/etc/hotplug") + if [ x"${INPLACE_RUNTIME-}" = xtrue ]; then + CONFIG_OPTS+=("--enable-inplace-runtime") + fi + + # 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. + if [ "${CANBUILD_NIT_TESTS-}" != no ] ; then + CONFIG_OPTS+=("--enable-check-NIT") + else + echo "WARNING: Build agent does not say it can reliably 'make check-NIT'" >&2 + CONFIG_OPTS+=("--disable-check-NIT") + fi + if [ -n "${PYTHON-}" ]; then # WARNING: Watch out for whitespaces, not handled here! CONFIG_OPTS+=("--with-python=${PYTHON}") fi + # Even in scenarios that request --with-all, we do not want + # to choke on absence of desktop-related modules in Python. + # Just make sure relevant install recipes are tested: + CONFIG_OPTS+=("--with-nut_monitor=force") + CONFIG_OPTS+=("--with-pynut=auto") + + # Similarly for nut-scanner which requires libltdl which + # is not ubiquitous on CI workers. So unless agent labels + # declare it should be capable, err on the safe side: + if [ "${CANBUILD_WITH_LIBLTDL-}" != yes ] ; then + CONFIG_OPTS+=("--with-nut-scanner=auto") + fi # Some OSes have broken cppunit support, it crashes either build/link # or at run-time. While distros take time to figure out fixes, we can @@ -669,26 +1092,54 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("--enable-cppunit=no") fi - if [ -z "${CANBUILD_NUTCONF-}" ] \ - || ( [ "${CANBUILD_NUTCONF-}" = "no-gcc" ] && [ "$COMPILER_FAMILY" = "GCC" ] ) \ + if ( [ "${CANBUILD_NUTCONF-}" = "no-gcc" ] && [ "$COMPILER_FAMILY" = "GCC" ] ) \ || ( [ "${CANBUILD_NUTCONF-}" = "no-clang" ] && [ "$COMPILER_FAMILY" = "CLANG" ] ) \ ; then CANBUILD_NUTCONF=no fi - if [ "${CANBUILD_NUTCONF-}" = yes ] ; then - echo "WARNING: Build agent says it can build nutconf, enabling the experimental feature" >&2 - CONFIG_OPTS+=("--with-nutconf=yes") - elif [ "${CANBUILD_NUTCONF-}" = no ] ; then - echo "WARNING: Build agent says it can not build nutconf (or did not say anything), disabling the feature" >&2 - CONFIG_OPTS+=("--with-nutconf=no") - fi + case "${CANBUILD_NUTCONF-}" in + yes) + # Depends on C++11 or newer, so let configure script try this tediously + # unless we know we would not build for the too-old language revision + case "${CXXFLAGS-}" in + *-std=c++98*|*-std=gnu++98*|*-std=c++03*|*-std=gnu++03*) + echo "WARNING: Build agent says it can build nutconf, but requires a test with C++ revision too old - so not requiring the experimental feature (auto-try)" >&2 + CONFIG_OPTS+=("--with-nutconf=auto") + ;; + *-std=c++0x*|*-std=gnu++0x*|*-std=c++1*|*-std=gnu++1*|*-std=c++2*|*-std=gnu++2*) + echo "WARNING: Build agent says it can build nutconf, and requires a test with a sufficiently new C++ revision - so requiring the experimental feature" >&2 + CONFIG_OPTS+=("--with-nutconf=yes") + ;; + *) + echo "WARNING: Build agent says it can build nutconf, and does not specify a test with prticular C++ revision - so not requiring the experimental feature (auto-try)" >&2 + CONFIG_OPTS+=("--with-nutconf=auto") + ;; + esac + ;; + no) + echo "WARNING: Build agent says it can not build nutconf, disabling the feature (do not even try)" >&2 + CONFIG_OPTS+=("--with-nutconf=no") + ;; + "") + CONFIG_OPTS+=("--with-nutconf=auto") + ;; + esac if [ "${CANBUILD_VALGRIND_TESTS-}" = no ] ; then echo "WARNING: Build agent says it has a broken valgrind, adding configure option to skip tests with it" >&2 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. @@ -696,12 +1147,14 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp case "$BUILD_TYPE" in "default-nodoc") CONFIG_OPTS+=("--with-doc=no") + CONFIG_OPTS+=("--disable-spellcheck") DO_DISTCHECK=no ;; "default-spellcheck"|"default-shellcheck") CONFIG_OPTS+=("--with-all=no") CONFIG_OPTS+=("--with-libltdl=no") CONFIG_OPTS+=("--with-doc=man=skip") + CONFIG_OPTS+=("--enable-spellcheck") #TBD# CONFIG_OPTS+=("--with-shellcheck=yes") DO_DISTCHECK=no ;; @@ -755,13 +1208,14 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Do not build the docs as we are interested in binary code CONFIG_OPTS+=("--with-doc=skip") + CONFIG_OPTS+=("--disable-spellcheck") # Enable as many binaries to build as current worker setup allows CONFIG_OPTS+=("--with-all=auto") 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 @@ -780,6 +1234,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp "default-alldrv") # Do not build the docs and make possible a distcheck below CONFIG_OPTS+=("--with-doc=skip") + CONFIG_OPTS+=("--disable-spellcheck") if [ "${CANBUILD_DRIVERS_ALL-}" = no ]; then echo "WARNING: Build agent says it can't build 'all' driver types; will ask for what we can build" >&2 if [ "$DO_DISTCHECK" != no ]; then @@ -812,57 +1267,74 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp "default"|"default-tgt:"*|*) # Do not build the docs and tell distcheck it is okay CONFIG_OPTS+=("--with-doc=skip") + CONFIG_OPTS+=("--disable-spellcheck") ;; esac # NOTE: The case "$BUILD_TYPE" above was about setting CONFIG_OPTS. # There is another below for running actual scenarios. if [ "$HAVE_CCACHE" = yes ] && [ "${COMPILER_FAMILY}" = GCC -o "${COMPILER_FAMILY}" = CLANG ]; then - PATH="/usr/lib/ccache:$PATH" - export PATH - if [ -n "$CC" ]; then - if [ -x "/usr/lib/ccache/`basename "$CC"`" ]; then + if [ -n "${CI_CCACHE_SYMLINKDIR}" ]; then + echo "INFO: Using ccache via PATH preferring tool names in ${CI_CCACHE_SYMLINKDIR}" >&2 + PATH="${CI_CCACHE_SYMLINKDIR}:$PATH" + export PATH + else + case "$CC" in + "") ;; # skip + *ccache*) ;; # already requested to use ccache + *) CC="ccache $CC" ;; + esac + case "$CXX" in + "") ;; # skip + *ccache*) ;; # already requested to use ccache + *) CXX="ccache $CXX" ;; + esac + # No-op for CPP currently + fi + if [ -n "$CC" ] && [ -n "${CI_CCACHE_SYMLINKDIR}" ]; then + if [ -x "${CI_CCACHE_SYMLINKDIR}/`basename "$CC"`" ]; then case "$CC" in *ccache*) ;; */*) DIR_CC="`dirname "$CC"`" && [ -n "$DIR_CC" ] && DIR_CC="`cd "$DIR_CC" && pwd `" && [ -n "$DIR_CC" ] && [ -d "$DIR_CC" ] || DIR_CC="" [ -z "$CCACHE_PATH" ] && CCACHE_PATH="$DIR_CC" || \ - if echo "$CCACHE_PATH" | egrep '(^'"$DIR_CC"':.*|^'"$DIR_CC"'$|:'"$DIR_CC"':|:'"$DIR_CC"'$)' ; then + if echo "$CCACHE_PATH" | grep -E '(^'"$DIR_CC"':.*|^'"$DIR_CC"'$|:'"$DIR_CC"':|:'"$DIR_CC"'$)' ; then CCACHE_PATH="$DIR_CC:$CCACHE_PATH" fi ;; esac - CC="/usr/lib/ccache/`basename "$CC"`" + CC="${CI_CCACHE_SYMLINKDIR}/`basename "$CC"`" else CC="ccache $CC" fi fi - if [ -n "$CXX" ]; then - if [ -x "/usr/lib/ccache/`basename "$CXX"`" ]; then + if [ -n "$CXX" ] && [ -n "${CI_CCACHE_SYMLINKDIR}" ]; then + if [ -x "${CI_CCACHE_SYMLINKDIR}/`basename "$CXX"`" ]; then case "$CXX" in *ccache*) ;; */*) DIR_CXX="`dirname "$CXX"`" && [ -n "$DIR_CXX" ] && DIR_CXX="`cd "$DIR_CXX" && pwd `" && [ -n "$DIR_CXX" ] && [ -d "$DIR_CXX" ] || DIR_CXX="" [ -z "$CCACHE_PATH" ] && CCACHE_PATH="$DIR_CXX" || \ - if echo "$CCACHE_PATH" | egrep '(^'"$DIR_CXX"':.*|^'"$DIR_CXX"'$|:'"$DIR_CXX"':|:'"$DIR_CXX"'$)' ; then + if echo "$CCACHE_PATH" | grep -E '(^'"$DIR_CXX"':.*|^'"$DIR_CXX"'$|:'"$DIR_CXX"':|:'"$DIR_CXX"'$)' ; then CCACHE_PATH="$DIR_CXX:$CCACHE_PATH" fi ;; esac - CXX="/usr/lib/ccache/`basename "$CXX"`" + CXX="${CI_CCACHE_SYMLINKDIR}/`basename "$CXX"`" else CXX="ccache $CXX" fi fi - if [ -n "$CPP" ] && [ -x "/usr/lib/ccache/`basename "$CPP"`" ]; then + if [ -n "$CPP" ] && [ -n "${CI_CCACHE_SYMLINKDIR}" ] \ + && [ -x "${CI_CCACHE_SYMLINKDIR}/`basename "$CPP"`" ]; then case "$CPP" in *ccache*) ;; */*) DIR_CPP="`dirname "$CPP"`" && [ -n "$DIR_CPP" ] && DIR_CPP="`cd "$DIR_CPP" && pwd `" && [ -n "$DIR_CPP" ] && [ -d "$DIR_CPP" ] || DIR_CPP="" [ -z "$CCACHE_PATH" ] && CCACHE_PATH="$DIR_CPP" || \ - if echo "$CCACHE_PATH" | egrep '(^'"$DIR_CPP"':.*|^'"$DIR_CPP"'$|:'"$DIR_CPP"':|:'"$DIR_CPP"'$)' ; then + if echo "$CCACHE_PATH" | grep -E '(^'"$DIR_CPP"':.*|^'"$DIR_CPP"'$|:'"$DIR_CPP"':|:'"$DIR_CPP"'$)' ; then CCACHE_PATH="$DIR_CPP:$CCACHE_PATH" fi ;; esac - CPP="/usr/lib/ccache/`basename "$CPP"`" + CPP="${CI_CCACHE_SYMLINKDIR}/`basename "$CPP"`" else : # CPP="ccache $CPP" fi @@ -899,26 +1371,66 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # and errors are found more easily in a wall of text: CONFIG_OPTS+=("--enable-Wcolor") + if [ -n "${BUILD_DEBUGINFO-}" ]; then + CONFIG_OPTS+=("--with-debuginfo=${BUILD_DEBUGINFO}") + fi + # Note: modern auto(re)conf requires pkg-config to generate the configure # script, so to stage the situation of building without one (as if on an # older system) we have to remove it when we already have the script. # This matches the use-case of distro-building from release tarballs that # include all needed pre-generated files to rely less on OS facilities. + if [ -s Makefile ]; then + if [ -n "`find "${SCRIPTDIR}" -name configure.ac -newer "${CI_BUILDDIR}"/configure`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name '*.m4' -newer "${CI_BUILDDIR}"/configure`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.am -newer "${CI_BUILDDIR}"/Makefile`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.in -newer "${CI_BUILDDIR}"/Makefile`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.am -newer "${CI_BUILDDIR}"/Makefile.in`" ] \ + ; then + # Avoid reconfiguring for the sake of distclean + echo "=== Starting initial clean-up (from old build products): TAKING SHORTCUT because recipes changed" + rm -f "${CI_BUILDDIR}"/Makefile "${CI_BUILDDIR}"/configure + fi + fi + + # When itertating configure.ac or m4 sources, we can end up with an + # existing but useless scropt file - nuke it and restart from scratch! + if [ -s "${CI_BUILDDIR}"/configure ] ; then + if ! sh -n "${CI_BUILDDIR}"/configure 2>/dev/null ; then + echo "=== Starting initial clean-up (from old build products): TAKING SHORTCUT because current configure script syntax is broken" + rm -f "${CI_BUILDDIR}"/Makefile "${CI_BUILDDIR}"/configure + fi + fi + if [ -s Makefile ]; then # Let initial clean-up be at default verbosity + + # Handle Ctrl+C with helpful suggestions: + trap 'echo "!!! If clean-up looped remaking the configure script for maintainer-clean, try to:"; echo " rm -f Makefile configure ; $0 $SCRIPT_ARGS"' 2 + echo "=== Starting initial clean-up (from old build products)" - ${MAKE} maintainer-clean -k || ${MAKE} distclean -k || true + case "$MAKE_FLAGS $MAKE_FLAGS_CLEAN" in + *V=0*) + ${MAKE} maintainer-clean $MAKE_FLAGS_CLEAN -k > /dev/null \ + || ${MAKE} maintainer-clean $MAKE_FLAGS_CLEAN -k + ;; + *) + ${MAKE} maintainer-clean $MAKE_FLAGS_CLEAN -k + esac \ + || ${MAKE} distclean $MAKE_FLAGS_CLEAN -k \ + || true echo "=== Finished initial clean-up" - fi - if [ "$CI_OS_NAME" = "windows" ] ; then - $CI_TIME ./autogen.sh || true - else - $CI_TIME ./autogen.sh ### 2>/dev/null + trap - 2 fi + # Just prepare `configure` script; we run it at different points + # below depending on scenario + autogen_get_CONFIGURE_SCRIPT + 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 +1521,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 +1542,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 @@ -1114,9 +1626,9 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # would just re-evaluate `configure` to update the # Makefile to remove it and other generated data. #echo "=== Clean the sandbox, $BUILDSTODO build variants remaining..." - #$MAKE distclean -k || true + #$MAKE distclean $MAKE_FLAGS_CLEAN -k || true - echo "=== Starting NUT_SSL_VARIANT='$NUT_SSL_VARIANT', $BUILDSTODO build variants remaining..." + echo "=== Starting 'NUT_SSL_VARIANT=$NUT_SSL_VARIANT', $BUILDSTODO build variants remaining..." case "$NUT_SSL_VARIANT" in ""|auto|default) # Quietly build one scenario, whatever we can (or not) @@ -1131,7 +1643,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ) ;; *) - echo "=== Building with NUT_SSL_VARIANT='${NUT_SSL_VARIANT}' ..." + echo "=== Building with 'NUT_SSL_VARIANT=${NUT_SSL_VARIANT}' ..." ( CONFIG_OPTS+=("--with-${NUT_SSL_VARIANT}") configure_nut ) @@ -1148,12 +1660,29 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp continue } - echo "=== Configured NUT_SSL_VARIANT='$NUT_SSL_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." - build_to_only_catch_errors && { + echo "=== Configured 'NUT_SSL_VARIANT=$NUT_SSL_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" + # Use default target e.g. "all": + build_to_only_catch_errors_target && { SUCCEEDED="${SUCCEEDED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[build]" } || { RES_ALLERRORS=$? FAILED="${FAILED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[build]" + # Help find end of build (before cleanup noise) in logs: + echo "=== FAILED 'NUT_SSL_VARIANT=${NUT_SSL_VARIANT}' build" + if [ "$CI_FAILFAST" = true ]; then + echo "===== Aborting because CI_FAILFAST=$CI_FAILFAST" >&2 + break + fi + } + + build_to_only_catch_errors_check && { + SUCCEEDED="${SUCCEEDED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[check]" + } || { + RES_ALLERRORS=$? + FAILED="${FAILED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[check]" + # Help find end of build (before cleanup noise) in logs: + echo "=== FAILED 'NUT_SSL_VARIANT=${NUT_SSL_VARIANT}' check" if [ "$CI_FAILFAST" = true ]; then echo "===== Aborting because CI_FAILFAST=$CI_FAILFAST" >&2 break @@ -1196,7 +1725,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp echo "=== Completed sandbox cleanup-check after NUT_SSL_VARIANT=${NUT_SSL_VARIANT}, $BUILDSTODO build variants remaining" else if [ "$BUILDSTODO" -gt 0 ] && [ "${DO_CLEAN_CHECK-}" != no ]; then - $MAKE distclean -k || echo "WARNING: 'make distclean' FAILED: $? ... proceeding" >&2 + $MAKE distclean $MAKE_FLAGS_CLEAN -k \ + || echo "WARNING: 'make distclean' FAILED: $? ... proceeding" >&2 echo "=== Completed sandbox cleanup after NUT_SSL_VARIANT=${NUT_SSL_VARIANT}, $BUILDSTODO build variants remaining" else echo "=== SKIPPED sandbox cleanup because DO_CLEAN_CHECK=$DO_CLEAN_CHECK and $BUILDSTODO build variants remaining" @@ -1220,14 +1750,16 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp || [ "$CI_FAILFAST" = "true" -a "$RES_ALLERRORS" = 0 ] \ ) && \ for NUT_USB_VARIANT in $NUT_USB_VARIANTS ; do - echo "=== Starting NUT_USB_VARIANT='$NUT_USB_VARIANT', $BUILDSTODO build variants remaining..." + echo "=== Starting 'NUT_USB_VARIANT=$NUT_USB_VARIANT', $BUILDSTODO build variants remaining..." case "$NUT_USB_VARIANT" in ""|auto|default) # 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,26 +1767,32 @@ 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 ) ;; libusb-*) - echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + echo "=== Building with 'NUT_USB_VARIANT=${NUT_USB_VARIANT}' ..." + ( 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 ) ;; *) - echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + echo "=== Building with 'NUT_USB_VARIANT=${NUT_USB_VARIANT}' ..." + ( 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 @@ -1272,12 +1810,29 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp continue } - echo "=== Configured NUT_USB_VARIANT='$NUT_USB_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." - build_to_only_catch_errors && { + echo "=== Configured 'NUT_USB_VARIANT=$NUT_USB_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" + # Use default target e.g. "all": + build_to_only_catch_errors_target && { SUCCEEDED="${SUCCEEDED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[build]" } || { RES_ALLERRORS=$? FAILED="${FAILED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[build]" + # Help find end of build (before cleanup noise) in logs: + echo "=== FAILED 'NUT_USB_VARIANT=${NUT_USB_VARIANT}' build" + if [ "$CI_FAILFAST" = true ]; then + echo "===== Aborting because CI_FAILFAST=$CI_FAILFAST" >&2 + break + fi + } + + build_to_only_catch_errors_check && { + SUCCEEDED="${SUCCEEDED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[check]" + } || { + RES_ALLERRORS=$? + FAILED="${FAILED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[check]" + # Help find end of build (before cleanup noise) in logs: + echo "=== FAILED 'NUT_USB_VARIANT=${NUT_USB_VARIANT}' check" if [ "$CI_FAILFAST" = true ]; then echo "===== Aborting because CI_FAILFAST=$CI_FAILFAST" >&2 break @@ -1318,7 +1873,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp echo "=== Completed sandbox cleanup-check after NUT_USB_VARIANT=${NUT_USB_VARIANT}, $BUILDSTODO build variants remaining" else if [ "$BUILDSTODO" -gt 0 ] && [ "${DO_CLEAN_CHECK-}" != no ]; then - $MAKE distclean -k || echo "WARNING: 'make distclean' FAILED: $? ... proceeding" >&2 + $MAKE distclean $MAKE_FLAGS_CLEAN -k \ + || echo "WARNING: 'make distclean' FAILED: $? ... proceeding" >&2 echo "=== Completed sandbox cleanup after NUT_USB_VARIANT=${NUT_USB_VARIANT}, $BUILDSTODO build variants remaining" else echo "=== SKIPPED sandbox cleanup because DO_CLEAN_CHECK=$DO_CLEAN_CHECK and $BUILDSTODO build variants remaining" @@ -1349,6 +1905,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 @@ -1363,15 +1921,30 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Quiet parallel make, redo loud sequential if that failed build_to_only_catch_errors_target all - # Can be noisy if regen is needed (DMF branch) + # Can be noisy if regen is needed (DMF branch with this or that BUILD_TGT) # Bail out due to DMF will (optionally) happen in the next check - GIT_DIFF_SHOW=false FILE_DESCR="DMF" FILE_REGEX='\.dmf$' FILE_GLOB='*.dmf' check_gitignore "$BUILD_TGT" || true + #GIT_DIFF_SHOW=false FILE_DESCR="DMF" FILE_REGEX='\.dmf$' FILE_GLOB='*.dmf' check_gitignore "$BUILD_TGT" || true # This check should not-list the "*.dmf" files even if they # changed (listed as a special group above) but should still # fail due to them: ( set -o pipefail; check_gitignore "all" | egrep -v '^.. .*\.dmf$' ) || exit + if test -s "${SCRIPTDIR}/install-sh" \ + && grep -w MKDIRPROG "${SCRIPTDIR}/install-sh" >/dev/null \ + ; then + if grep -v '#' "${SCRIPTDIR}/install-sh" | grep -E '\$mkdirprog.*-p' >/dev/null \ + ; then + true + else + if [ -z "${MKDIRPROG-}" ] ; then + echo "`date`: WARNING: setting MKDIRPROG to work around possible deficiencies of install-sh" + MKDIRPROG="mkdir -p" + export MKDIRPROG + fi + fi + fi + [ -z "$CI_TIME" ] || echo "`date`: Trying to install the currently tested project into the custom DESTDIR..." $CI_TIME $MAKE $MAKE_FLAGS_VERBOSE DESTDIR="$INST_PREFIX" install [ -n "$CI_TIME" ] && echo "`date`: listing files installed into the custom DESTDIR..." && \ @@ -1391,7 +1964,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp MAKEFLAGS="${MAKEFLAGS-} $MAKE_FLAGS_QUIET" \ $CI_TIME $MAKE DISTCHECK_FLAGS="$DISTCHECK_FLAGS" $PARMAKE_FLAGS distcheck - FILE_DESCR="DMF" FILE_REGEX='\.dmf$' FILE_GLOB='*.dmf' check_gitignore "$BUILD_TGT" || true + #FILE_DESCR="DMF" FILE_REGEX='\.dmf$' FILE_GLOB='*.dmf' check_gitignore "$BUILD_TGT" || true check_gitignore "distcheck" || exit ) fi @@ -1403,37 +1976,165 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp bindings) pushd "./bindings/${BINDING}" && ./ci_build.sh ;; -"") - echo "ERROR: No BUILD_TYPE was specified, doing a minimal default ritual without any required options" >&2 +""|inplace) + echo "WARNING: No BUILD_TYPE was specified, doing a minimal default ritual without any *required* build products and with developer-oriented options" >&2 if [ -n "${BUILD_WARNOPT}${BUILD_WARNFATAL}" ]; then - echo "WARNING: BUILD_WARNOPT and BUILD_WARNFATAL settings are ignored in this mode" >&2 + echo "WARNING: BUILD_WARNOPT and BUILD_WARNFATAL settings are ignored in this mode (warnings are always enabled and fatal for these developer-oriented builds)" >&2 sleep 5 fi echo "" + + if [ -n "${CI_CCACHE_SYMLINKDIR}" ] && [ -d "${CI_CCACHE_SYMLINKDIR}" ] ; then + PATH="`echo "$PATH" | sed -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?$,,' -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?$,,'`" + CCACHE_PATH="$PATH" + CCACHE_DIR="${HOME}/.ccache" + if (command -v ccache || which ccache) && ls -la "${CI_CCACHE_SYMLINKDIR}" && mkdir -p "${CCACHE_DIR}"/ ; then + echo "INFO: Using ccache via PATH preferring tool names in ${CI_CCACHE_SYMLINKDIR}" >&2 + PATH="${CI_CCACHE_SYMLINKDIR}:$PATH" + export CCACHE_PATH CCACHE_DIR PATH + fi + fi + + cd "${SCRIPTDIR}" if [ -s Makefile ]; then + if [ -n "`find "${SCRIPTDIR}" -name configure.ac -newer "${CI_BUILDDIR}"/configure`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name '*.m4' -newer "${CI_BUILDDIR}"/configure`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.am -newer "${CI_BUILDDIR}"/Makefile`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.in -newer "${CI_BUILDDIR}"/Makefile`" ] \ + || [ -n "`find "${SCRIPTDIR}" -name Makefile.am -newer "${CI_BUILDDIR}"/Makefile.in`" ] \ + ; then + # Avoid reconfiguring for the sake of distclean + echo "=== Starting initial clean-up (from old build products): TAKING SHORTCUT because recipes changed" + rm -f "${CI_BUILDDIR}"/Makefile "${CI_BUILDDIR}"/configure + fi + fi + + # When itertating configure.ac or m4 sources, we can end up with an + # existing but useless scropt file - nuke it and restart from scratch! + if [ -s "${CI_BUILDDIR}"/configure ] ; then + if ! sh -n "${CI_BUILDDIR}"/configure 2>/dev/null ; then + echo "=== Starting initial clean-up (from old build products): TAKING SHORTCUT because current configure script syntax is broken" + rm -f "${CI_BUILDDIR}"/Makefile "${CI_BUILDDIR}"/configure + fi + fi + + if [ -s Makefile ]; then + # Help developers debug: # Let initial clean-up be at default verbosity echo "=== Starting initial clean-up (from old build products)" ${MAKE} realclean -k || true echo "=== Finished initial clean-up" fi - ./autogen.sh + configure_CI_BUILDDIR # 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. + CONFIG_OPTS=(--enable-Wcolor \ + --enable-warnings --enable-Werror \ + --enable-keep_nut_report_feature \ + --with-all=auto --with-cgi=auto --with-serial=auto \ + --with-dev=auto --with-doc=skip \ + --with-nut_monitor=auto --with-pynut=auto \ + --disable-force-nut-version-header \ + --enable-check-NIT --enable-maintainer-mode) + + # Not default for parameter-less build, to prevent "make check-NIT" + # from somehow interfering with the running daemons. + if [ x"${INPLACE_RUNTIME-}" = xtrue ] || [ x"${BUILD_TYPE-}" = xinplace ] ; then + CONFIG_OPTS+=("--enable-inplace-runtime") + else + # Help developers debug: + CONFIG_OPTS+=("--disable-silent-rules") + fi + + if [ -n "${BUILD_DEBUGINFO-}" ]; then + CONFIG_OPTS+=("--with-debuginfo=${BUILD_DEBUGINFO}") + else + CONFIG_OPTS+=("--with-debuginfo=auto") + fi + + ${CONFIGURE_SCRIPT} "${CONFIG_OPTS[@]}" # NOTE: Currently parallel builds are expected to succeed (as far # as recipes are concerned), and the builds without a BUILD_TYPE # are aimed at developer iterations so not tweaking verbosity. - #$MAKE all && \ - $MAKE $PARMAKE_FLAGS all && \ - $MAKE check + #$MAKE all || \ + $MAKE $PARMAKE_FLAGS all || exit + if [ "${CI_SKIP_CHECK}" != true ] ; then $MAKE check || exit ; fi + + case "$CI_OS_NAME" in + windows*) + echo "INFO: Build and tests succeeded. If you plan to install a NUT bundle now" >&2 + echo "for practical usage or testing on a native Windows system, consider calling" >&2 + echo " make install-win-bundle DESTDIR=`pwd`/.inst/NUT4Win" >&2 + echo "(or some other valid DESTDIR) to co-bundle dependency FOSS DLL files there." >&2 + ;; + esac + + if [ -s config.nut_report_feature.log ]; then + cat config.nut_report_feature.log + fi + ;; + +# These mingw modes below are currently experimental and not too integrated +# with this script per se; it is intended to run for NUT CI farm on prepared +# Linux+mingw worker nodes (see scripts/Windows/README.adoc) in an uniform +# manner, using mostly default settings (warnings in particular) and some +# values hardcoded in that script (ARCH, CFLAGS, ...). +# Note that semi-native builds with e.g. MSYS2 on Windows should "just work" as +# on any other supported platform (more details in docs/config-prereqs.txt). +cross-windows-mingw*) + echo "INFO: When using build-mingw-nut.sh consider 'export INSTALL_WIN_BUNDLE=true' to use mainstream DLL co-bundling recipe" >&2 + + if [ "$HAVE_CCACHE" = yes ] \ + && [ -n "${CI_CCACHE_SYMLINKDIR}" ] \ + && [ -d "${CI_CCACHE_SYMLINKDIR}" ] \ + ; then + PATH="`echo "$PATH" | sed -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?:,,' -e 's,:'"${CI_CCACHE_SYMLINKDIR}"'/?$,,' -e 's,^'"${CI_CCACHE_SYMLINKDIR}"'/?$,,'`" + CCACHE_PATH="$PATH" + CCACHE_DIR="${HOME}/.ccache" + if (command -v ccache || which ccache) && ls -la "${CI_CCACHE_SYMLINKDIR}" && mkdir -p "${CCACHE_DIR}"/ ; then + echo "INFO: Using ccache via PATH preferring tool names in ${CI_CCACHE_SYMLINKDIR}" >&2 + PATH="${CI_CCACHE_SYMLINKDIR}:$PATH" + export CCACHE_PATH CCACHE_DIR PATH + fi + fi + + ./autogen.sh || exit + cd scripts/Windows || exit + + cmd="" # default soup of the day, as defined in the called script + case "$BUILD_TYPE" in + cross-windows-mingw32|cross-windows-mingw-32) cmd="all32" ;; + cross-windows-mingw64|cross-windows-mingw-64) cmd="all64" ;; + cross-windows-mingw) # make a difficult guess + case "${BITS-}" in + 32|64) cmd="all${BITS}" + ;; + *) # Use other clues + case "${CFLAGS-}${CXXFLAGS-}${LDFLAGS-}" in + *-m32*-m64*|*-m64*-m32*) + echo "FATAL: Mismatched bitness requested in *FLAGS" >&2 + exit 1 + ;; + *-m32*) cmd="all32" ;; + *-m64*) cmd="all64" ;; + esac + ;; + esac + ;; + esac + + SOURCEMODE="out-of-tree" \ + MAKEFLAGS="$PARMAKE_FLAGS" \ + KEEP_NUT_REPORT_FEATURE="true" \ + ./build-mingw-nut.sh $cmd ;; + *) pushd "./builds/${BUILD_TYPE}" && REPO_DIR="$(dirs -l +1)" ./ci_build.sh ;; diff --git a/clients/Makefile.am b/clients/Makefile.am index 8189724ae8..b873046cc0 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -1,4 +1,14 @@ # Network UPS Tools: clients + +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + EXTRA_DIST = # nutclient.cpp for some legacy reason (maybe initial detached development?) @@ -10,14 +20,22 @@ AM_CXXFLAGS = -DHAVE_NUTCOMMON=1 -I$(top_srcdir)/include $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libcommonclient.la \ $(top_builddir)/common/libparseconf.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +LDADD_FULL = $(top_builddir)/common/libcommon.la libupsclient.la $(NETLIBS) +if WITH_SSL + LDADD_FULL += $(LIBSSL_LIBS) +endif -# by default, link programs in this directory with libcommon.a -LDADD = $(top_builddir)/common/libcommon.la libupsclient.la $(NETLIBS) +LDADD_CLIENT = $(top_builddir)/common/libcommonclient.la libupsclient.la $(NETLIBS) if WITH_SSL - LDADD += $(LIBSSL_LIBS) + LDADD_CLIENT += $(LIBSSL_LIBS) endif +# by default, link programs in this directory with +# the more compact libcommonclient.a bundle +LDADD = $(LDADD_CLIENT) + # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. @@ -32,16 +50,26 @@ endif bin_PROGRAMS = upsc upslog upsrw upscmd dist_bin_SCRIPTS = upssched-cmd sbin_PROGRAMS = upsmon upssched +if HAVE_WINDOWS_SOCKETS + sbin_PROGRAMS += message +endif lib_LTLIBRARIES = libupsclient.la if HAVE_CXX11 lib_LTLIBRARIES += libnutclient.la lib_LTLIBRARIES += libnutclientstub.la endif + +# Optionally deliverable as part of NUT public API: if WITH_DEV - include_HEADERS = upsclient.h ../include/parseconf.h nutclient.h nutclientmem.h - include_HEADERS += ../include/dmfsnmp.h -endif + include_HEADERS = upsclient.h +if HAVE_CXX11 + include_HEADERS += nutclient.h nutclientmem.h +else !HAVE_CXX11 + EXTRA_DIST += nutclient.h nutclientmem.h +endif !HAVE_CXX11 +endif WITH_DEV + if WITH_CGI cgiexec_PROGRAMS = upsstats.cgi upsimage.cgi upsset.cgi endif @@ -51,9 +79,13 @@ upscmd_SOURCES = upscmd.c upsclient.h upsrw_SOURCES = upsrw.c upsclient.h upslog_SOURCES = upslog.c upsclient.h upslog.h upsmon_SOURCES = upsmon.c upsmon.h upsclient.h +upsmon_LDADD = $(LDADD_FULL) +if HAVE_WINDOWS_SOCKETS +message_SOURCES = message.c +endif upssched_SOURCES = upssched.c upssched.h -upssched_LDADD = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la $(NETLIBS) +upssched_LDADD = $(top_builddir)/common/libcommonclient.la $(top_builddir)/common/libparseconf.la $(NETLIBS) upsimage_cgi_SOURCES = upsimage.c upsclient.h upsimagearg.h cgilib.c cgilib.h upsimage_cgi_LDADD = $(LDADD) $(LIBGD_LDFLAGS) @@ -62,9 +94,12 @@ upsset_cgi_SOURCES = upsset.c upsclient.h cgilib.c cgilib.h upsstats_cgi_SOURCES = upsstats.c upsclient.h status.h upsstats.h \ upsimagearg.h cgilib.c cgilib.h -# not LDADD. +# not LDADD... why? libupsclient_la_SOURCES = upsclient.c upsclient.h libupsclient_la_LIBADD = $(top_builddir)/common/libcommonclient.la +if HAVE_WINDOWS_SOCKETS + libupsclient_la_LIBADD += -lws2_32 +endif if WITH_SSL libupsclient_la_LIBADD += $(LIBSSL_LIBS) endif @@ -75,15 +110,24 @@ 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:1:0 +libupsclient_la_LDFLAGS += -export-symbols-regex ^upscli_ +if HAVE_WINDOWS + # Many versions of MingW seem to fail to build non-static DLL without this + libupsclient_la_LDFLAGS += -no-undefined +endif 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:2: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 +if HAVE_WINDOWS + # Many versions of MingW seem to fail to build non-static DLL without this + libnutclient_la_LDFLAGS += -no-undefined +endif else EXTRA_DIST += nutclient.h nutclient.cpp endif @@ -91,8 +135,12 @@ endif if HAVE_CXX11 # libnutclientstub version information and build libnutclientstub_la_SOURCES = nutclientmem.h nutclientmem.cpp -libnutclientstub_la_LDFLAGS = -version-info 1:0:0 +libnutclientstub_la_LDFLAGS = -version-info 1:1:0 libnutclientstub_la_LIBADD = libnutclient.la +if HAVE_WINDOWS + # Many versions of MingW seem to fail to build non-static DLL without this + libnutclientstub_la_LDFLAGS += -no-undefined +endif else EXTRA_DIST += nutclientmem.h nutclientmem.cpp endif @@ -104,4 +152,4 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! #clean-local: -# rm -rf $(builddir)/.deps +# $(AM_V_at)rm -rf $(builddir)/.deps diff --git a/clients/cgilib.c b/clients/cgilib.c index 88cebfacb0..160618e354 100644 --- a/clients/cgilib.c +++ b/clients/cgilib.c @@ -17,9 +17,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "common.h" + #include +#include -#include "common.h" #include "cgilib.h" #include "parseconf.h" @@ -40,6 +42,7 @@ static char *unescape(char *buf) ch = ' '; if (ch == '%') { + long l; if (i + 2 > buflen) fatalx(EXIT_FAILURE, "string too short for escaped char"); hex[0] = buf[++i]; @@ -48,7 +51,7 @@ static char *unescape(char *buf) if (!isxdigit((unsigned char) hex[0]) || !isxdigit((unsigned char) hex[1])) fatalx(EXIT_FAILURE, "bad escape char"); - long l = strtol(hex, NULL, 16); + l = strtol(hex, NULL, 16); assert(l>=0); assert(l<=255); ch = (char)l; /* FIXME: Loophole about non-ASCII symbols in top 128 values, or negatives for signed char... */ diff --git a/clients/message.c b/clients/message.c new file mode 100644 index 0000000000..93a823e0e8 --- /dev/null +++ b/clients/message.c @@ -0,0 +1,14 @@ +#ifdef WIN32 +#include + +int main(int argc, char ** argv) +{ + if (argc < 2) + return 1; + + MessageBox(NULL, argv[1], "Network UPS Tools", + MB_OK|MB_ICONEXCLAMATION|MB_SERVICE_NOTIFICATION); + + return 0; +} +#endif diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index 34179527f5..63ec2ae903 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -17,10 +17,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "nutclient.h" #include +/* TODO: Make it a run-time option like upsdebugx(), + * probably with a verbosity level variable in each + * class instance */ +#include /* std::cerr debugging */ +#include +#include +#include + +#ifndef WIN32 +# ifdef HAVE_PTHREAD +/* this include is needed on AIX to have errno stored in thread local storage */ +# include +# endif +#endif + #include #include #include @@ -29,7 +45,38 @@ /* Thanks to Benjamin Roux (http://broux.developpez.com/articles/c/sockets/) */ #ifdef WIN32 # include -#else +# define SOCK_OPT_CAST (char *) + +/* equivalent of W32_NETWORK_CALL_OVERRIDE + * invoked by wincompat.h in upsclient.c: + */ +static inline int sktconnect(int fh, struct sockaddr * name, int len) +{ + int ret = connect(fh,name,len); + errno = WSAGetLastError(); + return ret; +} +static inline int sktread(int fh, void *buf, int size) +{ + int ret = recv(fh,(char*)buf,size,0); + errno = WSAGetLastError(); + return ret; +} +static inline int sktwrite(int fh, const void*buf, int size) +{ + int ret = send(fh,(char*)buf,size,0); + errno = WSAGetLastError(); + return ret; +} +static inline int sktclose(int fh) +{ + int ret = closesocket((SOCKET)fh); + errno = WSAGetLastError(); + return ret; +} + +#else /* not WIN32 */ + # include # include # include @@ -37,28 +84,67 @@ # include /* close */ # include /* gethostbyname */ # include -# define INVALID_SOCKET -1 -# define SOCKET_ERROR -1 -# define closesocket(s) close(s) +# ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +# endif +# ifndef SOCKET_ERROR +# define SOCKET_ERROR -1 +# endif +# ifndef closesocket +# define closesocket(s) close(s) +# endif +# define SOCK_OPT_CAST typedef int SOCKET; typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr SOCKADDR; typedef struct in_addr IN_ADDR; + +# define sktconnect(h,n,l) ::connect(h,n,l) +# define sktread(h,b,s) ::read(h,b,s) +# define sktwrite(h,b,s) ::write(h,b,s) +# define sktclose(h) ::close(h) + +/* WA for Solaris/i386 bug: non-blocking connect sets errno to ENOENT */ +# if (defined NUT_PLATFORM_SOLARIS) +# define SOLARIS_i386_NBCONNECT_ENOENT(status) ( (!strcmp("i386", CPU_TYPE)) ? (ENOENT == (status)) : 0 ) +# else +# define SOLARIS_i386_NBCONNECT_ENOENT(status) (0) +# endif /* end of Solaris/i386 WA for non-blocking connect */ + +/* WA for AIX bug: non-blocking connect sets errno to 0 */ +# if (defined NUT_PLATFORM_AIX) +# define AIX_NBCONNECT_0(status) (0 == (status)) +# else +# define AIX_NBCONNECT_0(status) (0) +# endif /* end of AIX WA for non-blocking connect */ + #endif /* WIN32 */ -/* End of Windows/Linux Socket compatibility layer: */ +/* End of Windows/Linux Socket compatibility layer */ /* Include nut common utility functions or define simple ones if not */ #ifdef HAVE_NUTCOMMON +/* For C++ code below, we do not actually use the fallback time methods + * (on mingw mostly), but in C++ context they happen to conflict with + * time.h or ctime headers, while native-C does not. Just disable fallback: + */ +# ifndef HAVE_GMTIME_R +# define HAVE_GMTIME_R 111 +# endif +# ifndef HAVE_LOCALTIME_R +# define HAVE_LOCALTIME_R 111 +# endif #include "common.h" -#else /* HAVE_NUTCOMMON */ +#else /* not HAVE_NUTCOMMON */ #include #include static inline void *xmalloc(size_t size){return malloc(size);} static inline void *xcalloc(size_t number, size_t size){return calloc(number, size);} static inline void *xrealloc(void *ptr, size_t size){return realloc(ptr, size);} static inline char *xstrdup(const char *string){return strdup(string);} -#endif /* HAVE_NUTCOMMON */ +#endif /* not HAVE_NUTCOMMON */ + +#include "nut_stdint.h" /* PRIuMAX etc. */ /* To stay in line with modern C++, we use nullptr (not numeric NULL * or shim __null on some systems) which was defined after C++98. @@ -108,12 +194,12 @@ std::string SystemException::err() * https://lgtm.com/rules/2165180572/ like: * NutException& operator=(NutException& rhs) = default; */ -NutException::~NutException() {} -SystemException::~SystemException() {} -IOException::~IOException() {} -UnknownHostException::~UnknownHostException() {} -NotConnectedException::~NotConnectedException() {} -TimeoutException::~TimeoutException() {} +NutException::~NutException() noexcept {} +SystemException::~SystemException() noexcept {} +IOException::~IOException() noexcept {} +UnknownHostException::~UnknownHostException() noexcept {} +NotConnectedException::~NotConnectedException() noexcept {} +TimeoutException::~TimeoutException() noexcept {} namespace internal @@ -123,7 +209,7 @@ namespace internal * Internal socket wrapper. * Provides only client socket functions. * - * Implemented as separate internal class to easily hide plateform specificities. + * Implemented as separate internal class to easily hide platform specificities. */ class Socket { @@ -131,11 +217,12 @@ 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 setDebugConnect(bool d); - 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); @@ -147,12 +234,14 @@ class Socket private: SOCKET _sock; + bool _debugConnect; struct timeval _tv; std::string _buffer; /* Received buffer, string because data should be text only. */ }; Socket::Socket(): _sock(INVALID_SOCKET), +_debugConnect(false), _tv() { _tv.tv_sec = -1; @@ -164,12 +253,17 @@ 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::setDebugConnect(bool d) +{ + _debugConnect = d; +} + +void Socket::connect(const std::string& host, uint16_t port) { int sock_fd; struct addrinfo hints, *res, *ai; @@ -178,40 +272,94 @@ void Socket::connect(const std::string& host, int port) fd_set wfds; int error; socklen_t error_size; + +#ifndef WIN32 long fd_flags; +#else + HANDLE event = NULL; + unsigned long argp; - _sock = -1; + WSADATA WSAdata; + WSAStartup(2,&WSAdata); +#endif + + _sock = INVALID_SOCKET; if (host.empty()) { + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): host.empty()" << + std::endl << std::flush; throw nut::UnknownHostException(); } - snprintf(sport, sizeof(sport), "%hu", static_cast(port)); + snprintf(sport, sizeof(sport), "%" PRIuMAX, static_cast(port)); memset(&hints, 0, sizeof(hints)); + /* TODO? Port IPv4 vs IPv6 detail from upsclient.c */ hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): getaddrinfo(" << + host << ", " << + sport << ", " << + "...)" << std::endl << std::flush; + while ((v = getaddrinfo(host.c_str(), sport, &hints, &res)) != 0) { switch (v) { case EAI_AGAIN: continue; case EAI_NONAME: + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "UnknownHostException" << + std::endl << std::flush; throw nut::UnknownHostException(); - case EAI_SYSTEM: - throw nut::SystemException(); case EAI_MEMORY: + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "Out of memory" << + std::endl << std::flush; throw nut::NutException("Out of memory"); +#ifndef WIN32 + case EAI_SYSTEM: +#else + case WSANO_RECOVERY: +#endif + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "SystemException" << + std::endl << std::flush; + throw nut::SystemException(); default: + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "Unknown error" << + std::endl << std::flush; throw nut::NutException("Unknown error"); } } for (ai = res; ai != nullptr; ai = ai->ai_next) { + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): socket(" << + ai->ai_family << ", " << + ai->ai_socktype << ", " << + ai->ai_protocol << ")" << + std::endl << std::flush; + sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): socket(): " << + "sock_fd = " << sock_fd << + std::endl << std::flush; if (sock_fd < 0) { switch (errno) @@ -220,39 +368,94 @@ void Socket::connect(const std::string& host, int port) case EINVAL: break; default: + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "SystemException" << + std::endl << std::flush; throw nut::SystemException(); } continue; } /* non blocking connect */ - if(hasTimeout()) { + if (hasTimeout()) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags |= O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); +#else + event = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( sock_fd, event, FD_CONNECT ); + CloseHandle(event); +#endif } - while ((v = ::connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) { - if(errno == EINPROGRESS) { + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): sktconnect(" << + sock_fd << ", " << + ai->ai_addr << ", " << + ai->ai_addrlen << ")" << + std::endl << std::flush; + + while ((v = sktconnect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) { + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "sktconnect() < 0" << + "; errno = " << errno << + "; v = " << v << + std::endl << std::flush; + +#ifndef WIN32 + if(errno == EINPROGRESS || SOLARIS_i386_NBCONNECT_ENOENT(errno) || AIX_NBCONNECT_0(errno)) { +#else + if(errno == WSAEWOULDBLOCK) { +#endif FD_ZERO(&wfds); FD_SET(sock_fd, &wfds); - select(sock_fd+1, nullptr, &wfds, nullptr, hasTimeout() ? &_tv : nullptr); + select(sock_fd+1, nullptr, &wfds, nullptr, + hasTimeout() ? &_tv : nullptr); if (FD_ISSET(sock_fd, &wfds)) { error_size = sizeof(error); getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, - &error, &error_size); + SOCK_OPT_CAST &error, &error_size); if( error == 0) { /* connect successful */ + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect-select successful" << + std::endl << std::flush; v = 0; break; } errno = error; + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect-select not successful: " << + "errno = " << errno << + std::endl << std::flush; } else { /* Timeout */ v = -1; + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect-select not successful: timeout" << + std::endl << std::flush; break; } + } else { + /* WIN32: errno=10061 is actively refusing connection */ + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "connect not successful: " << + "errno = " << errno << + std::endl << std::flush; } switch (errno) @@ -271,17 +474,34 @@ void Socket::connect(const std::string& host, int port) } if (v < 0) { - close(sock_fd); + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "sktconnect() remains < 0 => sktclose()" << + std::endl << std::flush; + sktclose(sock_fd); continue; } + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "sktconnect() > 0, looks promising" << + std::endl << std::flush; /* switch back to blocking operation */ - if(hasTimeout()) { + if (hasTimeout()) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags &= ~O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); +#else + argp = 0; + ioctlsocket(sock_fd, FIONBIO, &argp); +#endif } + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "saving sock_fd = " << sock_fd << + std::endl << std::flush; _sock = sock_fd; // ups->upserror = 0; // ups->syserrno = 0; @@ -290,10 +510,24 @@ void Socket::connect(const std::string& host, int port) freeaddrinfo(res); +#ifndef WIN32 if (_sock < 0) { +#else + if (_sock == INVALID_SOCKET) { + /* In tracing one may see 18446744073709551615 = "-1" after + * conversion from 'long long unsigned int' to 'int' + * 64-bit WINSOCK API with UINT_PTR , see gory details at e.g. + * https://github.com/openssl/openssl/issues/7282#issuecomment-430633656 + */ +#endif + if (_debugConnect) std::cerr << + "[D2] Socket::connect(): " << + "invalid _sock = " << _sock << + std::endl << std::flush; throw nut::IOException("Cannot connect to host"); } + /* TODO? See upsclient.c for NSS/SSL connection handling */ #ifdef OLD struct hostent *hostinfo = nullptr; @@ -315,7 +549,7 @@ void Socket::connect(const std::string& host, int port) sin.sin_addr = *(IN_ADDR *) hostinfo->h_addr; sin.sin_port = htons(port); sin.sin_family = AF_INET; - if(::connect(_sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR) + if(sktconnect(_sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR) { _sock = INVALID_SOCKET; throw nut::IOException("Cannot connect to host"); @@ -356,7 +590,7 @@ size_t Socket::read(void* buf, size_t sz) } } - ssize_t res = ::read(_sock, buf, sz); + ssize_t res = sktread(_sock, buf, sz); if(res==-1) { disconnect(); @@ -383,7 +617,7 @@ size_t Socket::write(const void* buf, size_t sz) } } - ssize_t res = ::write(_sock, buf, sz); + ssize_t res = sktwrite(_sock, buf, sz); if(res==-1) { disconnect(); @@ -561,7 +795,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 +808,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; @@ -586,12 +820,17 @@ void TcpClient::connect() _socket->connect(_host, _port); } +void TcpClient::setDebugConnect(bool d) +{ + _socket->setDebugConnect(d); +} + std::string TcpClient::getHost()const { return _host; } -int TcpClient::getPort()const +uint16_t TcpClient::getPort()const { return _port; } @@ -606,12 +845,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; } @@ -806,17 +1045,76 @@ TrackingID TcpClient::executeDeviceCommand(const std::string& dev, const std::st return sendTrackingQuery("INSTCMD " + dev + " " + name + " " + param); } +std::map> TcpClient::listDeviceClients(void) +{ + /* Lists all clients of all devices (which have at least one client) */ + std::map> deviceClientsMap; + + std::set devs = getDeviceNames(); + for(std::set::iterator it=devs.begin(); it!=devs.end(); ++it) + { + std::string dev = *it; + std::set deviceClients = deviceGetClients(dev); + if (!deviceClients.empty()) { + deviceClientsMap[dev] = deviceClients; + } + } + + return deviceClientsMap; +} + +std::set TcpClient::deviceGetClients(const std::string& dev) +{ + /* Who did a deviceLogin() to this dev? */ + std::set clients; + + std::vector > res = list("CLIENT", dev); + for(size_t n=0; nexecuteDeviceCommand(getName(), name, param); } +std::set Device::getClients() +{ + if (!isOk()) throw NutException("Invalid device"); + return getClient()->deviceGetClients(getName()); +} + void Device::login() { if (!isOk()) throw NutException("Invalid device"); 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() { + if (!isOk()) throw NutException("Invalid device"); + getClient()->deviceForcedShutdown(getName()); } int Device::getNumLogins() @@ -1566,7 +1877,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 +1946,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 +1958,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 +2036,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 +2054,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..8047e26ced 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 @@ -58,7 +60,7 @@ class NutException : public std::exception NutException(const std::string& msg):_msg(msg){} NutException(const NutException&) = default; NutException& operator=(NutException& rhs) = default; - virtual ~NutException() override; + virtual ~NutException() noexcept override; virtual const char * what() const noexcept override {return this->_msg.c_str();} virtual std::string str() const noexcept {return this->_msg;} private: @@ -74,7 +76,7 @@ class SystemException : public NutException SystemException(); SystemException(const SystemException&) = default; SystemException& operator=(SystemException& rhs) = default; - virtual ~SystemException() override; + virtual ~SystemException() noexcept override; private: static std::string err(); }; @@ -89,7 +91,7 @@ class IOException : public NutException IOException(const std::string& msg):NutException(msg){} IOException(const IOException&) = default; IOException& operator=(IOException& rhs) = default; - virtual ~IOException() override; + virtual ~IOException() noexcept override; }; /** @@ -101,7 +103,7 @@ class UnknownHostException : public IOException UnknownHostException():IOException("Unknown host"){} UnknownHostException(const UnknownHostException&) = default; UnknownHostException& operator=(UnknownHostException& rhs) = default; - virtual ~UnknownHostException() override; + virtual ~UnknownHostException() noexcept override; }; /** @@ -113,7 +115,7 @@ class NotConnectedException : public IOException NotConnectedException():IOException("Not connected"){} NotConnectedException(const NotConnectedException&) = default; NotConnectedException& operator=(NotConnectedException& rhs) = default; - virtual ~NotConnectedException() override; + virtual ~NotConnectedException() noexcept override; }; /** @@ -125,7 +127,7 @@ class TimeoutException : public IOException TimeoutException():IOException("Timeout"){} TimeoutException(const TimeoutException&) = default; TimeoutException& operator=(TimeoutException& rhs) = default; - virtual ~TimeoutException() override; + virtual ~TimeoutException() noexcept override; }; /** @@ -322,17 +324,32 @@ 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. + /** + * Who did a deviceLogin() to this dev? + * \param dev Device name. + * \return List of clients e.g. {'127.0.0.1', 'admin-workstation.local.domain'} + */ + virtual std::set deviceGetClients(const std::string& dev) = 0; + /* 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; + /** + * Lists all clients of all devices (which have at least one client) + * \return Map with device names vs. list of their connected clients + */ + virtual std::map> listDeviceClients(void) = 0; + /** * Retrieve the result of a tracking ID. * \param id Tracking ID. @@ -355,6 +372,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 +389,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,14 +397,20 @@ 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. - * Host name and ports must have already set (usefull for reconnection). + * Host name and ports must have already set (useful for reconnection). */ void connect(); + /** + * Enable or disable std::cerr tracing of internal Socket operations + * during connect() processing. Primarily for developer troubleshooting. + */ + void setDebugConnect(bool d); + /** * Test if the connection is active. * \return tru if the connection is active. @@ -397,13 +425,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 +442,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,8 +469,12 @@ 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; + virtual std::set deviceGetClients(const std::string& dev) override; + + virtual std::map> listDeviceClients(void) override; virtual TrackingResult getTrackingResult(const TrackingID& id) override; @@ -466,8 +498,8 @@ class TcpClient : public Client private: std::string _host; - int _port; - long _timeout; + uint16_t _port; + time_t _timeout; internal::Socket* _socket; }; @@ -608,10 +640,15 @@ class Device * Login current client's user for the device. */ void login(); + /** + * Who did a login() to this dev? + */ + std::set getClients(); /* FIXME: Protocol update needed to handle master/primary alias * 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 +910,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 +1056,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 +1079,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..45430b29cc 100644 --- a/clients/nutclientmem.cpp +++ b/clients/nutclientmem.cpp @@ -17,8 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "nutclientmem.h" -#include namespace nut { @@ -174,18 +174,37 @@ TrackingID MemClientStub::executeDeviceCommand(const std::string& dev, const std throw NutException("Not implemented"); } +std::map> MemClientStub::listDeviceClients(void) +{ + throw NutException("Not implemented"); +} + +std::set MemClientStub::deviceGetClients(const std::string& dev) +{ + NUT_UNUSED_VARIABLE(dev); + throw NutException("Not implemented"); +} + void MemClientStub::deviceLogin(const std::string& dev) { NUT_UNUSED_VARIABLE(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 +261,3 @@ NUTCLIENT_MEM_t nutclient_mem_create_client() } } /* extern "C" */ - - - diff --git a/clients/nutclientmem.h b/clients/nutclientmem.h index 6788a2bf60..6363262681 100644 --- a/clients/nutclientmem.h +++ b/clients/nutclientmem.h @@ -69,9 +69,14 @@ 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; + virtual std::set deviceGetClients(const std::string& dev) override; + virtual std::map> listDeviceClients(void) override; virtual TrackingResult getTrackingResult(const TrackingID& id) override; diff --git a/clients/upsc.c b/clients/upsc.c index 01959ae646..71715f4731 100644 --- a/clients/upsc.c +++ b/clients/upsc.c @@ -21,10 +21,13 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include +#endif +#include "nut_stdint.h" #include "upsclient.h" static char *upsname = NULL, *hostname = NULL; @@ -53,6 +56,12 @@ static void usage(const char *prog) printf("\nThird form (lists clients connected to a device):\n"); printf(" -c - lists each client connected on , one per line.\n"); printf(" - upsd server, [@[:]] form\n"); + + printf("\nCommon arguments:\n"); + printf(" -V - display the version of this software\n"); + printf(" -h - display this help text\n"); + + nut_report_config_flags(); } static void printvar(const char *var) @@ -86,7 +95,7 @@ static void printvar(const char *var) } if (numa < numq) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least %zu)", numa, numq); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least %" PRIuSIZE ")", numa, numq); } printf("%s\n", answer[3]); @@ -119,7 +128,7 @@ static void list_vars(void) /* VAR */ if (numa < 4) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 4)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 4)", numa); } printf("%s: %s\n", answer[2], answer[3]); @@ -151,7 +160,7 @@ static void list_upses(int verbose) /* UPS */ if (numa < 3) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 3)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 3)", numa); } if(verbose) { @@ -188,7 +197,7 @@ static void list_clients(const char *devname) /* CLIENT
*/ if (numa < 3) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 3)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 3)", numa); } printf("%s\n", answer[2]); @@ -208,7 +217,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]); @@ -228,7 +238,9 @@ int main(int argc, char **argv) break; case 'V': - fatalx(EXIT_SUCCESS, "Network UPS Tools upscmd %s", UPS_VERSION); + nut_report_config_flags(); + + fatalx(EXIT_SUCCESS, "Network UPS Tools upsc %s", UPS_VERSION); #ifndef HAVE___ATTRIBUTE__NORETURN exit(EXIT_SUCCESS); /* Should not get here in practice, but compiler is afraid we can fall through */ #endif diff --git a/clients/upsclient.c b/clients/upsclient.c index afe815ec4c..74290cd26f 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -22,26 +22,42 @@ #include "config.h" /* safe because it doesn't contain prototypes */ #include "nut_platform.h" -#ifdef HAVE_PTHREAD +#ifndef WIN32 +# ifdef HAVE_PTHREAD /* this include is needed on AIX to have errno stored in thread local storage */ -#include +# include +# endif #endif #include -#include #include #include #include #include -#include -#include -#include -#include -#include "upsclient.h" +#ifndef WIN32 +# include +# include +# include +# include +# include +# define SOCK_OPT_CAST +#else /* => WIN32 */ +# define SOCK_OPT_CAST (char *) +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +# include +# include +/* This override network system calls to adapt to Windows specificity */ +# define W32_NETWORK_CALL_OVERRIDE +# include "wincompat.h" +# undef W32_NETWORK_CALL_OVERRIDE +#endif + #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) @@ -168,7 +184,7 @@ static int ssl_error(SSL *ssl, ssize_t ret) int e; if (ret >= INT_MAX) { - upslogx(LOG_ERR, "ssl_error() ret=%zd would not fit in an int", ret); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " would not fit in an int", ret); return -1; } e = SSL_get_error(ssl, (int)ret); @@ -176,23 +192,23 @@ static int ssl_error(SSL *ssl, ssize_t ret) switch (e) { case SSL_ERROR_WANT_READ: - upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_WANT_READ", ret); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_WANT_READ", ret); break; case SSL_ERROR_WANT_WRITE: - upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_WANT_WRITE", ret); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_WANT_WRITE", ret); break; case SSL_ERROR_SYSCALL: if (ret == 0 && ERR_peek_error() == 0) { upslogx(LOG_ERR, "ssl_error() EOF from client"); } else { - upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR_SYSCALL", ret); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_SYSCALL", ret); } break; default: - upslogx(LOG_ERR, "ssl_error() ret=%zd SSL_ERROR %d", ret, e); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR %d", ret, e); ssl_debug(); } @@ -313,6 +329,7 @@ static void HandshakeCallback(PRFileDesc *fd, UPSCONN_t *client_data) int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd) { + const char *quiet_init_ssl; #ifdef WITH_OPENSSL long ret; int ssl_mode = SSL_VERIFY_NONE; @@ -327,6 +344,18 @@ int upscli_init(int certverify, const char *certpath, NUT_UNUSED_VARIABLE(certpasswd); #endif /* WITH_OPENSSL | WITH_NSS */ + quiet_init_ssl = getenv("NUT_QUIET_INIT_SSL"); + if (quiet_init_ssl != NULL) { + if (*quiet_init_ssl == '\0' + || (strncmp(quiet_init_ssl, "true", 4) + && strncmp(quiet_init_ssl, "TRUE", 4) + && strncmp(quiet_init_ssl, "1", 1) ) + ) { + upsdebugx(1, "NUT_QUIET_INIT_SSL='%s' value was not recognized, ignored", quiet_init_ssl); + quiet_init_ssl = NULL; + } + } + if (upscli_initialized == 1) { upslogx(LOG_WARNING, "upscli already initialized"); return -1; @@ -389,10 +418,18 @@ int upscli_init(int certverify, const char *certpath, PK11_SetPasswordFunc(nss_password_callback); if (certpath) { - upslogx(LOG_INFO, "Init SSL with cerificate database located at %s", certpath); + if (quiet_init_ssl != NULL) { + upsdebugx(1, "Init SSL with certificate database located at %s", certpath); + } else { + upslogx(LOG_INFO, "Init SSL with certificate database located at %s", certpath); + } status = NSS_Init(certpath); } else { - upslogx(LOG_NOTICE, "Init SSL without certificate database"); + if (quiet_init_ssl != NULL) { + upsdebugx(1, "Init SSL without certificate database"); + } else { + upslogx(LOG_NOTICE, "Init SSL without certificate database"); + } status = NSS_NoDB_Init(NULL); } if (status != SECSuccess) { @@ -630,7 +667,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; @@ -642,8 +679,9 @@ static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const long tim * 32-bit builds)... Not likely to exceed in 64-bit builds, * but smaller systems with 16-bits might be endangered :) */ + int iret; assert(buflen <= INT_MAX); - int iret = SSL_read(ups->ssl, buf, (int)buflen); + iret = SSL_read(ups->ssl, buf, (int)buflen); assert(iret <= SSIZE_MAX); ret = (ssize_t)iret; #elif defined(WITH_NSS) /* WITH_OPENSSL */ @@ -714,7 +752,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; @@ -726,8 +764,9 @@ static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const l * 32-bit builds)... Not likely to exceed in 64-bit builds, * but smaller systems with 16-bits might be endangered :) */ + int iret; assert(buflen <= INT_MAX); - int iret = SSL_write(ups->ssl, buf, (int)buflen); + iret = SSL_write(ups->ssl, buf, (int)buflen); assert(iret <= SSIZE_MAX); ret = (ssize_t)iret; #elif defined(WITH_NSS) /* WITH_OPENSSL */ @@ -870,6 +909,10 @@ static int upscli_sslinit(UPSCONN_t *ups, int verifycert) return -1; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif if (verifycert) { status = SSL_AuthCertificateHook(ups->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB()); @@ -899,6 +942,9 @@ static int upscli_sslinit(UPSCONN_t *ups, int verifycert) nss_error("upscli_sslinit / SSL_HandshakeCallback"); return -1; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif cert = upscli_find_host_cert(ups->host); if (cert != NULL && cert->certname != NULL) { @@ -948,7 +994,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; @@ -958,8 +1004,16 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru fd_set wfds; int error; socklen_t error_size; + +#ifndef WIN32 long fd_flags; +#else + HANDLE event = NULL; + unsigned long argp; + WSADATA WSAdata; + WSAStartup(2,&WSAdata); +#endif if (!ups) { return -1; } @@ -974,7 +1028,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), "%" PRIuMAX, (uintmax_t)port); memset(&hints, 0, sizeof(hints)); @@ -1018,7 +1072,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru { case EAFNOSUPPORT: case EINVAL: - break; + break; default: ups->upserror = UPSCLI_ERR_SOCKFAILURE; ups->syserrno = errno; @@ -1028,21 +1082,36 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru /* non blocking connect */ if(timeout != NULL) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags |= O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); +#else + event = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( sock_fd, event, FD_CONNECT ); + CloseHandle(event); +#endif } while ((v = connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0) { +#ifndef WIN32 if(errno == EINPROGRESS || SOLARIS_i386_NBCONNECT_ENOENT(errno) || AIX_NBCONNECT_0(errno)) { +#else + if(errno == WSAEWOULDBLOCK) { +#endif FD_ZERO(&wfds); FD_SET(sock_fd, &wfds); select(sock_fd+1,NULL,&wfds,NULL, timeout); if (FD_ISSET(sock_fd, &wfds)) { error_size = sizeof(error); - getsockopt(sock_fd,SOL_SOCKET,SO_ERROR, - &error,&error_size); + getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, + SOCK_OPT_CAST &error, &error_size); if( error == 0) { /* connect successful */ v = 0; @@ -1077,10 +1146,15 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru } /* switch back to blocking operation */ - if(timeout != NULL) { + if (timeout != NULL) { +#ifndef WIN32 fd_flags = fcntl(sock_fd, F_GETFL); fd_flags &= ~O_NONBLOCK; fcntl(sock_fd, F_SETFL, fd_flags); +#else + argp = 0; + ioctlsocket(sock_fd, FIONBIO, &argp); +#endif } ups->fd = sock_fd; @@ -1097,7 +1171,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru pconf_init(&ups->pc_ctx, NULL); - ups->host = strdup(host); + ups->host = xstrdup(host); if (!ups->host) { ups->upserror = UPSCLI_ERR_NOMEM; @@ -1150,7 +1224,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 +1515,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 +1553,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 +1612,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; @@ -1554,15 +1628,32 @@ int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port s = strchr(tmp, '@'); - if ((*upsname = strdup(strtok_r(tmp, "@", &last))) == NULL) { - fprintf(stderr, "upscli_splitname: strdup failed\n"); + /* someone passed a "@hostname" string? */ + if (s == tmp) { + fprintf(stderr, "upscli_splitname: got empty upsname string\n"); + return -1; + } + + if ((*upsname = xstrdup(strtok_r(tmp, "@", &last))) == NULL) { + fprintf(stderr, "upscli_splitname: xstrdup failed\n"); + return -1; + } + + /* someone passed a "@hostname" string (take two)? */ + if (!**upsname) { + fprintf(stderr, "upscli_splitname: got empty upsname string\n"); return -1; } + /* + fprintf(stderr, "upscli_splitname3: got buf='%s', tmp='%s', upsname='%s', possible hostname:port='%s'\n", + NUT_STRARG(buf), NUT_STRARG(tmp), NUT_STRARG(*upsname), NUT_STRARG((s ? s+1 : s))); + */ + /* only a upsname is specified, fill in defaults */ if (s == NULL) { - if ((*hostname = strdup("localhost")) == NULL) { - fprintf(stderr, "upscli_splitname: strdup failed\n"); + if ((*hostname = xstrdup("localhost")) == NULL) { + fprintf(stderr, "upscli_splitname: xstrdup failed\n"); return -1; } @@ -1570,13 +1661,20 @@ int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port return 0; } + /* someone passed a "upsname@" string? */ + if (!(*(s+1))) { + fprintf(stderr, "upscli_splitname: got the @ separator and then an empty hostname[:port] string\n"); + return -1; + } + return upscli_splitaddr(s+1, hostname, 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)) { @@ -1594,8 +1692,8 @@ int upscli_splitaddr(const char *buf, char **hostname, int *port) return -1; } - if ((*hostname = strdup(strtok_r(tmp+1, "]", &last))) == NULL) { - fprintf(stderr, "upscli_splitaddr: strdup failed\n"); + if ((*hostname = xstrdup(strtok_r(tmp+1, "]", &last))) == NULL) { + fprintf(stderr, "upscli_splitaddr: xstrdup failed\n"); return -1; } @@ -1607,8 +1705,8 @@ int upscli_splitaddr(const char *buf, char **hostname, int *port) } else { s = strchr(tmp, ':'); - if ((*hostname = strdup(strtok_r(tmp, ":", &last))) == NULL) { - fprintf(stderr, "upscli_splitaddr: strdup failed\n"); + if ((*hostname = xstrdup(strtok_r(tmp, ":", &last))) == NULL) { + fprintf(stderr, "upscli_splitaddr: xstrdup failed\n"); return -1; } @@ -1619,12 +1717,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..c79be96cfd 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -21,13 +21,38 @@ #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 + +/* Not including NUT timehead.h because this is part of end-user API */ +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { @@ -41,7 +66,7 @@ extern "C" { typedef struct { char *host; - int port; + uint16_t port; int fd; int flags; int upserror; @@ -71,8 +96,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 +111,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); @@ -109,6 +134,8 @@ int upscli_ssl(UPSCONN_t *ups); /* upsclient error list */ +#define UPSCLI_ERR_NONE -1 /* No known error (internally used in tools like upsmon, not set by upsclient.c) */ + #define UPSCLI_ERR_UNKNOWN 0 /* Unknown error */ #define UPSCLI_ERR_VARNOTSUPP 1 /* Variable not supported by UPS */ #define UPSCLI_ERR_NOSUCHHOST 2 /* No such host */ diff --git a/clients/upscmd.c b/clients/upscmd.c index 7134671103..4d71ebce2f 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -22,11 +22,15 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include #include #include +#else +#include "wincompat.h" +#endif #include "nut_stdint.h" #include "upsclient.h" @@ -50,6 +54,7 @@ static void usage(const char *prog) printf("Administration program to initiate instant commands on UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); + printf(" -V display the version of this software\n"); printf(" -l show available commands on UPS \n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); @@ -60,6 +65,8 @@ static void usage(const char *prog) printf(" UPS identifier - [@[:]]\n"); printf(" Valid instant command - test.panel.start, etc.\n"); printf(" [] Additional data for command - number of seconds, etc.\n"); + + nut_report_config_flags(); } static void print_cmd(char *cmdname) @@ -113,7 +120,7 @@ static void listcmds(void) /* CMD */ if (numa < 3) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 3)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 3)", numa); } /* we must first read the entire list of commands, @@ -197,9 +204,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 +277,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]; @@ -303,6 +312,8 @@ int main(int argc, char **argv) break; case 'V': + nut_report_config_flags(); + fatalx(EXIT_SUCCESS, "Network UPS Tools upscmd %s", UPS_VERSION); #ifndef HAVE___ATTRIBUTE__NORETURN exit(EXIT_SUCCESS); /* Should not get here in practice, but compiler is afraid we can fall through */ 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..502f6299e9 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -37,18 +37,38 @@ #include "config.h" #include "timehead.h" +#include "nut_stdint.h" #include "upslog.h" - static int port, reopen_flag = 0, exit_flag = 0; - static char *upsname, *hostname; - static UPSCONN_t ups; +#ifdef WIN32 +#include "wincompat.h" +#endif + + static int reopen_flag = 0, exit_flag = 0; + static char *upsname; + static UPSCONN_t *ups; - static FILE *logfile; - static const char *logfn, *monhost; + static char *logfn, *monhost; +#ifndef WIN32 static sigset_t nut_upslog_sigmask; +#endif static char logbuffer[LARGEBUF], *logformat; static flist_t *fhead = NULL; + struct monhost_ups { + char *monhost; + char *logfn; + char *upsname; + char *hostname; + uint16_t port; + UPSCONN_t *ups; + FILE *logfile; + struct monhost_ups *next; + }; + static struct monhost_ups *monhost_ups_anchor = NULL; + static struct monhost_ups *monhost_ups_current = NULL; + static struct monhost_ups *monhost_ups_prev = NULL; + #define DEFAULT_LOGFORMAT "%TIME @Y@m@d @H@M@S% %VAR battery.charge% " \ "%VAR input.voltage% %VAR ups.load% [%VAR ups.status%] " \ @@ -56,17 +76,23 @@ static void reopen_log(void) { - if (logfile == stdout) { - upslogx(LOG_INFO, "logging to stdout"); - return; - } + for (monhost_ups_current = monhost_ups_anchor; + monhost_ups_current != NULL; + monhost_ups_current = monhost_ups_current->next) { + if (monhost_ups_current->logfile == stdout) { + upslogx(LOG_INFO, "logging to stdout"); + return; + } - fclose(logfile); - logfile = fopen(logfn, "a"); - if (logfile == NULL) - fatal_with_errno(EXIT_FAILURE, "could not reopen logfile %s", logfn); + if ((monhost_ups_current->logfile = freopen( + monhost_ups_current->logfn, "a", + monhost_ups_current->logfile)) == NULL) + fatal_with_errno(EXIT_FAILURE, + "could not reopen logfile %s", logfn); + } } +#ifndef WIN32 static void set_reopen_flag(int sig) { reopen_flag = sig; @@ -83,10 +109,12 @@ static void set_print_now_flag(int sig) /* no need to do anything, the signal will cause sleep to be interrupted */ } +#endif /* handlers: reload on HUP, exit on INT/QUIT/TERM */ static void setup_signals(void) { +#ifndef WIN32 struct sigaction sa; sigemptyset(&nut_upslog_sigmask); @@ -108,6 +136,7 @@ static void setup_signals(void) sa.sa_handler = set_print_now_flag; if (sigaction(SIGUSR1, &sa, NULL) < 0) fatal_with_errno(EXIT_FAILURE, "Can't install SIGUSR1 handler"); +#endif } static void help(const char *prog) @@ -123,11 +152,17 @@ 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"); + printf(" -m - Monitor UPS \n"); + printf(" - Example: -m myups@server,/var/log/myups.log\n"); printf(" -u - Switch to if started as root\n"); + printf(" -V - Display the version of this software\n"); + printf(" -h - Display this help text\n"); printf("\n"); printf("Some valid format string escapes:\n"); @@ -143,6 +178,8 @@ static void help(const char *prog) printf("\n"); printf("See the upslog(8) man page for more information.\n"); + nut_report_config_flags(); + exit(EXIT_SUCCESS); } @@ -211,7 +248,7 @@ static void getvar(const char *var) query[2] = var; numq = 3; - ret = upscli_get(&ups, numq, query, &numa, &answer); + ret = upscli_get(ups, numq, query, &numa, &answer); if ((ret < 0) || (numa < numq)) { snprintfcat(logbuffer, sizeof(logbuffer), "NA"); @@ -364,7 +401,7 @@ static void compile_format(void) } /* go through the list of functions and call them in order */ -static void run_flist(void) +static void run_flist(struct monhost_ups *monhost_ups_print) { flist_t *tmp; @@ -378,8 +415,8 @@ static void run_flist(void) tmp = tmp->next; } - fprintf(logfile, "%s\n", logbuffer); - fflush(logfile); + fprintf(monhost_ups_print->logfile, "%s\n", logbuffer); + fflush(monhost_ups_print->logfile); } /* -s @@ -391,7 +428,8 @@ static void run_flist(void) int main(int argc, char **argv) { - int interval = 30, i; + int interval = 30, i, foreground = -1; + size_t monhost_len = 0; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; @@ -403,7 +441,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:FBm:")) != -1) { switch(i) { case 'h': help(prog); @@ -411,12 +449,47 @@ int main(int argc, char **argv) break; #endif + case 'm': { /* var scope */ + char *m_arg, *s; + + monhost_ups_prev = monhost_ups_current; + monhost_ups_current = xmalloc(sizeof(struct monhost_ups)); + if (monhost_ups_anchor == NULL) + monhost_ups_anchor = monhost_ups_current; + else + monhost_ups_prev->next = monhost_ups_current; + monhost_ups_current->next = NULL; + monhost_len++; + + /* Be sure to not mangle original optarg, nor rely on its longevity */ + s = xstrdup(optarg); + m_arg = s; + monhost_ups_current->monhost = xstrdup(strsep(&m_arg, ",")); + if (!m_arg) + fatalx(EXIT_FAILURE, "Argument '-m upsspec,logfile' requires exactly 2 components in the tuple"); +#ifndef WIN32 + monhost_ups_current->logfn = xstrdup(strsep(&m_arg, ",")); +#else + monhost_ups_current->logfn = xstrdup(filter_path(strsep(&m_arg, ","))); +#endif + if (m_arg) /* Had a third comma - also unexpected! */ + fatalx(EXIT_FAILURE, "Argument '-m upsspec,logfile' requires exactly 2 components in the tuple"); + if (upscli_splitname(monhost_ups_current->monhost, &(monhost_ups_current->upsname), &(monhost_ups_current->hostname), &(monhost_ups_current->port)) != 0) { + fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); + } + free(s); + } /* var scope */ + break; case 's': monhost = optarg; break; case 'l': +#ifndef WIN32 logfn = optarg; +#else + logfn = filter_path(optarg); +#endif break; case 'i': @@ -432,11 +505,20 @@ int main(int argc, char **argv) break; case 'V': + nut_report_config_flags(); exit(EXIT_SUCCESS); case 'p': pidfilebase = optarg; break; + + case 'F': + foreground = 1; + break; + + case 'B': + foreground = 0; + break; } } @@ -453,7 +535,11 @@ int main(int argc, char **argv) if (argc >= 3) { monhost = argv[0]; +#ifndef WIN32 logfn = argv[1]; +#else + logfn = filter_path(argv[1]); +#endif interval = atoi(argv[2]); } @@ -467,42 +553,71 @@ int main(int argc, char **argv) snprintfcat(logformat, LARGEBUF, "%s ", argv[i]); } - if (!monhost) - fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s "); + if (monhost_ups_anchor == NULL) { + if (monhost) { + monhost_ups_current = xmalloc(sizeof(struct monhost_ups)); + monhost_ups_anchor = monhost_ups_current; + monhost_ups_current->next = NULL; + monhost_ups_current->monhost = monhost; + monhost_len = 1; + } else { + fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s or -m "); + } - if (!logfn) - fatalx(EXIT_FAILURE, "No filename defined for logging - use -l "); + if (logfn) + monhost_ups_current->logfn = logfn; + else + fatalx(EXIT_FAILURE, "No filename defined for logging - use -l "); + } /* shouldn't happen */ if (!logformat) fatalx(EXIT_FAILURE, "No format defined - but this should be impossible"); - printf("logging status of %s to %s (%is intervals)\n", - monhost, logfn, interval); + /* shouldn't happen */ + if (!monhost_len) + fatalx(EXIT_FAILURE, "No UPS defined for monitoring - use -s or -m "); + + for (monhost_ups_current = monhost_ups_anchor; + monhost_ups_current != NULL; + monhost_ups_current = monhost_ups_current->next) { + printf("logging status of %s to %s (%is intervals)\n", + monhost_ups_current->monhost, monhost_ups_current->logfn, interval); + if (upscli_splitname(monhost_ups_current->monhost, &(monhost_ups_current->upsname), &(monhost_ups_current->hostname), &(monhost_ups_current->port)) != 0) { + fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); + } - if (upscli_splitname(monhost, &upsname, &hostname, &port) != 0) { - fatalx(EXIT_FAILURE, "Error: invalid UPS definition. Required format: upsname[@hostname[:port]]\n"); - } + monhost_ups_current->ups = xmalloc(sizeof(UPSCONN_t)); + if (upscli_connect(monhost_ups_current->ups, monhost_ups_current->hostname, monhost_ups_current->port, UPSCLI_CONN_TRYSSL) < 0) + fprintf(stderr, "Warning: initial connect failed: %s\n", + upscli_strerror(monhost_ups_current->ups)); - if (upscli_connect(&ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) - fprintf(stderr, "Warning: initial connect failed: %s\n", - upscli_strerror(&ups)); + if (strcmp(monhost_ups_current->logfn, "-") == 0) + monhost_ups_current->logfile = stdout; + else + monhost_ups_current->logfile = fopen(monhost_ups_current->logfn, "a"); - if (strcmp(logfn, "-") == 0) - logfile = stdout; - else - logfile = fopen(logfn, "a"); + if (monhost_ups_current->logfile == NULL) + fatal_with_errno(EXIT_FAILURE, "could not open logfile %s", logfn); - if (logfile == NULL) - fatal_with_errno(EXIT_FAILURE, "could not open logfile %s", logfn); + } /* now drop root if we have it */ new_uid = get_user_pwent(user); open_syslog(prog); - if (logfile != stdout) + if (foreground < 0) { + if (monhost_ups_anchor->logfile == stdout) { + foreground = 1; + } else { + foreground = 0; + } + } + + if (!foreground) { background(); + } setup_signals(); @@ -512,7 +627,11 @@ int main(int argc, char **argv) compile_format(); + upsnotify(NOTIFY_STATE_READY_WITH_PID, NULL); + while (exit_flag == 0) { + upsnotify(NOTIFY_STATE_WATCHDOG, NULL); + time(&now); if (nextpoll > now) { @@ -525,31 +644,45 @@ int main(int argc, char **argv) } if (reopen_flag) { + upsnotify(NOTIFY_STATE_RELOADING, NULL); upslogx(LOG_INFO, "Signal %d: reopening log file", reopen_flag); reopen_log(); reopen_flag = 0; + upsnotify(NOTIFY_STATE_READY, NULL); } - /* reconnect if necessary */ - if (upscli_fd(&ups) < 0) { - upscli_connect(&ups, hostname, port, 0); - } + for (monhost_ups_current = monhost_ups_anchor; + monhost_ups_current != NULL; + monhost_ups_current = monhost_ups_current->next) { + ups = monhost_ups_current->ups; /* XXX Not ideal */ + upsname = monhost_ups_current->upsname; /* XXX Not ideal */ + /* reconnect if necessary */ + if (upscli_fd(ups) < 0) { + upscli_connect(ups, monhost_ups_current->hostname, monhost_ups_current->port, 0); + } - run_flist(); + run_flist(monhost_ups_current); - /* don't keep connection open if we don't intend to use it shortly */ - if (interval > 30) { - upscli_disconnect(&ups); + /* don't keep connection open if we don't intend to use it shortly */ + if (interval > 30) { + upscli_disconnect(ups); + } } } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); + upsnotify(NOTIFY_STATE_STOPPING, "Signal %d: exiting", exit_flag); - if (logfile != stdout) - fclose(logfile); + for (monhost_ups_current = monhost_ups_anchor; + monhost_ups_current != NULL; + monhost_ups_current = monhost_ups_current->next) { - upscli_disconnect(&ups); + if (monhost_ups_current->logfile != stdout) + fclose(monhost_ups_current->logfile); + + upscli_disconnect(monhost_ups_current->ups); + } exit(EXIT_SUCCESS); } diff --git a/clients/upsmon.c b/clients/upsmon.c index 37c908091f..1b4af7cfb4 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3,6 +3,7 @@ Copyright (C) 1998 Russell Kroll 2012 Arnaud Quette + 2020-2023 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 @@ -22,10 +23,14 @@ #include "common.h" #include +#ifndef WIN32 #include #include #include #include +#else +#include +#endif #include "nut_stdint.h" #include "upsclient.h" @@ -51,6 +56,36 @@ static int deadtime = 15; /* default polling interval = 5 sec */ static unsigned int pollfreq = 5, pollfreqalert = 5; + /* If pollfail_log_throttle_max > 0, error messages for same + * state of an UPS (e.g. "Data stale" or "Driver not connected") + * will only be repeated every so many POLLFREQ loops. + * If pollfail_log_throttle_max == 0, such error messages will + * only be reported once when that situation starts, and ends. + * By default (or for negative values) it is logged every pollfreq + * loop cycle (which can abuse syslog and its storage), same as + * if "max = 1". + * To support this, each utype_t (UPS) structure tracks individual + * pollfail_log_throttle_count and pollfail_log_throttle_state + */ +static int pollfail_log_throttle_max = -1; + + /* We support "administrative OFF" for power devices which can + * be managed to turn off their load while the UPS or ePDU remains + * accessible for monitoring and management. This toggle allows + * to delay propagation of such state into a known loss of a feed + * (possibly triggering FSD on MONITOR'ing clients that are in fact + * still alive - e.g. with multiple power sources), because when + * some devices begin battery calibration, they report "OFF" for + * a few seconds and only then they report "CAL" after switching + * all the power relays (causing false-positives for FSD trigger). + * A negative value means to disable decreasing the power-source + * counter in such cases, and a zero makes the effect immediate. + * NOTE: so far we support the device reporting an "OFF" state + * which usually means completely un-powering the load; TODO was + * logged for adding similar support for just some outlets/groups. + */ +static int offdurationtime = 30; + /* secondary hosts are given 15 sec by default to logout from upsd */ static int hostsync = 15; @@ -79,15 +114,43 @@ static char *certpasswd = NULL; static int certverify = 0; /* don't verify by default */ static int forcessl = 0; /* don't require ssl by default */ -static int userfsd = 0, use_pipe = 1, pipefd[2]; +static int shutdownexitdelay = 0; /* by default doshutdown() exits immediately */ +static int userfsd = 0, pipefd[2]; + /* Should we run "all in one" (e.g. as root) or split + * into two upsmon processes for some more security? */ +#ifndef WIN32 +static int use_pipe = 1; +#else + /* Do not fork in WIN32 */ +static int use_pipe = 0; +static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif static utype_t *firstups = NULL; static int opt_af = AF_UNSPEC; +#ifndef WIN32 /* signal handling things */ static struct sigaction sa; static sigset_t nut_upsmon_sigmask; +#endif + +#ifdef HAVE_SYSTEMD +# define SERVICE_UNIT_NAME "nut-monitor.service" +#endif + +/* 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; +/* Debug level specified via command line - we revert to + * it when reloading if there was no DEBUG_MIN in ups.conf + */ +static int nut_debug_level_args = 0; static void setflag(int *val, int flag) { @@ -104,8 +167,19 @@ static int flag_isset(int num, int flag) return ((num & flag) == flag); } +static int try_restore_pollfreq(utype_t *ups) { + /* Use relaxed pollfreq if we are not in a hardware + * power state that is prone to UPS disappearance */ + if (!flag_isset(ups->status, ST_ONBATT | ST_OFF | ST_BYPASS | ST_CAL)) { + sleepval = pollfreq; + return 1; + } + return 0; +} + static void wall(const char *text) { +#ifndef WIN32 FILE *wf; wf = popen("wall", "w"); @@ -117,20 +191,98 @@ static void wall(const char *text) fprintf(wf, "%s\n", text); pclose(wf); +#else + #define MESSAGE_CMD "message.exe" + char * command; + + /* first +1 is for the space between message and text + second +1 is for trailing 0 + +2 is for "" */ + command = malloc (strlen(MESSAGE_CMD) + 1 + 2 + strlen(text) + 1); + if( command == NULL ) { + upslog_with_errno(LOG_NOTICE, "Not enough memory for wall"); + return; + } + + sprintf(command,"%s \"%s\"",MESSAGE_CMD,text); + if ( system(command) != 0 ) { + upslog_with_errno(LOG_NOTICE, "Can't invoke wall"); + } + free(command); +#endif +} + +#ifdef WIN32 +typedef struct async_notify_s { + char *notice; + int flags; + char *ntype; + char *upsname; + char *date; +} async_notify_t; + +static unsigned __stdcall async_notify(LPVOID param) +{ + char exec[LARGEBUF]; + char notice[LARGEBUF]; + + /* the following code is a copy of the content of the NOT WIN32 part of + "notify" function below */ + + async_notify_t *data = (async_notify_t *)param; + + if (flag_isset(data->flags, NOTIFY_WALL)) { + snprintf(notice,LARGEBUF,"%s: %s", data->date, data->notice); + wall(notice); + } + + if (flag_isset(data->flags, NOTIFY_EXEC)) { + if (notifycmd != NULL) { + snprintf(exec, sizeof(exec), "%s \"%s\"", notifycmd, data->notice); + + if (data->upsname) + setenv("UPSNAME", data->upsname, 1); + else + setenv("UPSNAME", "", 1); + + setenv("NOTIFYTYPE", data->ntype, 1); + if (system(exec) == -1) { + upslog_with_errno(LOG_ERR, "%s", __func__); + } + } + } + + free(data->notice); + free(data->ntype); + free(data->upsname); + free(data->date); + free(data); + return 1; } +#endif static void notify(const char *notice, int flags, const char *ntype, const char *upsname) { +#ifndef WIN32 char exec[LARGEBUF]; int ret; +#endif + + upsdebugx(6, "%s: sending notification for [%s]: type %s with flags 0x%04x: %s", + __func__, upsname, ntype, flags, notice); - if (flag_isset(flags, NOTIFY_IGNORE)) + if (flag_isset(flags, NOTIFY_IGNORE)) { + upsdebugx(6, "%s: NOTIFY_IGNORE", __func__); return; + } - if (flag_isset(flags, NOTIFY_SYSLOG)) + if (flag_isset(flags, NOTIFY_SYSLOG)) { + upsdebugx(6, "%s: NOTIFY_SYSLOG (as LOG_NOTICE)", __func__); upslogx(LOG_NOTICE, "%s", notice); + } +#ifndef WIN32 /* fork here so upsmon doesn't get wedged if the notifier is slow */ ret = fork(); @@ -139,16 +291,24 @@ static void notify(const char *notice, int flags, const char *ntype, return; } - if (ret != 0) /* parent */ + if (ret != 0) { /* parent */ + upsdebugx(6, "%s (parent): forked a child to notify via subprocesses", __func__); return; + } /* child continues and does all the work */ + upsdebugx(6, "%s (child): forked to notify via subprocesses", __func__); - if (flag_isset(flags, NOTIFY_WALL)) + if (flag_isset(flags, NOTIFY_WALL)) { + upsdebugx(6, "%s (child): NOTIFY_WALL", __func__); wall(notice); + } if (flag_isset(flags, NOTIFY_EXEC)) { if (notifycmd != NULL) { + upsdebugx(6, "%s (child): NOTIFY_EXEC: calling NOTIFYCMD as '%s \"%s\"'", + __func__, notifycmd, notice); + snprintf(exec, sizeof(exec), "%s \"%s\"", notifycmd, notice); if (upsname) @@ -160,10 +320,33 @@ static void notify(const char *notice, int flags, const char *ntype, if (system(exec) == -1) { upslog_with_errno(LOG_ERR, "%s", __func__); } + } else { + upsdebugx(6, "%s (child): NOTIFY_EXEC: no NOTIFYCMD was configured", __func__); } } exit(EXIT_SUCCESS); +#else + async_notify_t * data; + time_t t; + + data = malloc(sizeof(async_notify_t)); + data->notice = strdup(notice); + data->flags = flags; + data->ntype = strdup(ntype); + data->upsname = strdup(upsname); + t = time(NULL); + data->date = strdup(ctime(&t)); + + _beginthreadex( + NULL, /* security FIXME */ + 0, /* stack size */ + async_notify, + (void *)data, + 0, /* Creation flags */ + NULL /* thread id */ + ); +#endif } static void do_notify(const utype_t *ups, int ntype) @@ -209,9 +392,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 +412,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 +428,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); @@ -252,10 +470,8 @@ static int apply_for_primary(utype_t *ups) return 0; } -/* 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) - */ +/* authenticate to upsd, plus do LOGIN and apply for PRIMARY/MASTER privileges + * if applicable to this ups device MONITORing configuration */ static int do_upsd_auth(utype_t *ups) { char buf[SMALLBUF]; @@ -377,13 +593,100 @@ static void ups_is_gone(utype_t *ups) /* now only complain if we haven't lately */ if ((now - ups->lastncwarn) > nocommwarntime) { - /* NOCOMM indicates a persistent condition */ do_notify(ups, NOTIFY_NOCOMM); ups->lastncwarn = now; } } +static void ups_is_off(utype_t *ups) +{ + time_t now; + + time(&now); + + if (flag_isset(ups->status, ST_OFF)) { /* no change */ + upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + if (ups->offsince < 1) { + /* Should not happen, but just in case */ + ups->offsince = now; + } else { + if (offdurationtime > 0 && (now - ups->offsince) > offdurationtime) { + /* This should be a rare but urgent situation + * that warrants an extra notification? */ + upslogx(LOG_WARNING, "%s: %s is in state OFF for %d sec, " + "assuming the line is not fed " + "(if it is calibrating etc., check " + "the upsmon 'OFFDURATION' option)", + __func__, ups->sys, (int)(now - ups->offsince)); + ups->offstate = 1; + } + } + return; + } + + sleepval = pollfreqalert; /* bump up polling frequency */ + + ups->offsince = now; + if (offdurationtime == 0) { + /* This should be a rare but urgent situation + * that warrants an extra notification? */ + upslogx(LOG_WARNING, "%s: %s is in state OFF, assuming the line is not fed (if it is calibrating etc., check the upsmon 'OFFDURATION' option)", __func__, ups->sys); + ups->offstate = 1; + } else + if (offdurationtime < 0) { + upsdebugx(1, "%s: %s is in state OFF, but we are not assuming the line is not fed (due to upsmon 'OFFDURATION' option)", __func__, ups->sys); + } + + upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); + + /* must have changed from !OFF to OFF, so notify */ + do_notify(ups, NOTIFY_OFF); + setflag(&ups->status, ST_OFF); +} + +static void ups_is_notoff(utype_t *ups) +{ + /* Called when OFF is NOT among known states */ + ups->offsince = 0; + ups->offstate = 0; + if (flag_isset(ups->status, ST_OFF)) { /* actual change */ + do_notify(ups, NOTIFY_NOTOFF); + clearflag(&ups->status, ST_OFF); + try_restore_pollfreq(ups); + } +} + +static void ups_is_bypass(utype_t *ups) +{ + if (flag_isset(ups->status, ST_BYPASS)) { /* no change */ + upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); + return; + } + + sleepval = pollfreqalert; /* bump up polling frequency */ + + ups->bypassstate = 1; /* if we lose comms, consider it AWOL */ + + upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); + + /* must have changed from !BYPASS to BYPASS, so notify */ + + do_notify(ups, NOTIFY_BYPASS); + setflag(&ups->status, ST_BYPASS); +} + +static void ups_is_notbypass(utype_t *ups) +{ + /* Called when BYPASS is NOT among known states */ + ups->bypassstate = 0; + if (flag_isset(ups->status, ST_BYPASS)) { /* actual change */ + do_notify(ups, NOTIFY_NOTBYPASS); + clearflag(&ups->status, ST_BYPASS); + try_restore_pollfreq(ups); + } +} + static void ups_on_batt(utype_t *ups) { if (flag_isset(ups->status, ST_ONBATT)) { /* no change */ @@ -406,13 +709,13 @@ static void ups_on_batt(utype_t *ups) static void ups_on_line(utype_t *ups) { + try_restore_pollfreq(ups); + if (flag_isset(ups->status, ST_ONLINE)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); return; } - sleepval = pollfreq; - upsdebugx(3, "%s: %s (first time)", __func__, ups->sys); /* ignore the first OL at startup, otherwise send the notifier */ @@ -449,6 +752,8 @@ static void doshutdown(void) static void doshutdown(void) { + upsnotify(NOTIFY_STATE_STOPPING, "Executing automatic power-fail shutdown"); + /* this should probably go away at some point */ upslogx(LOG_CRIT, "Executing automatic power-fail shutdown"); wall("Executing automatic power-fail shutdown\n"); @@ -471,11 +776,39 @@ static void doshutdown(void) /* one process model = we do all the work here */ int sret; +#ifndef WIN32 if (geteuid() != 0) upslogx(LOG_WARNING, "Not root, shutdown may fail"); +#endif set_pdflag(); +#ifdef WIN32 + SC_HANDLE SCManager; + SC_HANDLE Service; + SERVICE_STATUS Status; + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServiceActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + } + else { + Service = OpenService(SCManager,SVCNAME,SERVICE_STOP); + if (Service == NULL) { + upslogx(LOG_ERR,"OpenService failed (%d)\n", (int)GetLastError()); + } + else { + ControlService(Service,SERVICE_CONTROL_STOP,&Status); + /* Give time to the service to stop */ + Sleep(2000); + } + } +#endif + sret = system(shutdowncmd); if (sret != 0) @@ -483,6 +816,28 @@ static void doshutdown(void) shutdowncmd); } + if (shutdownexitdelay == 0) { + upsdebugx(1, + "Exiting upsmon immediately " + "after initiating shutdown, by default"); + } else + if (shutdownexitdelay < 0) { + upslogx(LOG_WARNING, + "Configured to not exit upsmon " + "after initiating shutdown"); + /* Technically, here we sleep until SIGTERM or poweroff */ + do { + sleep(1); + } while (!exit_flag); + } else { + upslogx(LOG_WARNING, + "Configured to only exit upsmon %d sec " + "after initiating shutdown", shutdownexitdelay); + do { + sleep(1); + shutdownexitdelay--; + } while (!exit_flag && shutdownexitdelay); + } exit(EXIT_SUCCESS); } @@ -528,20 +883,24 @@ static void setfsd(utype_t *ups) static void set_alarm(void) { +#ifndef WIN32 alarm(NET_TIMEOUT); +#endif } static void clear_alarm(void) { -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wstrict-prototypes" -#endif +#ifndef WIN32 +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +# endif signal(SIGALRM, SIG_IGN); -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) -# pragma GCC diagnostic pop -#endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +# endif alarm(0); +#endif } static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize) @@ -598,7 +957,7 @@ static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize) if (numa < numq) { upslogx(LOG_ERR, "%s: Error: insufficient data " - "(got %zu args, need at least %zu)", + "(got %" PRIuSIZE " args, need at least %" PRIuSIZE ")", var, numa, numq); return -1; } @@ -703,9 +1062,56 @@ static int is_ups_critical(utype_t *ups) if (flag_isset(ups->status, ST_FSD)) return 1; + if (ups->commstate == 0) { + if (flag_isset(ups->status, ST_CAL)) { + upslogx(LOG_WARNING, + "UPS [%s] was last known to be calibrating " + "and currently is not communicating, assuming dead", + ups->sys); + return 1; + } + + if (ups->bypassstate == 1 + || flag_isset(ups->status, ST_BYPASS)) { + upslogx(LOG_WARNING, + "UPS [%s] was last known to be on BYPASS " + "and currently is not communicating, assuming dead", + ups->sys); + return 1; + } + + if (ups->offstate == 1 + || (offdurationtime >= 0 && flag_isset(ups->status, ST_OFF))) { + upslogx(LOG_WARNING, + "UPS [%s] was last known to be (administratively) OFF " + "and currently is not communicating, assuming dead", + ups->sys); + return 1; + } + + if (ups->linestate == 0) { + upslogx(LOG_WARNING, + "UPS [%s] was last known to be not fully online " + "and currently is not communicating, assuming dead", + ups->sys); + return 1; + } + } + + /* administratively OFF (long enough, see OFFDURATION) */ + if (flag_isset(ups->status, ST_OFF) && offdurationtime >= 0 + && ups->offstate == 1) { + upslogx(LOG_WARNING, + "UPS [%s] is reported as (administratively) OFF", + ups->sys); + upsdebugx(1, "UPS [%s] is now critical being OFF for too long. In case of persisting unwanted shutdowns, consider disabling the upsmon 'OFFDURATION' option.", ups->sys); + return 1; + } + /* not OB or not LB = not critical yet */ - if ((!flag_isset(ups->status, ST_ONBATT)) || - (!flag_isset(ups->status, ST_LOWBATT))) + if ((!flag_isset(ups->status, ST_ONBATT)) + || (!flag_isset(ups->status, ST_LOWBATT)) + ) return 0; /* must be OB+LB now */ @@ -735,8 +1141,9 @@ static int is_ups_critical(utype_t *ups) /* give the primary up to HOSTSYNC seconds before shutting down */ if ((now - ups->lastnoncrit) > hostsync) { - upslogx(LOG_WARNING, "Giving up on the primary for UPS [%s]", - ups->sys); + upslogx(LOG_WARNING, "Giving up on the primary for UPS [%s] " + "after %d sec since last comms", + ups->sys, (int)(now - ups->lastnoncrit)); return 1; } @@ -766,7 +1173,7 @@ static void recalc(void) * this means a UPS we've never heard from is assumed OL * * whether this is really the best thing to do is undecided */ - /* crit = (FSD) || (OB & LB) > HOSTSYNC seconds */ + /* crit = (FSD) || (OB & LB) > HOSTSYNC seconds || (OFF || BYPASS) && nocomms */ if (is_ups_critical(ups)) upsdebugx(1, "Critical UPS: %s", ups->sys); else @@ -809,7 +1216,7 @@ static void upsreplbatt(utype_t *ups) } } -static void ups_cal(utype_t *ups) +static void ups_is_cal(utype_t *ups) { if (flag_isset(ups->status, ST_CAL)) { /* no change */ upsdebugx(4, "%s: %s (no change)", __func__, ups->sys); @@ -824,6 +1231,16 @@ static void ups_cal(utype_t *ups) setflag(&ups->status, ST_CAL); } +static void ups_is_notcal(utype_t *ups) +{ + /* Called when CAL is NOT among known states */ + if (flag_isset(ups->status, ST_CAL)) { /* actual change */ + do_notify(ups, NOTIFY_NOTCAL); + clearflag(&ups->status, ST_CAL); + try_restore_pollfreq(ups); + } +} + static void ups_fsd(utype_t *ups) { if (flag_isset(ups->status, ST_FSD)) { /* no change */ @@ -842,12 +1259,28 @@ static void ups_fsd(utype_t *ups) /* cleanly close the connection to a given UPS */ static void drop_connection(utype_t *ups) { - upsdebugx(2, "Dropping connection to UPS [%s]", ups->sys); + if (ups->linestate == 1 && flag_isset(ups->status, ST_ONLINE)) + upsdebugx(2, "Dropping connection to UPS [%s], last seen as fully online.", ups->sys); + else + upsdebugx(2, "Dropping connection to UPS [%s], last seen as not fully online (might be considered critical later).", ups->sys); + + if(ups->offstate == 1 || flag_isset(ups->status, ST_OFF)) + upsdebugx(2, "Disconnected UPS [%s] was last seen in status OFF, this UPS might be considered critical later.", ups->sys); + + if(ups->bypassstate == 1 || flag_isset(ups->status, ST_BYPASS)) + upsdebugx(2, "Disconnected UPS [%s] was last seen in status BYPASS, this UPS might be considered critical later.", ups->sys); + + if(flag_isset(ups->status, ST_CAL)) + upsdebugx(2, "Disconnected UPS [%s] was last seen in status CAL, this UPS might be considered critical later.", ups->sys); ups->commstate = 0; - ups->linestate = 0; + + /* forget poll-failure logging throttling */ + ups->pollfail_log_throttle_count = -1; + ups->pollfail_log_throttle_state = UPSCLI_ERR_NONE; + clearflag(&ups->status, ST_LOGIN); - clearflag(&ups->status, ST_CONNECTED); + clearflag(&ups->status, ST_CLICONNECTED); upscli_disconnect(&ups->conn); } @@ -959,6 +1392,7 @@ static void addups(int reloading, const char *sys, const char *pvs, { unsigned int pv; utype_t *tmp, *last; + long lpv; /* the username is now required - no more host-based auth */ @@ -968,7 +1402,7 @@ static void addups(int reloading, const char *sys, const char *pvs, return; } - long lpv = strtol(pvs, (char **) NULL, 10); + lpv = strtol(pvs, (char **) NULL, 10); #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 @@ -1037,6 +1471,10 @@ static void addups(int reloading, const char *sys, const char *pvs, tmp->commstate = -1; tmp->linestate = -1; + /* forget poll-failure logging throttling */ + tmp->pollfail_log_throttle_count = -1; + tmp->pollfail_log_throttle_state = UPSCLI_ERR_NONE; + tmp->lastpoll = 0; tmp->lastnoncrit = 0; tmp->lastrbwarn = 0; @@ -1181,12 +1619,38 @@ static int parse_conf_arg(size_t numargs, char **arg) return 1; } + /* SHUTDOWNEXIT */ + if (!strcmp(arg[0], "SHUTDOWNEXIT")) { + if (!strcasecmp(arg[1], "on") + || !strcasecmp(arg[1], "yes") + || !strcasecmp(arg[1], "true")) { + shutdownexitdelay = 0; + } else + if (!strcasecmp(arg[1], "off") + || !strcasecmp(arg[1], "no") + || !strcasecmp(arg[1], "false")) { + shutdownexitdelay = -1; + } else { + if (!str_to_int(arg[1], &shutdownexitdelay, 10)) { + upslogx(LOG_WARNING, + "SHUTDOWNEXIT value not recognized, " + "defaulting to 'yes'"); + shutdownexitdelay = 0; + } + } + return 1; + } + /* POWERDOWNFLAG */ if (!strcmp(arg[0], "POWERDOWNFLAG")) { checkmode(arg[0], powerdownflag, arg[1], reload_flag); free(powerdownflag); +#ifndef WIN32 powerdownflag = xstrdup(arg[1]); +#else + powerdownflag = filter_path(arg[1]); +#endif if (!reload_flag) upslogx(LOG_INFO, "Using power down flag file %s", @@ -1224,6 +1688,23 @@ static int parse_conf_arg(size_t numargs, char **arg) return 1; } + /* POLLFAIL_LOG_THROTTLE_MAX */ + if (!strcmp(arg[0], "POLLFAIL_LOG_THROTTLE_MAX")) { + int ipollfail_log_throttle_max = atoi(arg[1]); + if (ipollfail_log_throttle_max < 0 || ipollfail_log_throttle_max == INT_MAX) { + upsdebugx(0, "Ignoring invalid POLLFAIL_LOG_THROTTLE_MAX value: %d", ipollfail_log_throttle_max); + } else { + pollfail_log_throttle_max = ipollfail_log_throttle_max; + } + return 1; + } + + /* OFFDURATION */ + if (!strcmp(arg[0], "OFFDURATION")) { + offdurationtime = atoi(arg[1]); + return 1; + } + /* HOSTSYNC */ if (!strcmp(arg[0], "HOSTSYNC")) { hostsync = atoi(arg[1]); @@ -1271,7 +1752,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 +1777,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; @@ -1359,6 +1852,7 @@ static void upsmon_err(const char *errmsg) static void loadconfig(void) { PCONF_CTX_t ctx; + int numerrors = 0; pconf_init(&ctx, upsmon_err); @@ -1373,10 +1867,39 @@ 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; + + if (pollfail_log_throttle_max >= 0) { + utype_t *ups; + + upslogx(LOG_INFO, + "Forgetting POLLFAIL_LOG_THROTTLE_MAX=%d and " + "resetting UPS error-state counters before " + "a configuration reload", + pollfail_log_throttle_max); + pollfail_log_throttle_max = -1; + + /* forget poll-failure logging throttling, so that we + * rediscover the error-states and the counts involved + */ + ups = firstups; + while (ups) { + ups->pollfail_log_throttle_count = -1; + ups->pollfail_log_throttle_state = UPSCLI_ERR_NONE; + ups = ups->next; + } + } + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", configfile, ctx.linenum, ctx.errmsg); + numerrors++; continue; } @@ -1395,18 +1918,50 @@ static void loadconfig(void) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); + numerrors++; upslogx(LOG_WARNING, "%s", errmsg); } } + 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; + } else { + /* DEBUG_MIN is absent or commented-away in ups.conf */ + upslogx(LOG_INFO, + "Applying debug level %d from " + "original command line arguments", + nut_debug_level_args); + nut_debug_level = nut_debug_level_args; + } + + if (pollfail_log_throttle_max >= 0) { + upslogx(LOG_INFO, + "Applying POLLFAIL_LOG_THROTTLE_MAX %d from upsmon.conf", + pollfail_log_throttle_max); + } + } + + /* FIXME: Per legacy behavior, we silently went on. + * Maybe should abort on unusable configs? + */ + if (numerrors) { + upslogx(LOG_ERR, "Encountered %d config errors, those entries were ignored", numerrors); + } + pconf_finish(&ctx); } +#ifndef WIN32 /* SIGPIPE handler */ static void sigpipe(int sig) { upsdebugx(1, "SIGPIPE: dazed and confused, but continuing after signal %i...", sig); } +#endif /* SIGQUIT, SIGTERM handler */ static void set_exit_flag(int sig) @@ -1451,6 +2006,13 @@ static void upsmon_cleanup(void) } upscli_cleanup(); + +#ifdef WIN32 + if(mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } static void user_fsd(int sig) @@ -1466,6 +2028,7 @@ static void set_reload_flag(int sig) reload_flag = 1; } +#ifndef WIN32 /* handler for alarm when getupsvarfd times out */ static void read_timeout(int sig) { @@ -1473,10 +2036,12 @@ static void read_timeout(int sig) /* don't do anything here, just return */ } +#endif /* install handlers for a few signals */ static void setup_signals(void) { +#ifndef WIN32 sigemptyset(&nut_upsmon_sigmask); sa.sa_mask = nut_upsmon_sigmask; sa.sa_flags = 0; @@ -1501,6 +2066,9 @@ static void setup_signals(void) sa.sa_handler = set_reload_flag; sigaction(SIGCMD_RELOAD, &sa, NULL); +#else + pipe_create(UPSMON_PIPE_NAME); +#endif } /* remember the last time the ups was not critical (OB + LB) */ @@ -1525,7 +2093,7 @@ static int try_connect(utype_t *ups) upsdebugx(1, "Trying to connect to UPS [%s]", ups->sys); - clearflag(&ups->status, ST_CONNECTED); + clearflag(&ups->status, ST_CLICONNECTED); /* force it if configured that way, just try it otherwise */ if (forcessl == 1) @@ -1567,10 +2135,10 @@ static int try_connect(utype_t *ups) } /* we're definitely connected now */ - setflag(&ups->status, ST_CONNECTED); + setflag(&ups->status, ST_CLICONNECTED); /* prevent connection leaking to NOTIFYCMD */ - fcntl(upscli_fd(&ups->conn), F_SETFD, FD_CLOEXEC); + set_close_on_exec(upscli_fd(&ups->conn)); /* now try to authenticate to upsd */ @@ -1609,6 +2177,14 @@ static void parse_status(utype_t *ups, char *status) if (!strstr(status, "FSD")) clearflag(&ups->status, ST_FSD); + /* similar to above - clear these flags and send notifications */ + if (!strstr(status, "CAL")) + ups_is_notcal(ups); + if (!strstr(status, "OFF")) + ups_is_notoff(ups); + if (!strstr(status, "BYPASS")) + ups_is_notbypass(ups); + statword = status; /* split up the status words and parse each one separately */ @@ -1628,8 +2204,11 @@ static void parse_status(utype_t *ups, char *status) if (!strcasecmp(statword, "RB")) upsreplbatt(ups); if (!strcasecmp(statword, "CAL")) - ups_cal(ups); - + ups_is_cal(ups); + if (!strcasecmp(statword, "OFF")) + ups_is_off(ups); + if (!strcasecmp(statword, "BYPASS")) + ups_is_bypass(ups); /* do it last to override any possible OL */ if (!strcasecmp(statword, "FSD")) ups_fsd(ups); @@ -1644,11 +2223,15 @@ static void parse_status(utype_t *ups, char *status) static void pollups(utype_t *ups) { char status[SMALLBUF]; + int pollfail_log = 0; /* if we throttle, only upsdebugx() but not upslogx() the failures */ + int upserror; /* try a reconnect here */ - if (!flag_isset(ups->status, ST_CONNECTED)) - if (try_connect(ups) != 1) + if (!flag_isset(ups->status, ST_CLICONNECTED)) { + if (try_connect(ups) != 1) { return; + } + } if (upscli_ssl(&ups->conn) == 1) upsdebugx(2, "%s: %s [SSL]", __func__, ups->sys); @@ -1659,6 +2242,27 @@ static void pollups(utype_t *ups) if (get_var(ups, "status", status, sizeof(status)) == 0) { clear_alarm(); + + /* reset pollfail log throttling */ +#if 0 + /* Note: last error is never cleared, so we reset it below */ + upserror = upscli_upserror(&ups->conn); + upsdebugx(3, "%s: Poll UPS [%s] after getvar(status) okay: upserror=%d: %s", + __func__, ups->sys, upserror, upscli_strerror(&ups->conn)); +#endif + upserror = UPSCLI_ERR_NONE; + if (pollfail_log_throttle_max >= 0 + && ups->pollfail_log_throttle_state != upserror + ) { + /* Notify throttled log that we are okay now */ + upslogx(LOG_ERR, "Poll UPS [%s] recovered from " + "failure state code %d - now %d", + ups->sys, ups->pollfail_log_throttle_state, + upserror); + } + ups->pollfail_log_throttle_state = upserror; + ups->pollfail_log_throttle_count = -1; + parse_status(ups, status); return; } @@ -1667,18 +2271,78 @@ static void pollups(utype_t *ups) clear_alarm(); /* try to make some of these a little friendlier */ + upserror = upscli_upserror(&ups->conn); + upsdebugx(3, "%s: Poll UPS [%s] after getvar(status) failed: upserror=%d", + __func__, ups->sys, upserror); + if (pollfail_log_throttle_max < 0) { + /* Log properly on each loop */ + pollfail_log = 1; + } else { + if (ups->pollfail_log_throttle_state == upserror) { + /* known issue, no syslog spam now... maybe */ + if (pollfail_log_throttle_max == 0) { + /* Only log once for start or end of the same + * failure state */ + pollfail_log = 0; + } else { + /* here (pollfail_log_throttle_max > 0) : + * only log once for start, every MAX iterations, + * and end of the same failure state + */ + if (ups->pollfail_log_throttle_count++ >= (pollfail_log_throttle_max - 1)) { + /* ping... */ + pollfail_log = 1; + ups->pollfail_log_throttle_count = 0; + } else { + pollfail_log = 0; + } + } + } else { + /* new error => reset pollfail log throttling and log it + * now (numeric states here, string for new state below) */ + if (pollfail_log_throttle_max == 0) { + upslogx(LOG_ERR, "Poll UPS [%s] failure state code " + "changed from %d to %d; " + "report below will not be repeated to syslog:", + ups->sys, ups->pollfail_log_throttle_state, + upserror); + } else { + upslogx(LOG_ERR, "Poll UPS [%s] failure state code " + "changed from %d to %d; " + "report below will only be repeated to syslog " + "every %d polling loop cycles (%d sec):", + ups->sys, ups->pollfail_log_throttle_state, + upserror, pollfail_log_throttle_max, + pollfail_log_throttle_max * pollfreq); + } - switch (upscli_upserror(&ups->conn)) { + ups->pollfail_log_throttle_state = upserror; + ups->pollfail_log_throttle_count = 0; + pollfail_log = 1; + } + } + switch (upserror) { case UPSCLI_ERR_UNKNOWNUPS: - upslogx(LOG_ERR, "Poll UPS [%s] failed - [%s] " - "does not exist on server %s", - ups->sys, ups->upsname, ups->hostname); - + if (pollfail_log) { + upslogx(LOG_ERR, "Poll UPS [%s] failed - [%s] " + "does not exist on server %s", + ups->sys, ups->upsname, ups->hostname); + } else { + upsdebugx(1, "Poll UPS [%s] failed - [%s] " + "does not exist on server %s", + ups->sys, ups->upsname, ups->hostname); + } break; + default: - upslogx(LOG_ERR, "Poll UPS [%s] failed - %s", - ups->sys, upscli_strerror(&ups->conn)); + if (pollfail_log) { + upslogx(LOG_ERR, "Poll UPS [%s] failed - %s", + ups->sys, upscli_strerror(&ups->conn)); + } else { + upsdebugx(1, "Poll UPS [%s] failed - [%s]", + ups->sys, upscli_strerror(&ups->conn)); + } break; } @@ -1789,7 +2453,13 @@ 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"); +#ifndef WIN32 + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); +#endif + 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(" -V display the version of this software\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"); @@ -1797,9 +2467,12 @@ static void help(const char *arg_progname) printf(" -4 IPv4 only\n"); printf(" -6 IPv6 only\n"); + nut_report_config_flags(); + exit(EXIT_SUCCESS); } +#ifndef WIN32 static void runparent(int fd) __attribute__((noreturn)); @@ -1845,10 +2518,12 @@ static void runparent(int fd) close(fd); exit(EXIT_SUCCESS); } +#endif /* fire up the split parent/child scheme */ static void start_pipe(void) { +#ifndef WIN32 int ret; ret = pipe(pipefd); @@ -1874,7 +2549,8 @@ static void start_pipe(void) close(pipefd[0]); /* prevent pipe leaking to NOTIFYCMD */ - fcntl(pipefd[1], F_SETFD, FD_CLOEXEC); + set_close_on_exec(pipefd[1]); +#endif /* WIN32 */ } static void delete_ups(utype_t *target) @@ -2020,7 +2696,34 @@ 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, cmdret = -1, checking_flag = 0, foreground = -1; + +#ifndef WIN32 + pid_t oldpid = -1; + int cmd = 0; +#else + const char * cmd = NULL; + DWORD ret; + + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + int maxhandle = 0; + pipe_conn_t *conn; + + /* remove trailing .exe */ + char * drv_name; + drv_name = (char *)xbasename(argv[0]); + char * name = strrchr(drv_name,'.'); + if( name != NULL ) { + if(strcasecmp(name, ".exe") == 0 ) { + prog = strdup(drv_name); + char * t = strrchr(prog,'.'); + *t = 0; + } + } + else { + prog = drv_name; + } +#endif printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -2031,22 +2734,38 @@ 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))) + if (!strncmp(optarg, "fsd", strlen(optarg))) { cmd = SIGCMD_FSD; - if (!strncmp(optarg, "stop", strlen(optarg))) + } else + if (!strncmp(optarg, "stop", strlen(optarg))) { cmd = SIGCMD_STOP; - if (!strncmp(optarg, "reload", strlen(optarg))) + } else + if (!strncmp(optarg, "reload", strlen(optarg))) { cmd = SIGCMD_RELOAD; + } /* bad command name given */ if (cmd == 0) help(argv[0]); break; +#ifndef WIN32 + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(argv[0]); + break; +#endif case 'D': nut_debug_level++; + nut_debug_level_args++; + break; + case 'F': + foreground = 1; + break; + case 'B': + foreground = 0; break; case 'f': free(configfile); @@ -2068,7 +2787,8 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(optarg); break; case 'V': - /* just show the banner */ + /* just show the optional CONFIG_FLAGS banner */ + nut_report_config_flags(); exit(EXIT_SUCCESS); case '4': opt_af = AF_INET; @@ -2084,18 +2804,144 @@ int main(int argc, char *argv[]) } } + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + + { /* scoping */ + char *s = getenv("NUT_DEBUG_LEVEL"); + int l; + if (s && str_to_int(s, &l, 10)) { + if (l > 0 && nut_debug_level_args < 1) { + upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d " + "since none was requested by command-line options", l); + nut_debug_level = l; + nut_debug_level_args = l; + } /* else follow -D settings */ + } /* else nothing to bother about */ + } + + /* Note: "cmd" may be non-trivial to command that instance by + * explicit PID number or lookup in PID file (error if absent). + * Otherwise, we are being asked to start and "cmd" is 0/NULL - + * for probing whether a competing older instance of this program + * is running (error if it is). + */ +#ifndef WIN32 + /* If cmd == 0 we are starting and check if a previous instance + * is running by sending signal '0' (i.e. 'kill 0' equivalent) + */ + if (oldpid < 0) { + cmdret = sendsignal(prog, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + +#else /* WIN32 */ if (cmd) { - sendsignal(prog, cmd); - exit(EXIT_SUCCESS); + /* Command the running daemon, it should be there */ + cmdret = sendsignal(UPSMON_PIPE_NAME, cmd); + } else { + /* Starting new daemon, check for competition */ + mutex = CreateMutex(NULL, TRUE, UPSMON_PIPE_NAME); + if (mutex == NULL) { + if (GetLastError() != ERROR_ACCESS_DENIED) { + fatalx(EXIT_FAILURE, + "Can not create mutex %s : %d.\n", + UPSMON_PIPE_NAME, (int)GetLastError()); + } + } + + cmdret = -1; /* unknown, maybe ok */ + if (GetLastError() == ERROR_ALREADY_EXISTS + || GetLastError() == ERROR_ACCESS_DENIED + ) { + cmdret = 0; /* known conflict */ + } } +#endif /* WIN32 */ - /* 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) { - 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); + switch (cmdret) { + case 0: + if (cmd) { + upsdebugx(1, "Signaled old daemon OK"); + } else { + if (checking_flag) { + printf("Note: A previous upsmon instance is already running!\n"); + printf("Usually it should not be running during OS shutdown,\n"); + printf("which is when checking POWERDOWNFLAG makes most sense.\n"); + } else { + 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); + } + } + break; + + case -3: + case -2: + /* if starting new daemon, no competition running - + * maybe OK (or failed to detect it => problem) + * if signaling old daemon - certainly have a problem + */ + upslogx(LOG_WARNING, "Could not %s PID file " + "to see if previous upsmon instance is " + "already running!", + (cmdret == -3 ? "find" : "parse")); + break; + + case -1: + case 1: /* WIN32 */ + default: + /* if cmd was nontrivial - speak up below, else be quiet */ + upsdebugx(1, "Just failed to send signal, no daemon was running"); + break; + } + + if (cmd) { + /* We were signalling a daemon, successfully or not - exit now... */ + if (cmdret != 0) { + /* sendsignal*() above might have logged more details + * for troubleshooting, e.g. about lack of PID file + */ + upslogx(LOG_NOTICE, "Failed to signal the currently running daemon (if any)"); +#ifndef WIN32 +# ifdef HAVE_SYSTEMD + switch (cmd) { + case SIGCMD_RELOAD: + upslogx(LOG_NOTICE, "Try 'systemctl reload %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + case SIGCMD_STOP: + upslogx(LOG_NOTICE, "Try 'systemctl stop %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + case SIGCMD_FSD: + if (oldpid < 0) { + upslogx(LOG_NOTICE, "Try to add '-P $PID' argument"); + } + break; + default: + upslogx(LOG_NOTICE, "Try 'systemctl %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + } +# else + if (oldpid < 0) { + upslogx(LOG_NOTICE, "Try to add '-P $PID' argument"); + } +# endif +#endif /* not WIN32 */ + } + + exit((cmdret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); } argc -= optind; @@ -2105,6 +2951,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,10 +2981,8 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (nut_debug_level < 1) { + if (!foreground) { background(); - } else { - upsdebugx(1, "debug level is '%d'", nut_debug_level); } /* only do the pipe stuff if the user hasn't disabled it */ @@ -2145,12 +2997,16 @@ int main(int argc, char *argv[]) become_user(new_uid); } else { +#ifndef WIN32 + /* Note: upsmon does not fork in WIN32 */ upslogx(LOG_INFO, "Warning: running as one big root process by request (upsmon -p)"); +#endif writepid(prog); } if (upscli_init(certverify, certpath, certname, certpasswd) < 0) { + upsnotify(NOTIFY_STATE_STOPPING, "Failed upscli_init()"); exit(EXIT_FAILURE); } @@ -2161,15 +3017,22 @@ int main(int argc, char *argv[]) closelog(); open_syslog(prog); + upsnotify(NOTIFY_STATE_READY_WITH_PID, NULL); + while (exit_flag == 0) { utype_t *ups; + upsnotify(NOTIFY_STATE_WATCHDOG, NULL); + /* check flags from signal handlers */ if (userfsd) forceshutdown(); - if (reload_flag) + if (reload_flag) { + upsnotify(NOTIFY_STATE_RELOADING, NULL); reload_conf(); + upsnotify(NOTIFY_STATE_READY, NULL); + } for (ups = firstups; ups != NULL; ups = ups->next) pollups(ups); @@ -2180,13 +3043,74 @@ int main(int argc, char *argv[]) if (use_pipe) check_parent(); +#ifndef WIN32 /* reap children that have exited */ waitpid(-1, NULL, WNOHANG); sleep(sleepval); +#else + maxhandle = 0; + memset(&handles,0,sizeof(handles)); + + /* Wait on the read IO of each connections */ + for (conn = pipe_connhead; conn; conn = conn->next) { + handles[maxhandle] = conn->overlapped.hEvent; + maxhandle++; + } + /* Add the new pipe connected event */ + handles[maxhandle] = pipe_connection_overlapped.hEvent; + maxhandle++; + + ret = WaitForMultipleObjects(maxhandle,handles,FALSE,sleepval*1000); + + if (ret == WAIT_FAILED) { + upslogx(LOG_ERR, "Wait failed"); + exit(EXIT_FAILURE); + } + + if (ret == WAIT_TIMEOUT) { + continue; + } + + /* Retrieve the signaled connection */ + for(conn = pipe_connhead; conn != NULL; conn = conn->next) { + if( conn->overlapped.hEvent == handles[ret-WAIT_OBJECT_0]) { + break; + } + } + /* a new pipe connection has been signaled */ + if (handles[ret] == pipe_connection_overlapped.hEvent) { + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + if( conn != NULL) { + if ( pipe_ready(conn) ) { + if (!strncmp(conn->buf, SIGCMD_FSD, sizeof(SIGCMD_FSD))) { + user_fsd(1); + } + + else if (!strncmp(conn->buf, SIGCMD_RELOAD, sizeof(SIGCMD_RELOAD))) { + set_reload_flag(1); + } + + else if (!strncmp(conn->buf, SIGCMD_STOP, sizeof(SIGCMD_STOP))) { + set_exit_flag(1); + } + + else { + upslogx(LOG_ERR,"Unknown signal"); + } + + pipe_disconnect(conn); + } + } + } +#endif } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); + upsnotify(NOTIFY_STATE_STOPPING, "Signal %d: exiting", exit_flag); upsmon_cleanup(); exit(EXIT_SUCCESS); diff --git a/clients/upsmon.h b/clients/upsmon.h index 23315139b9..767e146709 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -29,8 +29,10 @@ #define ST_PRIMARY (1 << 4) /* we are the primary (manager) of this UPS */ #define ST_MASTER ST_PRIMARY /* legacy alias */ #define ST_LOGIN (1 << 5) /* we are logged into this UPS */ -#define ST_CONNECTED (1 << 6) /* upscli_connect returned OK */ +#define ST_CLICONNECTED (1 << 6) /* upscli_connect returned OK */ #define ST_CAL (1 << 7) /* UPS calibration in progress (CAL) */ +#define ST_OFF (1 << 8) /* UPS is administratively off or asleep (OFF) */ +#define ST_BYPASS (1 << 9) /* UPS is on bypass so not protecting */ /* required contents of flag file */ #define SDMAGIC "upsmon-shutdown-file" @@ -49,7 +51,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) */ @@ -60,11 +62,24 @@ typedef struct { /* handle suppression of COMMOK and ONLINE at startup */ int commstate; /* these start at -1, and only */ int linestate; /* fire on a 0->1 transition */ + int offstate; /* fire on a 0->1 transition, may */ + /* be delayed vs. seeing OFF state */ + int bypassstate; /* fire on a 0->1 transition; */ + /* delays not implemented now */ + + /* see detailed comment for pollfail_log_throttle_max in upsmon.c + * about handling of poll failure log throttling (syslog storage I/O) + */ + int pollfail_log_throttle_state; /* Last (error) state which we throttle */ + int pollfail_log_throttle_count; /* How many pollfreq loops this UPS was in this state since last logged report? */ time_t lastpoll; /* time of last successful poll */ time_t lastnoncrit; /* time of last non-crit poll */ time_t lastrbwarn; /* time of last REPLBATT warning*/ time_t lastncwarn; /* time of last NOCOMM warning */ + + time_t offsince; /* time of recent entry into OFF state */ + void *next; } utype_t; @@ -81,6 +96,11 @@ typedef struct { #define NOTIFY_NOCOMM 8 /* UPS hasn't been contacted in a while */ #define NOTIFY_NOPARENT 9 /* privileged parent process died */ #define NOTIFY_CAL 10 /* UPS is performing calibration */ +#define NOTIFY_NOTCAL 11 /* UPS is performing calibration */ +#define NOTIFY_OFF 12 /* UPS is administratively OFF or asleep*/ +#define NOTIFY_NOTOFF 13 /* UPS is not anymore administratively OFF or asleep*/ +#define NOTIFY_BYPASS 14 /* UPS is administratively on bypass */ +#define NOTIFY_NOTBYPASS 15 /* UPS is not anymore administratively on bypass */ /* notify flag values */ @@ -89,8 +109,15 @@ typedef struct { #define NOTIFY_WALL (1 << 2) /* send the msg to all users */ #define NOTIFY_EXEC (1 << 3) /* send the msg to NOTIFYCMD script */ -/* flags are set to NOTIFY_SYSLOG | NOTIFY_WALL at program init */ -/* the user can override with NOTIFYFLAGS in the upsmon.conf */ +/* flags are set to NOTIFY_SYSLOG | NOTIFY_WALL at program init */ +/* except under Windows where they are set to NOTIFY_SYSLOG only */ +/* the user can override with NOTIFYFLAGS in the upsmon.conf */ + +#ifdef WIN32 +#define NOTIFY_DEFAULT NOTIFY_SYSLOG +#else +#define NOTIFY_DEFAULT (NOTIFY_SYSLOG | NOTIFY_WALL) +#endif /* This is only used in upsmon.c, but might it also have external consumers?.. * To move or not to move?.. @@ -103,25 +130,36 @@ static struct { int flags; } notifylist[] = { - { NOTIFY_ONLINE, "ONLINE", NULL, "UPS %s on line power", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_ONBATT, "ONBATT", NULL, "UPS %s on battery", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_LOWBATT, "LOWBATT", NULL, "UPS %s battery is low", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_COMMOK, "COMMOK", NULL, "Communications with UPS %s established", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_COMMBAD, "COMMBAD", NULL, "Communications with UPS %s lost", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_SHUTDOWN, "SHUTDOWN", NULL, "Auto logout and shutdown proceeding", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_REPLBATT, "REPLBATT", NULL, "UPS %s battery needs to be replaced", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_NOCOMM, "NOCOMM", NULL, "UPS %s is unavailable", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_NOPARENT, "NOPARENT", NULL, "upsmon parent process died - shutdown impossible", NOTIFY_SYSLOG | NOTIFY_WALL }, - { NOTIFY_CAL, "CAL", NULL, "UPS %s: calibration in progress", NOTIFY_SYSLOG }, + { NOTIFY_ONLINE, "ONLINE", NULL, "UPS %s on line power", NOTIFY_DEFAULT }, + { NOTIFY_ONBATT, "ONBATT", NULL, "UPS %s on battery", NOTIFY_DEFAULT }, + { NOTIFY_LOWBATT, "LOWBATT", NULL, "UPS %s battery is low", NOTIFY_DEFAULT }, + { NOTIFY_FSD, "FSD", NULL, "UPS %s: forced shutdown in progress", NOTIFY_DEFAULT }, + { NOTIFY_COMMOK, "COMMOK", NULL, "Communications with UPS %s established", NOTIFY_DEFAULT }, + { NOTIFY_COMMBAD, "COMMBAD", NULL, "Communications with UPS %s lost", NOTIFY_DEFAULT }, + { NOTIFY_SHUTDOWN, "SHUTDOWN", NULL, "Auto logout and shutdown proceeding", NOTIFY_DEFAULT }, + { NOTIFY_REPLBATT, "REPLBATT", NULL, "UPS %s battery needs to be replaced", NOTIFY_DEFAULT }, + { NOTIFY_NOCOMM, "NOCOMM", NULL, "UPS %s is unavailable", NOTIFY_DEFAULT }, + { NOTIFY_NOPARENT, "NOPARENT", NULL, "upsmon parent process died - shutdown impossible", NOTIFY_DEFAULT }, + { NOTIFY_CAL, "CAL", NULL, "UPS %s: calibration in progress", NOTIFY_DEFAULT }, + { NOTIFY_NOTCAL, "NOTCAL", NULL, "UPS %s: calibration finished", NOTIFY_DEFAULT }, + { NOTIFY_OFF, "OFF", NULL, "UPS %s: administratively OFF or asleep", NOTIFY_DEFAULT }, + { NOTIFY_NOTOFF, "NOTOFF", NULL, "UPS %s: no longer administratively OFF or asleep", NOTIFY_DEFAULT }, + { NOTIFY_BYPASS, "BYPASS", NULL, "UPS %s: on bypass (powered, not protecting)", NOTIFY_DEFAULT }, + { NOTIFY_NOTBYPASS,"NOTBYPASS",NULL, "UPS %s: no longer on bypass", NOTIFY_DEFAULT }, { 0, NULL, NULL, NULL, 0 } }; /* values for signals passed between processes */ +#ifndef WIN32 #define SIGCMD_FSD SIGUSR1 #define SIGCMD_STOP SIGTERM #define SIGCMD_RELOAD SIGHUP +#else +#define SIGCMD_FSD COMMAND_FSD +#define SIGCMD_STOP COMMAND_STOP +#define SIGCMD_RELOAD COMMAND_RELOAD +#endif /* various constants */ diff --git a/clients/upsrw.c b/clients/upsrw.c index d2fe1d9efb..2086d6de43 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -22,10 +22,14 @@ #include "common.h" #include "nut_platform.h" +#ifndef WIN32 #include #include #include #include +#else +#include "wincompat.h" +#endif #include "nut_stdint.h" #include "upsclient.h" @@ -49,8 +53,10 @@ static void usage(const char *prog) printf("Demo program to set variables within UPS hardware.\n"); printf("\n"); printf(" -h display this help text\n"); + printf(" -V display the version of this software\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 +65,9 @@ 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"); + + nut_report_config_flags(); } static void clean_exit(void) @@ -122,9 +130,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 @@ -395,7 +404,7 @@ static void do_enum(const char *varname, const int vartype, const long len) /* ENUM */ if (numa < 4) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 4)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 4)", numa); } printf("Option: \"%s\"", answer[3]); @@ -448,7 +457,7 @@ static void do_range(const char *varname) /* RANGE */ if (numa < 5) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 4)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 4)", numa); } min = atoi(answer[3]); @@ -591,7 +600,7 @@ static void print_rwlist(void) /* RW */ if (numa < 4) { - fatalx(EXIT_FAILURE, "Error: insufficient data (got %zu args, need at least 4)", numa); + fatalx(EXIT_FAILURE, "Error: insufficient data (got %" PRIuSIZE " args, need at least 4)", numa); } /* sock this entry away for later */ @@ -628,16 +637,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; @@ -653,6 +669,7 @@ int main(int argc, char **argv) break; case 'V': printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); + nut_report_config_flags(); exit(EXIT_SUCCESS); case 'h': default: diff --git a/clients/upssched.c b/clients/upssched.c index f35fe09ec7..9a8d00dd46 100644 --- a/clients/upssched.c +++ b/clients/upssched.c @@ -42,12 +42,19 @@ #include "common.h" #include +#ifndef WIN32 #include #include #include #include #include #include +#include +#else +#include "wincompat.h" +#include +#include +#endif #include "upssched.h" #include "timehead.h" @@ -62,11 +69,15 @@ typedef struct ttype_s { static ttype_t *thead = NULL; static conn_t *connhead = NULL; static char *cmdscript = NULL, *pipefn = NULL, *lockfn = NULL; -static int verbose = 0; /* use for debugging */ /* ups name and notify type (string) as received from upsmon */ static const char *upsname, *notify_type; +#ifdef WIN32 +static OVERLAPPED connect_overlapped; +#define BUF_LEN 512 +#endif + #define PARENT_STARTED -2 #define PARENT_UNNECESSARY -3 #define MAX_TRIES 30 @@ -85,6 +96,7 @@ static void exec_cmd(const char *cmd) snprintf(buf, sizeof(buf), "%s %s", cmdscript, cmd); err = system(buf); +#ifndef WIN32 if (WIFEXITED(err)) { if (WEXITSTATUS(err)) { upslogx(LOG_INFO, "exec_cmd(%s) returned %d", buf, WEXITSTATUS(err)); @@ -96,6 +108,14 @@ static void exec_cmd(const char *cmd) upslogx(LOG_ERR, "Execute command failure: %s", buf); } } +#else + if(err != -1) { + upslogx(LOG_INFO, "Execute command \"%s\" OK", buf); + } + else { + upslogx(LOG_ERR, "Execute command failure : %s", buf); + } +#endif return; } @@ -143,7 +163,7 @@ static void checktimers(void) if (emptyctr < EMPTY_WAIT) return; - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Timer queue empty, exiting"); #ifdef UPSSCHED_RACE_TEST @@ -151,6 +171,7 @@ static void checktimers(void) sleep(15); #endif + upsdebugx(1, "Timer queue empty, closing pipe and exiting upssched daemon"); unlink(pipefn); exit(EXIT_SUCCESS); } @@ -165,7 +186,7 @@ static void checktimers(void) tmpnext = tmp->next; if (now >= tmp->etime) { - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Event: %s ", tmp->name); exec_cmd(tmp->name); @@ -195,7 +216,7 @@ static void start_timer(const char *name, const char *ofsstr) return; } - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "New timer: %s (%ld seconds)", name, ofs); /* now add to the queue */ @@ -223,7 +244,7 @@ static void cancel_timer(const char *name, const char *cname) for (tmp = thead; tmp != NULL; tmp = tmp->next) { if (!strcmp(tmp->name, name)) { /* match */ - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Cancelling timer: %s", name); removetimer(tmp); return; @@ -232,13 +253,14 @@ static void cancel_timer(const char *name, const char *cname) /* this is not necessarily an error */ if (cname && cname[0]) { - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Cancel %s, event: %s", name, cname); exec_cmd(cname); } } +#ifndef WIN32 static void us_serialize(int op) { static int pipefd[2]; @@ -266,15 +288,20 @@ static void us_serialize(int op) break; } } +#endif -static int open_sock(void) +static TYPE_FD open_sock(void) { - int ret, fd; + TYPE_FD fd; + +#ifndef WIN32 + int ret; struct sockaddr_un ssaddr; + check_unix_socket_filename(pipefn); fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) + if (INVALID_FD(fd)) fatal_with_errno(EXIT_FAILURE, "Can't create a unix domain socket"); ssaddr.sun_family = AF_UNIX; @@ -300,7 +327,41 @@ static int open_sock(void) fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, US_LISTEN_BACKLOG); /* don't leak socket to CMDSCRIPT */ - fcntl(fd, F_SETFD, FD_CLOEXEC); + set_close_on_exec(fd); + +#else /* WIN32 */ + + fd = CreateNamedPipe( + pipefn, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + BUF_LEN, /* output buffer size */ + BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attributes */ + + if (INVALID_FD(fd)) { + fatal_with_errno(EXIT_FAILURE, + "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if (connect_overlapped.hEvent == NULL) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(fd,&connect_overlapped); +#endif return fd; } @@ -360,28 +421,78 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) /* Can't compare buflen to ret */ upsdebugx(2, "send_to_one(): buffered message too large"); - close(conn->fd); + if (VALID_FD(conn->fd)) { +#ifndef WIN32 + close(conn->fd); +#else + FlushFileBuffers(conn->fd); + CloseHandle(conn->fd); +#endif + conn->fd = ERROR_FD; + } + conn_del(conn); return 0; /* failed */ } + +#ifndef WIN32 ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t) buflen)) { - upsdebugx(2, "write to fd %d failed", conn->fd); + upsdebugx(2, "write failed on socket %d, disconnecting", conn->fd); close(conn->fd); conn_del(conn); return 0; /* failed */ } +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (conn->fd, buf, buflen, &bytesWritten, NULL); + if (result == 0) { + upsdebugx(2, "write failed on handle %p, disconnecting", conn->fd); + + /* FIXME not sure this is the right way to close a connection */ + if( conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); + CloseHandle(conn->fd); + conn_del(conn); + + return 0; + } + else { + ret = (ssize_t)bytesWritten; + } + + if ((ret < 1) || (ret != (ssize_t)buflen)) { + upsdebugx(2, "write to fd %p failed", conn->fd); + /* FIXME not sure this is the right way to close a connection */ + if (conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); + CloseHandle(conn->fd); + + return 0; /* failed */ + } +#endif return 1; /* OK */ } -static void conn_add(int sockfd) +static TYPE_FD conn_add(TYPE_FD sockfd) { - int acc, ret; + TYPE_FD acc; + +#ifndef WIN32 + int ret; conn_t *tmp, *last; struct sockaddr_un saddr; #if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) @@ -393,13 +504,13 @@ static void conn_add(int sockfd) salen = sizeof(saddr); acc = accept(sockfd, (struct sockaddr *) &saddr, &salen); - if (acc < 0) { + if (INVALID_FD(acc)) { upslog_with_errno(LOG_ERR, "accept on unix fd failed"); - return; + return ERROR_FD; } /* don't leak connection to CMDSCRIPT */ - fcntl(acc, F_SETFD, FD_CLOEXEC); + set_close_on_exec(acc); /* enable nonblocking I/O */ @@ -408,7 +519,7 @@ static void conn_add(int sockfd) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed"); close(acc); - return; + return ERROR_FD; } ret = fcntl(acc, F_SETFL, ret | O_NDELAY); @@ -416,7 +527,7 @@ static void conn_add(int sockfd) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed"); close(acc); - return; + return ERROR_FD; } tmp = last = connhead; @@ -438,6 +549,85 @@ static void conn_add(int sockfd) upsdebugx(3, "new connection on fd %d", acc); pconf_init(&tmp->ctx, NULL); + +#else /* WIN32 */ + + conn_t *conn, *tmp, *last; + + /* We have detected a connection on the opened pipe. So we start + by saving its handle and create a new pipe for future connection */ + conn = xcalloc(1, sizeof(*conn)); + conn->fd = sockfd; + + /* sock is the handle of the connection pending pipe */ + acc = CreateNamedPipe( + pipefn, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + BUF_LEN, /* output buffer size */ + BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (INVALID_FD(acc)) { + fatal_with_errno(EXIT_FAILURE, + "Can't create a state socket (windows named pipe)"); + } + + /* Prepare a new async wait for a connection on the pipe */ + CloseHandle(connect_overlapped.hEvent); + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if (connect_overlapped.hEvent == NULL) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(acc,&connect_overlapped); + + /* A new pipe waiting for new client connection has been created. + We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait + on the event associated to this IO */ + memset(&conn->read_overlapped,0,sizeof(conn->read_overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if (conn->read_overlapped.hEvent == NULL) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile (conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); + + conn->next = NULL; + + tmp = last = connhead; + + while (tmp) { + last = tmp; + tmp = tmp->next; + } + + if (last) + last->next = conn; + else + connhead = conn; + + upsdebugx(3, "new connection on handle %p", acc); + + pconf_init(&conn->ctx, NULL); +#endif + + return acc; } static int sock_arg(conn_t *conn) @@ -478,7 +668,7 @@ static void log_unknown(size_t numarg, char **arg) upslogx(LOG_INFO, "Unknown command on socket: "); for (i = 0; i < numarg; i++) - upslogx(LOG_INFO, "arg %zu: %s", i, arg[i]); + upslogx(LOG_INFO, "arg %" PRIuSIZE ": %s", i, arg[i]); } static int sock_read(conn_t *conn) @@ -487,26 +677,83 @@ static int sock_read(conn_t *conn) ssize_t ret; char ch; + upsdebugx(6, "Starting sock_read()"); for (i = 0; i < US_MAX_READ; i++) { - + /* NOTE: This does not imply that each command line must + * fit in the US_MAX_READ length limit - at worst we would + * "return 0", and continue with pconf_char() next round. + */ + size_t numarg; +#ifndef WIN32 + errno = 0; ret = read(conn->fd, &ch, 1); + if (ret > 0) + upsdebugx(6, "read() from fd %d returned %" PRIiSIZE " (bytes): '%c'; errno=%d: %s", + conn->fd, ret, ch, errno, strerror(errno)); + if (ret < 1) { /* short read = no parsing, come back later */ - if ((ret == -1) && (errno == EAGAIN)) + if ((ret == -1) && (errno == EAGAIN)) { + upsdebugx(6, "Ending sock_read(): short read"); return 0; + } /* O_NDELAY with zero bytes means nothing to read but - * since read() follows a succesful select() with - * ready file descriptor, ret shouldn't be 0. */ - if (ret == 0) + * since read() follows a successful select() with + * ready file descriptor, ret shouldn't be 0. + * This may also mean that the counterpart has exited + * and the file descriptor should be reaped. + */ + if (ret == 0) { + struct pollfd pfd; + pfd.fd = conn->fd; + pfd.events = 0; + pfd.revents = 0; + /* Note: we check errno twice, since it could + * have been set by read() above or by one + * of the probing routines below + */ + if (errno + || (fcntl(conn->fd, F_GETFD) < 0) + || (poll(&pfd, 1, 0) <= 0) + || errno + ) { + upsdebugx(4, "read() from fd %d returned 0; errno=%d: %s", + conn->fd, errno, strerror(errno)); + return -1; /* connection closed, probably */ + } + if (i == (US_MAX_READ - 1)) { + upsdebugx(4, "read() from fd %d returned 0 " + "too many times in a row, aborting " + "sock_read(); errno=%d: %s", + conn->fd, errno, strerror(errno)); + return -1; /* connection closed, probably */ + } continue; + } /* some other problem */ + upsdebugx(6, "Ending sock_read(): some other problem"); return -1; /* error */ } +#else + DWORD bytesRead; + GetOverlappedResult(conn->fd, &conn->read_overlapped, &bytesRead,FALSE); + if( bytesRead < 1 ) { + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); + return 0; + } + + ch = conn->buf[0]; + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,1,NULL,&(conn->read_overlapped)); +#endif ret = pconf_char(&conn->ctx, ch); if (ret == 0) /* nothing to parse yet */ @@ -516,10 +763,14 @@ static int sock_read(conn_t *conn) upslogx(LOG_NOTICE, "Parse error on sock: %s", conn->ctx.errmsg); + upsdebugx(6, "Ending sock_read(): parse error"); return 0; /* nothing parsed */ } /* try to use it, and complain about unknown commands */ + upsdebugx(3, "Ending sock_read() on a good note: try to use command:"); + for (numarg = 0; numarg < conn->ctx.numargs; numarg++) + upsdebugx(3, "\targ %" PRIuSIZE ": %s", numarg, conn->ctx.arglist[numarg]); if (!sock_arg(conn)) { log_unknown(conn->ctx.numargs, conn->ctx.arglist); send_to_one(conn, "ERR UNKNOWN\n"); @@ -528,15 +779,25 @@ static int sock_read(conn_t *conn) return 1; /* we did some work */ } + upsdebugx(6, "sock_read() from fd %d returned nothing " + "(maybe still collecting the command line); " + "errno=%d: %s", + conn->fd, errno, strerror(errno)); + return 0; /* fell out without parsing anything */ } -static void start_daemon(int lockfd) +static void start_daemon(TYPE_FD lockfd) { - int maxfd, pid, pipefd, ret; + int maxfd = 0; /* Unidiomatic use vs. "pipefd" below, which is "int" on non-WIN32 */ + TYPE_FD pipefd; struct timeval tv; + conn_t *tmp; + +#ifndef WIN32 + int pid, ret; fd_set rfds; - conn_t *tmp, *tmpnext; + conn_t *tmpnext; us_serialize(SERIALIZE_INIT); @@ -553,23 +814,68 @@ static void start_daemon(int lockfd) /* child */ - close(0); - close(1); - close(2); + /* make fds 0-2 (typically) point somewhere defined */ +#ifdef HAVE_DUP2 + /* system can close (if needed) and (re-)open a specific FD number */ + if (1) { /* scoping */ + TYPE_FD devnull = open("/dev/null", O_RDWR); + if (devnull < 0) + fatal_with_errno(EXIT_FAILURE, "open /dev/null"); + + if (dup2(devnull, STDIN_FILENO) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); + if (dup2(devnull, STDOUT_FILENO) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDOUT"); + + if (nut_debug_level) { + upsdebugx(1, "Keeping stderr open due to debug verbosity %d", nut_debug_level); + } else { + if (dup2(devnull, STDERR_FILENO) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDERR"); + } - /* make fds 0-2 point somewhere defined */ - if (open("/dev/null", O_RDWR) != 0) - fatal_with_errno(EXIT_FAILURE, "open /dev/null"); + close(devnull); + } +#else +# ifdef HAVE_DUP + /* opportunistically duplicate to the "lowest-available" FD number */ + close(STDIN_FILENO); + if (open("/dev/null", O_RDWR) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); + + close(STDOUT_FILENO); + if (dup(STDIN_FILENO) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "dup /dev/null as STDOUT"); + + if (nut_debug_level) { + upsdebugx(1, "Keeping stderr open due to debug verbosity %d", nut_debug_level); + } else { + close(STDERR_FILENO); + if (dup(STDIN_FILENO) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "dup /dev/null as STDERR"); + } +# else + close(STDIN_FILENO); + if (open("/dev/null", O_RDWR) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); - if (dup(0) == -1) - fatal_with_errno(EXIT_FAILURE, "dup"); + close(STDOUT_FILENO); + if (open("/dev/null", O_RDWR) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDOUT"); - if (dup(0) == -1) - fatal_with_errno(EXIT_FAILURE, "dup"); + if (nut_debug_level) { + upsdebugx(1, "Keeping stderr open due to debug verbosity %d", nut_debug_level); + } else { + close(STDERR_FILENO); + if (open("/dev/null", O_RDWR) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDERR"); + } +# endif +#endif pipefd = open_sock(); - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Timer daemon started"); /* release the parent */ @@ -580,8 +886,15 @@ static void start_daemon(int lockfd) close(lockfd); /* now watch for activity */ + upsdebugx(2, "Timer daemon waiting for connections on pipefd %d", + pipefd); for (;;) { + int zero_reads = 0, total_reads = 0; + struct timeval start, now; + + gettimeofday(&start, NULL); + /* wait at most 1s so we can check our timers regularly */ tv.tv_sec = 1; tv.tv_usec = 0; @@ -601,7 +914,6 @@ static void start_daemon(int lockfd) ret = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (ret > 0) { - if (FD_ISSET(pipefd, &rfds)) conn_add(pipefd); @@ -611,10 +923,15 @@ static void start_daemon(int lockfd) tmpnext = tmp->next; if (FD_ISSET(tmp->fd, &rfds)) { - if (sock_read(tmp) < 0) { + total_reads++; + ret = sock_read(tmp); + if (ret < 0) { + upsdebugx(3, "closing connection on fd %d", tmp->fd); close(tmp->fd); conn_del(tmp); } + if (ret == 0) + zero_reads++; } tmp = tmpnext; @@ -622,23 +939,137 @@ static void start_daemon(int lockfd) } checktimers(); + + /* upsdebugx(6, "zero_reads=%d total_reads=%d", zero_reads, total_reads); */ + if (zero_reads && zero_reads == total_reads) { + /* Catch run-away loops - that is, consider + * throttling the cycle as to not hog CPU: + * did select() spend its second to reply, + * or had something to say immediately? + * Note that while select() may have changed + * "tv" to deduct the time waited, our further + * processing loops could eat some more time. + * So we just check the difference of "start" + * and "now". If we did spend a substantial + * part of the second, do not delay further. + */ + double d; + gettimeofday(&now, NULL); + d = difftimeval(now, start); + upsdebugx(6, "difftimeval() => %f sec", d); + if (d > 0 && d < 0.2) { + d = (1.0 - d) * 1000000.0; + upsdebugx(5, "Enforcing a throttling sleep: %f usec", d); + usleep((useconds_t)d); + } + } + } + +#else /* WIN32 */ + + DWORD timeout_ms; + HANDLE rfds[32]; + + char module[MAX_PATH]; + STARTUPINFO sinfo; + PROCESS_INFORMATION pinfo; + if( !GetModuleFileName(NULL,module,MAX_PATH) ) { + fatal_with_errno(EXIT_FAILURE, "Can't retrieve module name"); + } + memset(&sinfo,0,sizeof(sinfo)); + if(!CreateProcess(module, NULL, NULL,NULL,FALSE,0,NULL,NULL,&sinfo,&pinfo)) { + fatal_with_errno(EXIT_FAILURE, "Can't create child process"); + } + pipefd = open_sock(); + + if (nut_debug_level) + upslogx(LOG_INFO, "Timer daemon started"); + + /* drop the lock now that the background is running */ + CloseHandle(lockfd); + DeleteFile(lockfn); + + /* now watch for activity */ + + for (;;) { + /* wait at most 1s so we can check our timers regularly */ + tv.tv_sec = 1; + tv.tv_usec = 0; + + timeout_ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + maxfd = 0; + + /* Wait on the read IO of each connections */ + for (tmp = connhead; tmp != NULL; tmp = tmp->next) { + rfds[maxfd] = tmp->read_overlapped.hEvent; + maxfd++; + } + /* Add the connect event */ + rfds[maxfd] = connect_overlapped.hEvent; + maxfd++; + DWORD ret_val; + ret_val = WaitForMultipleObjects( + maxfd, /* number of objects in array */ + rfds, /* array of objects */ + FALSE, /* wait for any object */ + timeout_ms); /* timeout in millisecond */ + + if (ret_val == WAIT_FAILED) { + upslog_with_errno(LOG_ERR, "waitfor failed"); + return; + } + + /* timer has not expired */ + if (ret_val != WAIT_TIMEOUT) { + /* Retrieve the signaled connection */ + for(tmp = connhead; tmp != NULL; tmp = tmp->next) { + if( tmp->read_overlapped.hEvent == rfds[ret_val-WAIT_OBJECT_0]) { + break; + } + } + + /* the connection event handle has been signaled */ + if (rfds[ret_val] == connect_overlapped.hEvent) { + pipefd = conn_add(pipefd); + } + /* one of the read event handle has been signaled */ + else { + if( tmp != NULL) { + if (sock_read(tmp) < 0) { + upsdebugx(3, "closing connection on handle %p", tmp->fd); + CloseHandle(tmp->fd); + conn_del(tmp); + } + } + } + + } + + checktimers(); } +#endif } /* --- 'client' functions --- */ -static int try_connect(void) +static TYPE_FD try_connect(void) { - int pipefd, ret; + TYPE_FD pipefd; + +#ifndef WIN32 + int 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); pipefd = socket(AF_UNIX, SOCK_STREAM, 0); - if (pipefd < 0) + if (INVALID_FD(pipefd)) fatal_with_errno(EXIT_FAILURE, "socket"); ret = connect(pipefd, (const struct sockaddr *) &saddr, sizeof(saddr)); @@ -646,32 +1077,60 @@ static int try_connect(void) if (ret != -1) return pipefd; - return -1; +#else /* WIN32 */ + + BOOL result = FALSE; + + result = WaitNamedPipe(pipefn,NMPWAIT_USE_DEFAULT_WAIT); + + if (result == FALSE) { + return ERROR_FD; + } + + pipefd = CreateFile( + pipefn, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (VALID_FD(pipefd)) + return pipefd; + +#endif + + return ERROR_FD; } -static int get_lock(const char *fn) +static TYPE_FD get_lock(const char *fn) { +#ifndef WIN32 return open(fn, O_RDONLY | O_CREAT | O_EXCL, 0); - +#else + return CreateFile(fn,GENERIC_ALL,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); +#endif } /* try to connect to bg process, and start one if necessary */ -static int check_parent(const char *cmd, const char *arg2) +static TYPE_FD check_parent(const char *cmd, const char *arg2) { - int pipefd, lockfd, tries = 0; + TYPE_FD pipefd, lockfd; + int tries = 0; for (tries = 0; tries < MAX_TRIES; tries++) { - pipefd = try_connect(); - if (pipefd != -1) + if (VALID_FD(pipefd)) return pipefd; /* timer daemon isn't running */ /* it's not running, so there's nothing to cancel */ if (!strcmp(cmd, "CANCEL") && (arg2 == NULL)) - return PARENT_UNNECESSARY; + return (TYPE_FD)PARENT_UNNECESSARY; /* arg2 non-NULL means there is a cancel action available */ @@ -679,15 +1138,19 @@ static int check_parent(const char *cmd, const char *arg2) lockfd = get_lock(lockfn); - if (lockfd != -1) { + if (VALID_FD(lockfd)) { start_daemon(lockfd); - return PARENT_STARTED; /* started successfully */ + return (TYPE_FD)PARENT_STARTED; /* started successfully */ } /* we didn't get the lock - must be two upsscheds running */ /* blow this away in case we crashed before */ +#ifndef WIN32 unlink(lockfn); +#else + DeleteFile(lockfn); +#endif /* give the other one a chance to start it, then try again */ usleep(250000); @@ -699,13 +1162,18 @@ static int check_parent(const char *cmd, const char *arg2) static void sendcmd(const char *cmd, const char *arg1, const char *arg2) { - int i, pipefd; + int i; ssize_t ret; size_t enclen, buflen; char buf[SMALLBUF], enc[SMALLBUF + 8]; +#ifndef WIN32 int ret_s; struct timeval tv; fd_set fdread; +#else + DWORD bytesWritten = 0; +#endif + TYPE_FD pipefd; /* insanity */ if (!arg1) @@ -735,18 +1203,18 @@ static void sendcmd(const char *cmd, const char *arg1, const char *arg2) pipefd = check_parent(cmd, arg2); - if (pipefd == PARENT_STARTED) { + if (pipefd == (TYPE_FD)PARENT_STARTED) { /* loop back and try to connect now */ usleep(250000); continue; } /* special case for CANCEL when no parent is running */ - if (pipefd == PARENT_UNNECESSARY) + if (pipefd == (TYPE_FD)PARENT_UNNECESSARY) return; /* we're connected now */ - +#ifndef WIN32 ret = write(pipefd, enc, enclen); /* if we can't send the whole thing, loop back and try again */ @@ -791,6 +1259,38 @@ static void sendcmd(const char *cmd, const char *arg1, const char *arg2) continue; } +#else /* WIN32 */ + ret = WriteFile(pipefd, enc, enclen, &bytesWritten, NULL); + if (ret == 0 || bytesWritten != enclen) { + upslogx(LOG_ERR, "write failed, trying again"); + CloseHandle(pipefd); + continue; + } + + OVERLAPPED read_overlapped; + DWORD ret; + + memset(&read_overlapped,0,sizeof(read_overlapped)); + memset(buf,0,sizeof(buf)); + read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(read_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile(pipefd,buf,sizeof(buf)-1,NULL,&(read_overlapped)); + + ret = WaitForSingleObject(read_overlapped.hEvent,2000); + + if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { + upslogx(LOG_ERR, "read confirmation failed, trying again"); + CloseHandle(pipefd); + continue; + } +#endif + if (!strncmp(buf, "OK", 2)) return; /* success */ @@ -823,33 +1323,56 @@ static void parse_at(const char *ntype, const char *un, const char *cmd, } /* check upsname: does this apply to us? */ - if (strcmp(upsname, un) != 0) - if (strcmp(un, "*") != 0) + upsdebugx(2, "%s: is '%s' in AT command the '%s' we were launched to process?", + __func__, un, upsname); + if (strcmp(upsname, un) != 0) { + if (strcmp(un, "*") != 0) { + upsdebugx(1, "%s: SKIP: '%s' in AT command " + "did not match the '%s' UPSNAME " + "we were launched to process", + __func__, un, upsname); return; /* not for us, and not the wildcard */ + } else { + upsdebugx(1, "%s: this AT command is for a wildcard: matched", __func__); + } + } else { + upsdebugx(1, "%s: '%s' in AT command matched the '%s' " + "UPSNAME we were launched to process", + __func__, un, upsname); + } /* see if the current notify type matches the one from the .conf */ - if (strcasecmp(notify_type, ntype) != 0) + if (strcasecmp(notify_type, ntype) != 0) { + upsdebugx(1, "%s: SKIP: '%s' in AT command " + "did not match the '%s' NOTIFYTYPE " + "we were launched to process", + __func__, ntype, notify_type); return; + } /* if command is valid, send it to the daemon (which may start it) */ if (!strcmp(cmd, "START-TIMER")) { + upsdebugx(1, "%s: processing %s", __func__, cmd); sendcmd("START", ca1, ca2); return; } if (!strcmp(cmd, "CANCEL-TIMER")) { + upsdebugx(1, "%s: processing %s", __func__, cmd); sendcmd("CANCEL", ca1, ca2); return; } if (!strcmp(cmd, "EXECUTE")) { + upsdebugx(1, "%s: processing %s", __func__, cmd); + if (ca1[0] == '\0') { upslogx(LOG_ERR, "Empty EXECUTE command argument"); return; } - if (verbose) + if (nut_debug_level) upslogx(LOG_INFO, "Executing command: %s", ca1); exec_cmd(ca1); @@ -872,13 +1395,21 @@ static int conf_arg(size_t numargs, char **arg) /* PIPEFN */ if (!strcmp(arg[0], "PIPEFN")) { +#ifndef WIN32 pipefn = xstrdup(arg[1]); +#else + pipefn = xstrdup("\\\\.\\pipe\\upssched"); +#endif return 1; } /* LOCKFN */ if (!strcmp(arg[0], "LOCKFN")) { +#ifndef WIN32 lockfn = xstrdup(arg[1]); +#else + lockfn = filter_path(arg[1]); +#endif return 1; } @@ -910,6 +1441,7 @@ static void checkconf(void) { char fn[SMALLBUF]; PCONF_CTX_t ctx; + int numerrors = 0; snprintf(fn, sizeof(fn), "%s/upssched.conf", confpath()); @@ -924,6 +1456,7 @@ static void checkconf(void) if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); + numerrors++; continue; } @@ -941,24 +1474,76 @@ static void checkconf(void) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); + numerrors++; upslogx(LOG_WARNING, "%s", errmsg); } } + + /* FIXME: Per legacy behavior, we silently went on. + * Maybe should abort on unusable configs? + */ + if (numerrors) { + upslogx(LOG_ERR, "Encountered %d config errors, those entries were ignored", numerrors); + } + pconf_finish(&ctx); } +static void help(const char *arg_progname) + __attribute__((noreturn)); + +static void help(const char *arg_progname) +{ + printf("upssched: upsmon's scheduling helper for offset timers\n"); + printf("Practical behavior is managed by UPSNAME and NOTIFYTYPE envvars\n"); + + printf("\nUsage: %s [OPTIONS]\n\n", arg_progname); + printf(" -D raise debugging level (NOTE: keeps reporting when daemonized)\n"); + printf(" -V display the version of this software\n"); + printf(" -h display this help\n"); + + nut_report_config_flags(); + + exit(EXIT_SUCCESS); +} + + int main(int argc, char **argv) { - const char *prog = NULL; - /* More a use for argc to avoid warnings than a real need: */ - if (argc > 0) { - xbasename(argv[0]); - } else { - xbasename("upssched"); + const char *prog = xbasename(argv[0]); + int i; + + while ((i = getopt(argc, argv, "+DVh")) != -1) { + switch (i) { + case 'D': + nut_debug_level++; + break; + + case 'h': + help(argv[0]); +#ifndef HAVE___ATTRIBUTE__NORETURN + break; +#endif + + case 'V': + /* just show the optional CONFIG_FLAGS banner */ + nut_report_config_flags(); + exit(EXIT_SUCCESS); + } } - verbose = 1; /* TODO: remove when done testing, or add -D */ + { /* scoping */ + char *s = getenv("NUT_DEBUG_LEVEL"); + int l; + if (s && str_to_int(s, &l, 10)) { + if (l > 0 && nut_debug_level < 1) { + upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d " + "since none was requested by command-line options", l); + nut_debug_level = l; + } /* else follow -D settings */ + } /* else nothing to bother about */ + } /* normally we don't have stderr, so get this going to syslog early */ open_syslog(prog); @@ -968,13 +1553,18 @@ int main(int argc, char **argv) notify_type = getenv("NOTIFYTYPE"); if ((!upsname) || (!notify_type)) { - printf("Error: UPSNAME and NOTIFYTYPE must be set.\n"); + printf("Error: environment variables UPSNAME and NOTIFYTYPE must be set.\n"); printf("This program should only be run from upsmon.\n"); exit(EXIT_FAILURE); } /* see if this matches anything in the config file */ + /* This is actually the processing loop: + * checkconf -> conf_arg -> parse_at -> sendcmd -> daemon if needed + * -> start_daemon -> conn_add(pipefd) or sock_read(conn) + */ checkconf(); + upsdebugx(1, "Exiting upssched (CLI process)"); exit(EXIT_SUCCESS); } diff --git a/clients/upssched.h b/clients/upssched.h index 1b7e8f16dd..f5db60f48c 100644 --- a/clients/upssched.h +++ b/clients/upssched.h @@ -4,6 +4,7 @@ #define NUT_UPSSCHED_H_SEEN 1 #include +#include "common.h" #define SERIALIZE_INIT 1 #define SERIALIZE_SET 2 @@ -17,7 +18,11 @@ extern "C" { /* track client connections */ typedef struct conn_s { - int fd; + TYPE_FD fd; +#ifdef WIN32 + char buf[LARGEBUF]; + OVERLAPPED read_overlapped; +#endif PCONF_CTX_t ctx; struct conn_s *next; } conn_t; diff --git a/clients/upsset.c b/clients/upsset.c index ac8f2854ae..f9f94228c9 100644 --- a/clients/upsset.c +++ b/clients/upsset.c @@ -19,11 +19,16 @@ #include "common.h" +#ifndef WIN32 #include #include #include #include +#else +#include "wincompat.h" +#endif +#include "nut_stdint.h" #include "upsclient.h" #include "cgilib.h" #include "parseconf.h" @@ -44,7 +49,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; @@ -410,7 +415,7 @@ static void showcmds(void) /* CMD upsname cmdname */ if (numa < 3) { fprintf(stderr, "Error: insufficient data " - "(got %zu args, need at least 3)\n", numa); + "(got %" PRIuSIZE " args, need at least 3)\n", numa); return; } @@ -697,7 +702,7 @@ static void do_enum(const char *varname) if (numa < 4) { fprintf(stderr, "Error: insufficient data " - "(got %zu args, need at least 4)\n", numa); + "(got %" PRIuSIZE " args, need at least 4)\n", numa); free(val); return; @@ -746,11 +751,12 @@ static void do_type(const char *varname) if (!strncasecmp(answer[i], "STRING:", 7)) { char *ptr, len; + long l; /* split out the : data */ ptr = strchr(answer[i], ':'); *ptr++ = '\0'; - long l = strtol(ptr, (char **) NULL, 10); + l = strtol(ptr, (char **) NULL, 10); assert(l <= 127); /* FIXME: Loophole about longer numbers? Why are we limited to char at all here? */ len = (char)l; 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/Makefile.am b/common/Makefile.am index 6d3af988a6..3c92973cd6 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,17 +1,29 @@ # Network UPS Tools: common +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + AM_CFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include +AM_CXXFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include +AM_LDFLAGS = -no-undefined +EXTRA_DIST = +CLEANFILES = noinst_LTLIBRARIES = libparseconf.la libcommon.la libcommonclient.la if WITH_NUTCONF -# We define the recipe below in any case, but only activate it by default -# if the build configuration tells us to: -noinst_LTLIBRARIES += libnutconf.la -endif + # We define the recipe below in any case, but only activate it by default + # if the build configuration tells us to: + noinst_LTLIBRARIES += libnutconf.la +endif WITH_NUTCONF libparseconf_la_SOURCES = parseconf.c -AM_CXXFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include libnutconf_la_SOURCES = nutconf.cpp nutstream.cpp nutwriter.cpp nutipc.cpp # do not hard depend on '../include/nut_version.h', since it blocks @@ -19,42 +31,120 @@ libnutconf_la_SOURCES = nutconf.cpp nutstream.cpp nutwriter.cpp nutipc.cpp # BUILT_SOURCES (in ../include) will ensure nut_version.h will # be built before anything else... but do depend on its build area: if BUILDING_IN_TREE -# No need for symlink hack -common.c: $(top_builddir)/include/nut_version.h -else -# Surprisingly, for some "make" implementations this dependency means -# that the "common.c" required for builds below will be seeked in the -# current directory. So for out-of-tree builds like distcheck, we have -# to symlink the "real" source to build area: -common.c: $(top_builddir)/include/nut_version.h $(srcdir)/common.c + # No need for symlink hack + common.c: $(top_builddir)/include/nut_version.h +else !BUILDING_IN_TREE + # Surprisingly, for some "make" implementations this dependency means + # that the "common.c" required for builds below will be seeked in the + # current directory. So for out-of-tree builds like distcheck, we have + # to symlink the "real" source to build area: + common.c: $(top_builddir)/include/nut_version.h $(srcdir)/common.c test -s "$@" || ln -s -f "$(top_srcdir)/common/common.c" "$@" -endif +endif !BUILDING_IN_TREE $(top_builddir)/include/nut_version.h: - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) libcommon_la_SOURCES = state.c str.c upsconf.c libcommonclient_la_SOURCES = state.c str.c + +# several other Makefiles include the two helpers common.c str.c (and +# perhaps some other string-related code), so make them a library too; +# note that LTLIBOBJS pulls in snprintf.c contents too. +noinst_LTLIBRARIES += libcommonstr.la +libcommonstr_la_SOURCES = str.c +libcommonstr_la_CFLAGS = $(AM_CFLAGS) -DWITHOUT_LIBSYSTEMD=1 +libcommonstr_la_LIBADD = @LTLIBOBJS@ + if BUILDING_IN_TREE -libcommon_la_SOURCES += common.c -libcommonclient_la_SOURCES += common.c -else -nodist_libcommon_la_SOURCES = common.c -nodist_libcommonclient_la_SOURCES = common.c -CLEANFILES = $(top_builddir)/common/common.c -BUILT_SOURCES = common.c -endif + libcommon_la_SOURCES += common.c + libcommonstr_la_SOURCES += common.c + libcommonclient_la_SOURCES += common.c +else !BUILDING_IN_TREE + nodist_libcommon_la_SOURCES = common.c + nodist_libcommonstr_la_SOURCES = common.c + nodist_libcommonclient_la_SOURCES = common.c + CLEANFILES += $(top_builddir)/common/common.c + BUILT_SOURCES = common.c +endif !BUILDING_IN_TREE + +if HAVE_STRPTIME + EXTRA_DIST += strptime.c +else !HAVE_STRPTIME + # fall back to NetBSD implem + libcommon_la_SOURCES += strptime.c + libcommonstr_la_SOURCES += strptime.c + libcommonclient_la_SOURCES += strptime.c +endif !HAVE_STRPTIME + +if HAVE_STRNLEN + EXTRA_DIST += strnlen.c +else !HAVE_STRNLEN + # fall back to FreeBSD implem + libcommon_la_SOURCES += strnlen.c + libcommonstr_la_SOURCES += strnlen.c + libcommonclient_la_SOURCES += strnlen.c +endif !HAVE_STRNLEN + +if HAVE_STRSEP + EXTRA_DIST += strsep.c +else !HAVE_STRSEP + # fall back to simple implem + libcommon_la_SOURCES += strsep.c + libcommonstr_la_SOURCES += strsep.c + libcommonclient_la_SOURCES += strsep.c +endif !HAVE_STRSEP + +if HAVE_WINDOWS + libnutwincompat_la_SOURCES = wincompat.c $(top_srcdir)/include/wincompat.h + libnutwincompat_la_LDFLAGS = + libnutwincompat_la_LIBADD = + # Assume setenv() provided by OS or nut_setenv() provided by + # another NUT library and linked to the final NUT program/lib + # (anyhow, avoid a link-time conflict with two definitions): + libnutwincompat_la_CFLAGS = $(AM_CFLAGS) -DHAVE_SETENV=1 + noinst_LTLIBRARIES += libnutwincompat.la + + libcommon_la_SOURCES += wincompat.c $(top_srcdir)/include/wincompat.h + libcommonclient_la_SOURCES += wincompat.c $(top_srcdir)/include/wincompat.h +endif HAVE_WINDOWS # ensure inclusion of local implementation of missing systems functions # using LTLIBOBJS. Refer to configure.in/.ac -> AC_REPLACE_FUNCS -libcommon_la_LIBADD = libparseconf.la @LTLIBOBJS@ -libcommonclient_la_LIBADD = libparseconf.la @LTLIBOBJS@ +libcommon_la_LIBADD = libparseconf.la @LTLIBOBJS@ @NETLIBS@ +libcommonclient_la_LIBADD = libparseconf.la @LTLIBOBJS@ @NETLIBS@ -# several other Makefiles include these two helpers, so make them a library too -# note that LTLIBOBJS pulls in snprintf.c contents too -noinst_LTLIBRARIES += libcommonstr.la -libcommonstr_la_SOURCES = common.c str.c -libcommonstr_la_LIBADD = @LTLIBOBJS@ +libcommon_la_CFLAGS = $(AM_CFLAGS) +libcommonclient_la_CFLAGS = $(AM_CFLAGS) + +if WITH_NUTCONF + libnutconf_la_CXXFLAGS = $(AM_CXXFLAGS) + libnutconf_la_LIBADD = @LTLIBOBJS@ @NETLIBS@ libcommonclient.la +endif WITH_NUTCONF + +if HAVE_LIBREGEX + libcommon_la_CFLAGS += $(LIBREGEX_CFLAGS) + libcommon_la_LIBADD += $(LIBREGEX_LIBS) + + libcommonstr_la_CFLAGS += $(LIBREGEX_CFLAGS) + libcommonstr_la_LIBADD += $(LIBREGEX_LIBS) + + libcommonclient_la_CFLAGS += $(LIBREGEX_CFLAGS) + libcommonclient_la_LIBADD += $(LIBREGEX_LIBS) +endif HAVE_LIBREGEX + +# Did the user request, and build env support, tighter integration with +# libsystemd methods such as sd_notify()? +if WITH_LIBSYSTEMD + libcommon_la_CFLAGS += $(LIBSYSTEMD_CFLAGS) + libcommon_la_LIBADD += $(LIBSYSTEMD_LIBS) + +# A typical client should not need this, +# but just in case (and to simplify linking)... +# libcommonclient_la_CFLAGS += $(LIBSYSTEMD_CFLAGS) +# libcommonclient_la_LIBADD += $(LIBSYSTEMD_LIBS) + libcommonclient_la_CFLAGS += -DWITHOUT_LIBSYSTEMD=1 +endif WITH_LIBSYSTEMD if WITH_NEON if WITH_SNMP @@ -77,31 +167,35 @@ if WITH_DMF # other libraries we build (typically consumed with libcommonstr.la) libnutdmfsnmp_la_LIBADD = $(LIBNETSNMP_LIBS) -if WITH_LIBXML2 - libnutdmfsnmp_la_LIBADD += $(LIBXML2_LIBS) - libnutdmfsnmp_la_CFLAGS += $(LIBXML2_CFLAGS) -endif - if WITH_LIBLTDL # NOTE: the C macro WITH_LIBLTDL=1 will probably be defined by config.h # and unlike the DMF macros, we do not care much about this case. # We do care about it not defined when not enabled (so we define =0 below). libnutdmfsnmp_la_LIBADD += $(LIBLTDL_LIBS) libnutdmfsnmp_la_CFLAGS += -DWITH_LIBLTDL=1 $(LIBLTDL_CFLAGS) -else +else !WITH_LIBLTDL libnutdmfsnmp_la_LIBADD += $(LIBNEON_LIBS) libnutdmfsnmp_la_CFLAGS += -DWITH_LIBLTDL=0 -endif +endif !WITH_LIBLTDL # Note: absence of LUA does not require to set -DWITH_DMF_FUNCTIONS=0 if WITH_DMF_LUA libnutdmfsnmp_la_CFLAGS += -DWITH_DMF_LUA=1 -DWITH_DMF_FUNCTIONS=1 $(LUA_INCLUDE) libnutdmfsnmp_la_LIBADD += $(LUA_LIB) -else +else !WITH_DMF_LUA libnutdmfsnmp_la_CFLAGS += -DWITH_DMF_LUA=0 -endif +endif !WITH_DMF_LUA + +# Mention libxml2 last, its search paths seem to upset builds on FreeBSD +# at least (due to entanglement of SO cross-references). +if WITH_LIBXML2 + libnutdmfsnmp_la_LIBADD += $(LIBXML2_LIBS) + libnutdmfsnmp_la_CFLAGS += $(LIBXML2_CFLAGS) +endif WITH_LIBXML2 -endif +else !WITH_DMF + EXTRA_DIST += dmfsnmp.c dmfcore.c alist.c +endif !WITH_DMF # Note: Consumers of this lib will generally want the dependencies linked via: # libnutdmfsnmp_consumer_LDADD += $(abs_top_builddir)/common/libnutdmfsnmp.la @@ -111,13 +205,20 @@ endif # -DWITH_DMFMIB=1 -DWITH_SNMP=1 -DWITH_NEON=1 #if WITH_DMF_LUA # libnutdmfsnmp_consumer_CFLAGS += -DWITH_DMF_LUA=1 -DWITH_DMF_FUNCTIONS=1 $(LUA_INCLUDE) -#endif -endif -endif +#endif WITH_DMF_LUA +else !WITH_SNMP + EXTRA_DIST += dmfsnmp.c dmfcore.c alist.c +endif !WITH_SNMP +else !WITH_NEON + EXTRA_DIST += dmfsnmp.c dmfcore.c alist.c +endif !WITH_NEON MAINTAINERCLEANFILES = Makefile.in .dirstamp +# We only build it optionally, but if it lingers after a broken build - +# do clean it always anyway! +CLEANFILES += libnutdmfsnmp.la # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! #clean-local: -# rm -rf $(builddir)/.deps +# $(AM_V_at)rm -rf $(builddir)/.deps diff --git a/common/atexit.c b/common/atexit.c index 9279c6692b..22785c5a8b 100644 --- a/common/atexit.c +++ b/common/atexit.c @@ -6,6 +6,7 @@ #ifndef HAVE_ATEXIT #include +#include "common.h" int atexit(fn) void (*fn)(); @@ -13,6 +14,7 @@ int atexit(fn) #ifdef HAVE_ON_EXIT return on_exit(fn, 0); #else + NUT_UNUSED_VARIABLE(fn); /* Choose some errno thats likely to exist on lots of systems */ errno = EPERM; return (-1); diff --git a/common/common.c b/common/common.c index 88dbe573a3..1de1c2a7c1 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 @@ -19,20 +19,47 @@ */ #include "common.h" +#include "timehead.h" #include +#ifndef WIN32 #include #include #include #include +#include +#else +#include +#endif + #include +#if !HAVE_DECL_REALPATH +# include +#endif + +#ifdef WITH_LIBSYSTEMD +# include +/* upsnotify() debug-logs its reports; a watchdog ping is something we + * try to send often so report it just once (whether enabled or not) */ +static int upsnotify_reported_watchdog_systemd = 0; +/* Similarly for only reporting once if the notification subsystem is disabled */ +static int upsnotify_reported_disabled_systemd = 0; +# ifndef DEBUG_SYSTEMD_WATCHDOG +/* Define this to 1 for lots of spam at debug level 6, and ignoring WATCHDOG_PID + * so trying to post reports anyway if WATCHDOG_USEC is valid */ +# define DEBUG_SYSTEMD_WATCHDOG 0 +# endif +#endif +/* Similarly for only reporting once if the notification subsystem is not built-in */ +static int upsnotify_reported_disabled_notech = 0; +static int upsnotify_report_verbosity = -1; /* the reason we define UPS_VERSION as a static string, rather than a macro, is to make dependency tracking easier (only common.o depends on nut_version_macro.h), and also to prevent all sources from having to be recompiled each time the version changes (they only need to be re-linked). */ -#if DMFREINDEXER_MAKECHECK +#if defined DMFREINDEXER_MAKECHECK && DMFREINDEXER_MAKECHECK # define NUT_VERSION_MACRO "custom build" #else # include "nut_version.h" @@ -45,7 +72,7 @@ const char *UPS_VERSION = NUT_VERSION_MACRO; /* Know which bitness we were built for, * to adjust the search paths for get_libname() */ #include "nut_stdint.h" -#if UINTPTR_MAX == 0xffffffffffffffffULL +#if defined(UINTPTR_MAX) && (UINTPTR_MAX + 0) == 0xffffffffffffffffULL # define BUILD_64 1 #else # ifdef BUILD_64 @@ -57,7 +84,7 @@ const char *UPS_VERSION = NUT_VERSION_MACRO; #include #include #include -pid_t get_max_pid_t() +pid_t get_max_pid_t(void) { #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic push @@ -90,6 +117,8 @@ pid_t get_max_pid_t() int nut_log_level = 0; static int upslog_flags = UPSLOG_STDERR; + static struct timeval upslog_start = { 0, 0 }; + static void xbit_set(int *val, int flag) { *val |= flag; @@ -115,6 +144,7 @@ void syslogbit_set(void) /* get the syslog ready for us */ void open_syslog(const char *progname) { +#ifndef WIN32 int opt; opt = LOG_PID; @@ -154,7 +184,7 @@ void open_syslog(const char *progname) setlogmask(LOG_UPTO(LOG_DEBUG)); /* debug-level messages */ break; default: - fatalx(EXIT_FAILURE, "Invalid log level threshold"); + fatalx(EXIT_FAILURE, "Invalid log level threshold"); #else case 0: break; @@ -163,11 +193,15 @@ void open_syslog(const char *progname) break; #endif } +#else + EventLogName = progname; +#endif } /* close ttys and become a daemon */ void background(void) { +#ifndef WIN32 int pid; if ((pid = fork()) < 0) @@ -176,35 +210,79 @@ void background(void) xbit_set(&upslog_flags, UPSLOG_SYSLOG); xbit_clear(&upslog_flags, UPSLOG_STDERR); - close(0); - close(1); - close(2); - - if (pid != 0) - _exit(EXIT_SUCCESS); /* parent */ + if (pid != 0) { + /* parent */ + /* these are typically fds 0-2: */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + _exit(EXIT_SUCCESS); + } /* child */ - /* make fds 0-2 point somewhere defined */ - if (open("/dev/null", O_RDWR) != 0) - fatal_with_errno(EXIT_FAILURE, "open /dev/null"); + /* make fds 0-2 (typically) point somewhere defined */ +#ifdef HAVE_DUP2 + /* system can close (if needed) and (re-)open a specific FD number */ + if (1) { /* scoping */ + TYPE_FD devnull = open("/dev/null", O_RDWR); + if (devnull < 0) + fatal_with_errno(EXIT_FAILURE, "open /dev/null"); + + if (dup2(devnull, STDIN_FILENO) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); + if (dup2(devnull, STDOUT_FILENO) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDOUT"); + if (dup2(devnull, STDERR_FILENO) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDERR"); + + close(devnull); + } +#else +# ifdef HAVE_DUP + /* opportunistically duplicate to the "lowest-available" FD number */ + close(STDIN_FILENO); + if (open("/dev/null", O_RDWR) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); + + close(STDOUT_FILENO); + if (dup(STDIN_FILENO) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "dup /dev/null as STDOUT"); + + close(STDERR_FILENO); + if (dup(STDIN_FILENO) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "dup /dev/null as STDERR"); +# else + close(STDIN_FILENO); + if (open("/dev/null", O_RDWR) != STDIN_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDIN"); - if (dup(0) == -1) - fatal_with_errno(EXIT_FAILURE, "dup"); + close(STDOUT_FILENO); + if (open("/dev/null", O_RDWR) != STDOUT_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDOUT"); - if (dup(0) == -1) - fatal_with_errno(EXIT_FAILURE, "dup"); + close(STDERR_FILENO); + if (open("/dev/null", O_RDWR) != STDERR_FILENO) + fatal_with_errno(EXIT_FAILURE, "re-open /dev/null as STDERR"); +# endif +#endif #ifdef HAVE_SETSID setsid(); /* make a new session to dodge signals */ #endif +#else /* WIN32 */ + xbit_set(&upslog_flags, UPSLOG_SYSLOG); + xbit_clear(&upslog_flags, UPSLOG_STDERR); +#endif + upslogx(LOG_INFO, "Startup successful"); } /* do this here to keep pwd/grp stuff out of the main files */ struct passwd *get_user_pwent(const char *name) { +#ifndef WIN32 struct passwd *r; errno = 0; if ((r = getpwnam(name))) @@ -214,9 +292,12 @@ struct passwd *get_user_pwent(const char *name) some implementations of getpwnam() do not set errno when this happens. */ if (errno == 0) - fatalx(EXIT_FAILURE, "user %s not found", name); + fatalx(EXIT_FAILURE, "OS user %s not found", name); else fatal_with_errno(EXIT_FAILURE, "getpwnam(%s)", name); +#else + NUT_UNUSED_VARIABLE(name); +#endif /* WIN32 */ #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN) ) #pragma GCC diagnostic push @@ -248,11 +329,34 @@ struct passwd *get_user_pwent(const char *name) /* change to the user defined in the struct */ void become_user(struct passwd *pw) { +#ifndef WIN32 /* if we can't switch users, then don't even try */ - if ((geteuid() != 0) && (getuid() != 0)) + intmax_t initial_uid = getuid(); + intmax_t initial_euid = geteuid(); + + if (!pw) { + upsdebugx(1, "Can not become_user(), skipped"); return; + } - if (getuid() == 0) + if ((initial_euid != 0) && (initial_uid != 0)) { + intmax_t initial_gid = getgid(); + if (initial_euid == (intmax_t)pw->pw_uid + || initial_uid == (intmax_t)pw->pw_uid + ) { + upsdebugx(1, "No need to become_user(%s): " + "already UID=%jd GID=%jd", + pw->pw_name, initial_uid, initial_gid); + } else { + upsdebugx(1, "Can not become_user(%s): " + "not root initially, " + "remaining UID=%jd GID=%jd", + pw->pw_name, initial_uid, initial_gid); + } + return; + } + + if (initial_uid == 0) if (seteuid(0)) fatal_with_errno(EXIT_FAILURE, "getuid gave 0, but seteuid(0) failed"); @@ -264,6 +368,13 @@ void become_user(struct passwd *pw) if (setuid(pw->pw_uid) == -1) fatal_with_errno(EXIT_FAILURE, "setuid"); + + upsdebugx(1, "Succeeded to become_user(%s): now UID=%jd GID=%jd", + pw->pw_name, (intmax_t)getuid(), (intmax_t)getgid()); +#else + upsdebugx(1, "Can not become_user(%s): not implemented on this platform", + pw ? pw->pw_name : ""); +#endif } /* drop down into a directory and throw away pointers to the old path */ @@ -272,18 +383,50 @@ void chroot_start(const char *path) if (chdir(path)) fatal_with_errno(EXIT_FAILURE, "chdir(%s)", path); +#ifndef WIN32 if (chroot(path)) fatal_with_errno(EXIT_FAILURE, "chroot(%s)", path); +#else + upsdebugx(1, "Can not chroot into %s: not implemented on this platform", path); +#endif + if (chdir("/")) fatal_with_errno(EXIT_FAILURE, "chdir(/)"); +#ifndef WIN32 upsdebugx(1, "chrooted into %s", path); +#endif } +#ifdef WIN32 +/* In WIN32 all non binaries files (namely configuration and PID files) + are retrieved relative to the path of the binary itself. + So this function fill "dest" with the full path to "relative_path" + depending on the .exe path */ +char * getfullpath(char * relative_path) +{ + char buf[MAX_PATH]; + if ( GetModuleFileName(NULL, buf, sizeof(buf)) == 0 ) { + return NULL; + } + + /* remove trailing executable name and its preceeding slash */ + char * last_slash = strrchr(buf, '\\'); + *last_slash = '\0'; + + if( relative_path ) { + strncat(buf, relative_path, sizeof(buf) - 1); + } + + return(xstrdup(buf)); +} +#endif + /* drop off a pidfile for this process */ void writepid(const char *name) { +#ifndef WIN32 char fn[SMALLBUF]; FILE *pidf; mode_t mask; @@ -298,71 +441,145 @@ 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 %" PRIdMAX " into %s", pid, fn); + fprintf(pidf, "%" PRIdMAX "\n", pid); fclose(pidf); } else { upslog_with_errno(LOG_NOTICE, "writepid: fopen %s", fn); } umask(mask); +#else + NUT_UNUSED_VARIABLE(name); +#endif } -/* open pidfn, get the pid, then send it sig */ +/* send sig to pid, returns -1 for error, or + * zero for a successfully sent signal + */ +int sendsignalpid(pid_t pid, int sig) +{ +#ifndef WIN32 + int ret; + + if (pid < 2 || pid > get_max_pid_t()) { + upslogx(LOG_NOTICE, + "Ignoring invalid pid number %" PRIdMAX, + (intmax_t) pid); + return -1; + } + + /* see if this is going to work first - does the process exist? */ + ret = kill(pid, 0); + + if (ret < 0) { + perror("kill"); + return -1; + } + + if (sig != 0) { + /* now actually send it */ + ret = kill(pid, sig); + + if (ret < 0) { + perror("kill"); + return -1; + } + } + + return 0; +#else + NUT_UNUSED_VARIABLE(pid); + NUT_UNUSED_VARIABLE(sig); + upslogx(LOG_ERR, + "%s: not implemented for Win32 and " + "should not have been called directly!", + __func__); + return -1; +#endif +} + +/* 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; + intmax_t _pid; + + if (!buf) { + upsdebugx(6, "%s: called with NULL input", __func__); + return pid; + } + + /* assuming 10 digits for a long */ + _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); + } + + return pid; +} + +/* open pidfn, get the pid, then send it sig + * returns negative codes for errors, or + * zero for a successfully sent signal + */ +#ifndef WIN32 int sendsignalfn(const char *pidfn, int sig) { char buf[SMALLBUF]; FILE *pidf; pid_t pid = -1; - int ret; + int ret = -1; pidf = fopen(pidfn, "r"); if (!pidf) { upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); - return -1; + 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? + */ - { /* 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); - } - } + /* this method actively reports errors, if any */ + pid = parsepid(buf); - if (pid < 2) { - upslogx(LOG_NOTICE, "Ignoring invalid pid number %" PRIdMAX, (intmax_t) pid); - fclose(pidf); - return -1; + if (pid >= 0) { + /* this method actively reports errors, if any */ + ret = sendsignalpid(pid, sig); } - /* see if this is going to work first */ - ret = kill(pid, 0); + fclose(pidf); + return ret; +} - if (ret < 0) { - perror("kill"); - fclose(pidf); - return -1; - } +#else /* => WIN32 */ - /* now actually send it */ - ret = kill(pid, sig); +int sendsignalfn(const char *pidfn, const char * sig) +{ + BOOL ret; - if (ret < 0) { - perror("kill"); - fclose(pidf); + ret = send_to_named_pipe(pidfn, sig); + + if (ret != 0) { return -1; } - fclose(pidf); return 0; } +#endif /* WIN32 */ int snprintfcat(char *dst, size_t size, const char *fmt, ...) { @@ -414,6 +631,7 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) } /* lazy way to send a signal if the program uses the PIDPATH */ +#ifndef WIN32 int sendsignal(const char *progname, int sig) { char fn[SMALLBUF]; @@ -422,20 +640,633 @@ int sendsignal(const char *progname, int sig) return sendsignalfn(fn, sig); } +#else +int sendsignal(const char *progname, const char * sig) +{ + return sendsignalfn(progname, sig); +} +#endif const char *xbasename(const char *file) { +#ifndef WIN32 const char *p = strrchr(file, '/'); +#else + const char *p = strrchr(file, '\\'); + const char *r = strrchr(file, '/'); + /* if not found, try '/' */ + if( r > p ) { + p = r; + } +#endif if (p == NULL) return file; return p + 1; } -static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) +/* Based on https://www.gnu.org/software/libc/manual/html_node/Calculating-Elapsed-Time.html + * modified for a syntax similar to difftime() + */ +double difftimeval(struct timeval x, struct timeval y) { - int ret; + struct timeval result; + double d; + + /* Code below assumes that tv_sec is signed (time_t), + * but tv_usec is not necessarily */ + /* Perform the carry for the later subtraction by updating y. */ + if (x.tv_usec < y.tv_usec) { + intmax_t numsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; + y.tv_usec -= 1000000 * numsec; + y.tv_sec += numsec; + } + + if (x.tv_usec - y.tv_usec > 1000000) { + intmax_t numsec = (x.tv_usec - y.tv_usec) / 1000000; + y.tv_usec += 1000000 * numsec; + y.tv_sec -= numsec; + } + + /* Compute the time remaining to wait. + * tv_usec is certainly positive. */ + result.tv_sec = x.tv_sec - y.tv_sec; + result.tv_usec = x.tv_usec - y.tv_usec; + + d = 0.000001 * (double)(result.tv_usec) + (double)(result.tv_sec); + return d; +} + +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC +/* From https://github.com/systemd/systemd/blob/main/src/basic/time-util.c + * and https://github.com/systemd/systemd/blob/main/src/basic/time-util.h + */ +typedef uint64_t usec_t; +typedef uint64_t nsec_t; +#define PRI_NSEC PRIu64 +#define PRI_USEC PRIu64 + +#define USEC_INFINITY ((usec_t) UINT64_MAX) +#define NSEC_INFINITY ((nsec_t) UINT64_MAX) + +#define MSEC_PER_SEC 1000ULL +#define USEC_PER_SEC ((usec_t) 1000000ULL) +#define USEC_PER_MSEC ((usec_t) 1000ULL) +#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) +#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) +#define NSEC_PER_USEC ((nsec_t) 1000ULL) + +# if defined(WITH_LIBSYSTEMD) && (WITH_LIBSYSTEMD) && !(defined(WITHOUT_LIBSYSTEMD) && (WITHOUT_LIBSYSTEMD)) && defined(HAVE_SD_NOTIFY) && (HAVE_SD_NOTIFY) +/* Limited to upsnotify() use-cases below, currently */ +static usec_t timespec_load(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec < 0 || ts->tv_nsec < 0) + return USEC_INFINITY; + + if ((usec_t) ts->tv_sec > (UINT64_MAX - ((uint64_t)(ts->tv_nsec) / NSEC_PER_USEC)) / USEC_PER_SEC) + return USEC_INFINITY; + + return + (usec_t) ts->tv_sec * USEC_PER_SEC + + (usec_t) ts->tv_nsec / NSEC_PER_USEC; +} + +/* Not used, currently -- maybe later */ +/* +static nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec < 0 || ts->tv_nsec < 0) + return NSEC_INFINITY; + + if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) + return NSEC_INFINITY; + + return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; +} +*/ +# endif /* WITH_LIBSYSTEMD && HAVE_SD_NOTIFY && !WITHOUT_LIBSYSTEMD */ + +double difftimespec(struct timespec x, struct timespec y) +{ + struct timespec result; + double d; + + /* Code below assumes that tv_sec is signed (time_t), + * but tv_nsec is not necessarily */ + /* Perform the carry for the later subtraction by updating y. */ + if (x.tv_nsec < y.tv_nsec) { + intmax_t numsec = (y.tv_nsec - x.tv_nsec) / 1000000000L + 1; + y.tv_nsec -= 1000000000L * numsec; + y.tv_sec += numsec; + } + + if (x.tv_nsec - y.tv_nsec > 1000000) { + intmax_t numsec = (x.tv_nsec - y.tv_nsec) / 1000000000L; + y.tv_nsec += 1000000000L * numsec; + y.tv_sec -= numsec; + } + + /* Compute the time remaining to wait. + * tv_nsec is certainly positive. */ + result.tv_sec = x.tv_sec - y.tv_sec; + result.tv_nsec = x.tv_nsec - y.tv_nsec; + + d = 0.000000001 * (double)(result.tv_nsec) + (double)(result.tv_sec); + return d; +} +#endif /* HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC */ + +/* Send (daemon) state-change notifications to an + * external service management framework such as systemd + */ +int upsnotify(upsnotify_state_t state, const char *fmt, ...) +{ + int ret = -127; + va_list va; char buf[LARGEBUF]; + char msgbuf[LARGEBUF]; + size_t msglen = 0; + +#if defined(WITH_LIBSYSTEMD) && (WITH_LIBSYSTEMD) && !(defined(WITHOUT_LIBSYSTEMD) && (WITHOUT_LIBSYSTEMD)) +# ifdef HAVE_SD_NOTIFY +# if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC + /* In current systemd, this is only used for RELOADING/READY after + * a reload action for Type=notify-reload; for more details see + * https://github.com/systemd/systemd/blob/main/src/core/service.c#L2618 + */ + struct timespec monoclock_ts; + int got_monoclock = clock_gettime(CLOCK_MONOTONIC, &monoclock_ts); +# endif /* HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC */ +# endif /* HAVE_SD_NOTIFY */ +#endif /* WITH_LIBSYSTEMD */ + + /* Were we asked to be quiet on the console? */ + if (upsnotify_report_verbosity < 0) { + char *quiet_init = getenv("NUT_QUIET_INIT_UPSNOTIFY"); + if (quiet_init == NULL) { + /* No envvar, default is to inform once on the console */ + upsnotify_report_verbosity = 0; + } else { + /* Envvar is set, does it tell us to be quiet? + * NOTE: Empty also means "yes" */ + if (*quiet_init == '\0' + || (strcasecmp(quiet_init, "true") + && strcasecmp(quiet_init, "yes") + && strcasecmp(quiet_init, "on") + && strcasecmp(quiet_init, "1") ) + ) { + upsdebugx(1, + "NUT_QUIET_INIT_UPSNOTIFY='%s' value " + "was not recognized, ignored", + quiet_init); + upsnotify_report_verbosity = 0; + } else { + /* Avoid the verbose message below + * (only seen with non-zero debug) */ + upsnotify_report_verbosity = 1; + } + } + } + + /* Prepare the message (if any) as a string */ + msgbuf[0] = '\0'; + if (fmt) { + va_start(va, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* generic message... */ + ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, va); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + va_end(va); + + if ((ret < 0) || (ret >= (int) sizeof(msgbuf))) { + syslog(LOG_WARNING, + "%s (%s:%d): vsnprintf needed more than %" PRIuSIZE " bytes: %d", + __func__, __FILE__, __LINE__, sizeof(msgbuf), ret); + } else { + msglen = strlen(msgbuf); + } + /* Reset for actual notification processing below */ + ret = -127; + } + +#if defined(WITH_LIBSYSTEMD) && (WITH_LIBSYSTEMD) +# if defined(WITHOUT_LIBSYSTEMD) && (WITHOUT_LIBSYSTEMD) + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(msglen); + if (!upsnotify_reported_disabled_systemd) + upsdebugx(upsnotify_report_verbosity, + "%s: notify about state %i with libsystemd: " + "skipped for libcommonclient build, " + "will not spam more about it", __func__, state); + upsnotify_reported_disabled_systemd = 1; +# else + if (!getenv("NOTIFY_SOCKET")) { + if (!upsnotify_reported_disabled_systemd) + upsdebugx(upsnotify_report_verbosity, + "%s: notify about state %i with libsystemd: " + "was requested, but not running as a service unit now, " + "will not spam more about it", + __func__, state); + upsnotify_reported_disabled_systemd = 1; + } else { +# ifdef HAVE_SD_NOTIFY + char monoclock_str[SMALLBUF]; + monoclock_str[0] = '\0'; +# if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC + if (got_monoclock == 0) { + usec_t monots = timespec_load(&monoclock_ts); + ret = snprintf(monoclock_str + 1, sizeof(monoclock_str) - 1, "MONOTONIC_USEC=%" PRI_USEC, monots); + if ((ret < 0) || (ret >= (int) sizeof(monoclock_str) - 1)) { + syslog(LOG_WARNING, + "%s (%s:%d): snprintf needed more than %" PRIuSIZE " bytes: %d", + __func__, __FILE__, __LINE__, sizeof(monoclock_str), ret); + msglen = 0; + } else { + monoclock_str[0] = '\n'; + } + } +# endif /* HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC */ + +# if ! DEBUG_SYSTEMD_WATCHDOG + if (state != NOTIFY_STATE_WATCHDOG || !upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: notify about state %i with libsystemd: use sd_notify()", __func__, state); + + /* https://www.freedesktop.org/software/systemd/man/sd_notify.html */ + if (msglen) { + ret = snprintf(buf, sizeof(buf), "STATUS=%s", msgbuf); + if ((ret < 0) || (ret >= (int) sizeof(buf))) { + syslog(LOG_WARNING, + "%s (%s:%d): snprintf needed more than %" PRIuSIZE " bytes: %d", + __func__, __FILE__, __LINE__, sizeof(buf), ret); + msglen = 0; + } else { + msglen = (size_t)ret; + } + } + + switch (state) { + case NOTIFY_STATE_READY: + ret = snprintf(buf + msglen, sizeof(buf) - msglen, + "%sREADY=1%s", + msglen ? "\n" : "", + monoclock_str); + break; + + case NOTIFY_STATE_READY_WITH_PID: + if (1) { /* scoping */ + char pidbuf[SMALLBUF]; + if (snprintf(pidbuf, sizeof(pidbuf), "%lu", (unsigned long) getpid())) { + ret = snprintf(buf + msglen, sizeof(buf) - msglen, + "%sREADY=1\n" + "MAINPID=%s%s", + msglen ? "\n" : "", + pidbuf, + monoclock_str); + upsdebugx(6, "%s: notifying systemd about MAINPID=%s", + __func__, pidbuf); + /* https://github.com/systemd/systemd/issues/25961 + * Reset the WATCHDOG_PID so we know this is the + * process we want to post pings from! + */ + unsetenv("WATCHDOG_PID"); + setenv("WATCHDOG_PID", pidbuf, 1); + } else { + upsdebugx(6, "%s: NOT notifying systemd about MAINPID, " + "got an error stringifying it; processing as " + "plain NOTIFY_STATE_READY", + __func__); + ret = snprintf(buf + msglen, sizeof(buf) - msglen, + "%sREADY=1%s", + msglen ? "\n" : "", + monoclock_str); + /* TODO: Maybe revise/drop this tweak if + * loggers other than systemd are used: */ + state = NOTIFY_STATE_READY; + } + } + break; + + case NOTIFY_STATE_RELOADING: + ret = snprintf(buf + msglen, sizeof(buf) - msglen, "%s%s%s", + msglen ? "\n" : "", + "RELOADING=1", + monoclock_str); + break; + + case NOTIFY_STATE_STOPPING: + ret = snprintf(buf + msglen, sizeof(buf) - msglen, "%s%s", + msglen ? "\n" : "", + "STOPPING=1"); + break; + + case NOTIFY_STATE_STATUS: + /* Only send a text message per "fmt" */ + if (!msglen) { + upsdebugx(6, "%s: failed to notify about status: none provided", __func__); + ret = -1; + } else { + ret = (int)msglen; + } + break; + + case NOTIFY_STATE_WATCHDOG: + /* Ping the framework that we are still alive */ + if (1) { /* scoping */ + int postit = 0; + +# ifdef HAVE_SD_WATCHDOG_ENABLED + uint64_t to = 0; + postit = sd_watchdog_enabled(0, &to); + + if (postit < 0) { +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: sd_enabled_watchdog query failed: %s", + __func__, strerror(postit)); + } else { +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd || postit > 0) +# else + if (postit > 0) +# endif + upsdebugx(6, "%s: sd_enabled_watchdog query returned: %d " + "(%" PRIu64 "msec remain)", + __func__, postit, to); + } +# endif + + if (postit < 1) { + char *s = getenv("WATCHDOG_USEC"); +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: WATCHDOG_USEC=%s", __func__, s); + if (s && *s) { + long l = strtol(s, (char **)NULL, 10); + if (l > 0) { + pid_t wdpid = parsepid(getenv("WATCHDOG_PID")); + if (wdpid == (pid_t)-1 || wdpid == getpid()) { +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: can post: WATCHDOG_PID=%li", + __func__, (long)wdpid); + postit = 1; + } else { +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: watchdog is configured, " + "but not for this process: " + "WATCHDOG_PID=%li", + __func__, (long)wdpid); +# if DEBUG_SYSTEMD_WATCHDOG + /* Just try to post - at worst, systemd + * NotifyAccess will prohibit the message. + * The envvar simply helps child processes + * know they should not spam the watchdog + * handler (usually only MAINPID should): + * https://github.com/systemd/systemd/issues/25961#issuecomment-1373947907 + */ + postit = 1; +# else + postit = 0; +# endif + } + } + } + } + + if (postit > 0) { + ret = snprintf(buf + msglen, sizeof(buf) - msglen, "%s%s", + msglen ? "\n" : "", + "WATCHDOG=1"); + } else if (postit == 0) { +# if ! DEBUG_SYSTEMD_WATCHDOG + if (!upsnotify_reported_watchdog_systemd) +# endif + upsdebugx(6, "%s: failed to tickle the watchdog: not enabled for this unit", __func__); + ret = -126; + } + } + 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: + if (!msglen) { + upsdebugx(6, "%s: unknown state and no status message provided", __func__); + ret = -1; + } else { + upsdebugx(6, "%s: unknown state but have a status message provided", __func__); + ret = (int)msglen; + } +#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 ((ret < 0) || (ret >= (int) sizeof(buf))) { + /* Refusal to send the watchdog ping is not an error to report */ + if ( !(ret == -126 && (state == NOTIFY_STATE_WATCHDOG)) ) { + syslog(LOG_WARNING, + "%s (%s:%d): snprintf needed more than %" PRIuSIZE " bytes: %d", + __func__, __FILE__, __LINE__, sizeof(buf), ret); + } + ret = -1; + } else { + upsdebugx(6, "%s: posting sd_notify: %s", __func__, buf); + msglen = (size_t)ret; + ret = sd_notify(0, buf); + if (ret > 0 && state == NOTIFY_STATE_READY_WITH_PID) { + /* Usually we begin the main loop just after this + * and post a watchdog message but systemd did not + * yet prepare to handle us */ + upsdebugx(6, "%s: wait for NOTIFY_STATE_READY_WITH_PID to be handled by systemd", __func__); +# ifdef HAVE_SD_NOTIFY_BARRIER + sd_notify_barrier(0, UINT64_MAX); +# else + usleep(3 * 1000000); +# endif + } + } + +# else /* not HAVE_SD_NOTIFY: */ + /* FIXME: Try to fork and call systemd-notify helper program */ + upsdebugx(6, "%s: notify about state %i with libsystemd: lacking sd_notify()", __func__, state); + ret = -127; +# endif /* HAVE_SD_NOTIFY */ + } +# endif /* if not WITHOUT_LIBSYSTEMD (explicit avoid) */ +#else /* not WITH_LIBSYSTEMD */ + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(msglen); +#endif /* WITH_LIBSYSTEMD */ + + if (ret < 0 +#if defined(WITH_LIBSYSTEMD) && (WITH_LIBSYSTEMD) && !(defined(WITHOUT_LIBSYSTEMD) && (WITHOUT_LIBSYSTEMD)) && (defined(HAVE_SD_NOTIFY) && HAVE_SD_NOTIFY) +# if ! DEBUG_SYSTEMD_WATCHDOG + && (!upsnotify_reported_watchdog_systemd || (state != NOTIFY_STATE_WATCHDOG)) +# endif +#endif + ) { + if (ret == -127) { + if (!upsnotify_reported_disabled_notech) + upsdebugx(upsnotify_report_verbosity, + "%s: failed to notify about state %i: " + "no notification tech defined, " + "will not spam more about it", + __func__, state); + upsnotify_reported_disabled_notech = 1; + } else { + upsdebugx(6, + "%s: failed to notify about state %i", + __func__, state); + } + } + +#if defined(WITH_LIBSYSTEMD) && (WITH_LIBSYSTEMD) +# if ! DEBUG_SYSTEMD_WATCHDOG + if (state == NOTIFY_STATE_WATCHDOG && !upsnotify_reported_watchdog_systemd) { + upsdebugx(upsnotify_report_verbosity, + "%s: logged the systemd watchdog situation once, " + "will not spam more about it", __func__); + upsnotify_reported_watchdog_systemd = 1; + } +# endif +#endif + + return ret; +} + +void nut_report_config_flags(void) +{ + /* Roughly similar to upslogx() but without the buffer-size limits and + * timestamp/debug-level prefixes. Only printed if debug (any) is on. + * Depending on amount of configuration tunables involved by a particular + * build of NUT, the string can be quite long (over 1KB). + */ + const char *acinit_ver = NULL; + /* Pass these as variables to avoid warning about never reaching one + * of compiled codepaths: */ + const char *compiler_ver = CC_VERSION; + const char *config_flags = CONFIG_FLAGS; + struct timeval now; + + if (nut_debug_level < 1) + return; + + /* Only report git revision if NUT_VERSION_MACRO in nut_version.h aka + * UPS_VERSION here is remarkably different from PACKAGE_VERSION from + * configure.ac AC_INIT() -- which may be e.g. "2.8.0.1" although some + * distros, especially embedders, tend to place their product IDs here). + * The macro may be that fixed version or refer to git source revision, + * as decided when generating nut_version.h (and if it was re-generated + * in case of rebuilds while developers are locally iterating -- this + * may be disabled for faster local iterations at a cost of a little lie). + */ + if (PACKAGE_VERSION && UPS_VERSION && + (strlen(UPS_VERSION) < 12 || !strstr(UPS_VERSION, PACKAGE_VERSION)) + ) { + /* If UPS_VERSION is too short (so likely a static string + * from configure.ac AC_INIT() -- although some distros, + * especially embedders, tend to place their product IDs here), + * or if PACKAGE_VERSION *is NOT* a substring of it: */ + acinit_ver = PACKAGE_VERSION; + } + + /* NOTE: If changing wording here, keep in sync with configure.ac logic + * looking for CONFIG_FLAGS_DEPLOYED via "configured with flags:" string! + */ + + gettimeofday(&now, NULL); + + if (upslog_start.tv_sec == 0) { + upslog_start = now; + } + + if (upslog_start.tv_usec > now.tv_usec) { + now.tv_usec += 1000000; + now.tv_sec -= 1; + } + + if (xbit_test(upslog_flags, UPSLOG_STDERR)) { + fprintf(stderr, "%4.0f.%06ld\t[D1] Network UPS Tools version %s%s%s%s%s%s%s %s%s\n", + difftime(now.tv_sec, upslog_start.tv_sec), + (long)(now.tv_usec - upslog_start.tv_usec), + UPS_VERSION, + (acinit_ver ? " (release/snapshot of " : ""), + (acinit_ver ? acinit_ver : ""), + (acinit_ver ? ")" : ""), + (compiler_ver && *compiler_ver != '\0' ? " built with " : ""), + (compiler_ver && *compiler_ver != '\0' ? compiler_ver : ""), + (compiler_ver && *compiler_ver != '\0' ? " and" : ""), + (config_flags && *config_flags != '\0' ? "configured with flags: " : "configured all by default guesswork"), + (config_flags && *config_flags != '\0' ? config_flags : "") + ); +#ifdef WIN32 + fflush(stderr); +#endif + } + + /* NOTE: May be ignored or truncated by receiver if that syslog server + * (and/or OS sender) does not accept messages of such length */ + if (xbit_test(upslog_flags, UPSLOG_SYSLOG)) + syslog(LOG_DEBUG, "Network UPS Tools version %s%s%s%s%s%s%s %s%s", + UPS_VERSION, + (acinit_ver ? " (release/snapshot of " : ""), + (acinit_ver ? acinit_ver : ""), + (acinit_ver ? ")" : ""), + (compiler_ver && *compiler_ver != '\0' ? " built with " : ""), + (compiler_ver && *compiler_ver != '\0' ? compiler_ver : ""), + (compiler_ver && *compiler_ver != '\0' ? " and" : ""), + (config_flags && *config_flags != '\0' ? "configured with flags: " : "configured all by default guesswork"), + (config_flags && *config_flags != '\0' ? config_flags : "") + ); +} + +static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) +{ + int ret, errno_orig = errno; + size_t bufsize = LARGEBUF; + char *buf = xcalloc(sizeof(char), bufsize); + + /* Be pedantic about our limitations */ + bufsize *= sizeof(char); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic push @@ -451,7 +1282,71 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) #pragma clang diagnostic ignored "-Wformat-nonliteral" #pragma clang diagnostic ignored "-Wformat-security" #endif - ret = vsnprintf(buf, sizeof(buf), fmt, va); + /* Note: errors here can reset errno, + * so errno_orig is stashed beforehand */ + do { + ret = vsnprintf(buf, bufsize, fmt, va); + + if ((ret < 0) || ((uintmax_t)ret >= (uintmax_t)bufsize)) { + /* Try to adjust bufsize until we can print the + * whole message. Note that standards only require + * up to 4095 bytes to be manageable in printf-like + * methods: + * The number of characters that can be produced + * by any single conversion shall be at least 4095. + * C17dr Ā§ 7.21.6.1 15 + * In general, vsnprintf() is not specified to set + * errno on any condition (or to not implement a + * larger limit). Select implementations may do so + * though. + * Based on https://stackoverflow.com/a/72981237/4715872 + */ + if (bufsize < SIZE_MAX/2) { + size_t newbufsize = bufsize*2; + if (ret > 0) { + /* Be generous, we snprintfcat() some + * suffixes, prefix a timestamp, etc. */ + if (((uintmax_t)ret) > (SIZE_MAX - LARGEBUF)) { + goto vupslog_too_long; + } + newbufsize = (size_t)ret + LARGEBUF; + } /* else: errno, e.g. ERANGE printing: + * "...(34 => Result too large)" */ + if (nut_debug_level > 0) { + fprintf(stderr, "WARNING: vupslog: " + "vsnprintf needed more than %" + PRIuSIZE " bytes: %d (%d => %s)," + " extending to %" PRIuSIZE "\n", + bufsize, ret, + errno, strerror(errno), + newbufsize); + } + bufsize = newbufsize; + buf = xrealloc(buf, bufsize); + continue; + } + } else { + /* All fits well now; majority of use-cases should + * have nailed this on first try (envvar prints of + * longer fully-qualified PATHs, compilation settings + * reports etc. may need more). Even a LARGEBUF may + * still overflow some older syslog buffers and would + * be truncated there. At least stderr would see as + * complete a picture as we can give it. + */ + break; + } + + /* Arbitrary limit, gotta stop somewhere */ + if (bufsize > LARGEBUF * 64) { +vupslog_too_long: + syslog(LOG_WARNING, "vupslog: vsnprintf needed " + "more than %" PRIuSIZE " bytes; logged " + "output can be truncated", + bufsize); + break; + } + } while(1); #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -459,58 +1354,102 @@ static void vupslog(int priority, const char *fmt, va_list va, int use_strerror) #pragma GCC diagnostic pop #endif - if ((ret < 0) || (ret >= (int) sizeof(buf))) - syslog(LOG_WARNING, "vupslog: vsnprintf needed more than %d bytes", - LARGEBUF); + if (use_strerror) { +#ifdef WIN32 + LPVOID WinBuf; + DWORD WinErr = GetLastError(); +#endif - if (use_strerror) - snprintfcat(buf, sizeof(buf), ": %s", strerror(errno)); + snprintfcat(buf, bufsize, ": %s", strerror(errno_orig)); + +#ifdef WIN32 + FormatMessage( + FORMAT_MESSAGE_MAX_WIDTH_MASK | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + WinErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &WinBuf, + 0, NULL ); + + snprintfcat(buf, bufsize, " [%s]", (char *)WinBuf); + LocalFree(WinBuf); +#endif + } - if (nut_debug_level > 0) { - static struct timeval start = { 0, 0 }; + /* Note: nowadays debug level can be changed during run-time, + * so mark the starting point whenever we first try to log */ + if (upslog_start.tv_sec == 0) { struct timeval now; gettimeofday(&now, NULL); + upslog_start = now; + } - if (start.tv_sec == 0) { - start = now; - } + if (xbit_test(upslog_flags, UPSLOG_STDERR)) { + if (nut_debug_level > 0) { + struct timeval now; - if (start.tv_usec > now.tv_usec) { - now.tv_usec += 1000000; - now.tv_sec -= 1; - } + gettimeofday(&now, NULL); - fprintf(stderr, "%4.0f.%06ld\t", difftime(now.tv_sec, start.tv_sec), (long)(now.tv_usec - start.tv_usec)); - } + if (upslog_start.tv_usec > now.tv_usec) { + now.tv_usec += 1000000; + now.tv_sec -= 1; + } - if (xbit_test(upslog_flags, UPSLOG_STDERR)) - fprintf(stderr, "%s\n", buf); + /* Print all in one shot, to better avoid + * mixed lines in parallel threads */ + fprintf(stderr, "%4.0f.%06ld\t%s\n", + difftime(now.tv_sec, upslog_start.tv_sec), + (long)(now.tv_usec - upslog_start.tv_usec), + buf); + } else { + fprintf(stderr, "%s\n", buf); + } +#ifdef WIN32 + fflush(stderr); +#endif + } if (xbit_test(upslog_flags, UPSLOG_SYSLOG)) syslog(priority, "%s", buf); + free(buf); } + /* Return the default path for the directory containing configuration files */ const char * confpath(void) { - const char * path; + const char *path = getenv("NUT_CONFPATH"); - if ((path = getenv("NUT_CONFPATH")) == NULL) - path = CONFPATH; +#ifdef WIN32 + if (path == NULL) { + /* fall back to built-in pathname relative to binary/workdir */ + path = getfullpath(PATH_ETC); + } +#endif - return path; + /* We assume, here and elsewhere, that + * at least CONFPATH is always defined */ + return (path != NULL && *path != '\0') ? path : CONFPATH; } /* Return the default path for the directory containing state files */ const char * dflt_statepath(void) { - const char * path; + const char *path = getenv("NUT_STATEPATH"); - path = getenv("NUT_STATEPATH"); - if ( (path == NULL) || (*path == '\0') ) - path = STATEPATH; +#ifdef WIN32 + if (path == NULL) { + /* fall back to built-in pathname relative to binary/workdir */ + path = getfullpath(PATH_VAR_RUN); + } +#endif - return path; + /* We assume, here and elsewhere, that + * at least STATEPATH is always defined */ + return (path != NULL && *path != '\0') ? path : STATEPATH; } /* Return the alternate path for pid files, for processes running as non-root @@ -524,18 +1463,57 @@ const char * altpidpath(void) const char * path; path = getenv("NUT_ALTPIDPATH"); - if ( (path == NULL) || (*path == '\0') ) + if ( (path == NULL) || (*path == '\0') ) { path = getenv("NUT_STATEPATH"); +#ifdef WIN32 + if (path == NULL) { + /* fall back to built-in pathname relative to binary/workdir */ + path = getfullpath(PATH_VAR_RUN); + } +#endif + } + if ( (path != NULL) && (*path != '\0') ) return path; #ifdef ALTPIDPATH return ALTPIDPATH; #else -/* We assume, here and elsewhere, that at least STATEPATH is always defined */ - return STATEPATH; + /* With WIN32 in the loop, this may be more than a fallback to STATEPATH: */ + return dflt_statepath(); +#endif +} + +/* Die with a standard message if socket filename is too long */ +void check_unix_socket_filename(const char *fn) { + size_t len = strlen(fn); +#ifdef UNIX_PATH_MAX + size_t max = UNIX_PATH_MAX; +#else + size_t max = PATH_MAX; +#endif +#ifndef WIN32 + struct sockaddr_un ssaddr; + max = sizeof(ssaddr.sun_path); #endif + + if (len < max) + 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 (%" PRIuSIZE ") for 'struct sockaddr_un->sun_path' " + "on this system (%" PRIuSIZE ")", + fn, len, max); } /* logs the formatted string to any configured logging devices + the output of strerror(errno) */ @@ -586,6 +1564,7 @@ void s_upsdebug_with_errno(int level, const char *fmt, ...) { va_list va; char fmt2[LARGEBUF]; + static int NUT_DEBUG_PID = -1; /* Note: Thanks to macro wrapping, we do not quite need this * test now, but we still need the "level" value to report @@ -601,7 +1580,18 @@ void s_upsdebug_with_errno(int level, const char *fmt, ...) * can help limit this debug stream quicker, than experimentally picking ;) */ if (level > 0) { int ret; - ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt); + + if (NUT_DEBUG_PID < 0) { + NUT_DEBUG_PID = (getenv("NUT_DEBUG_PID") != NULL); + } + + if (NUT_DEBUG_PID) { + /* Note that we re-request PID every time as it can + * change during the run-time (forking etc.) */ + ret = snprintf(fmt2, sizeof(fmt2), "[D%d:%" PRIiMAX "] %s", level, (intmax_t)getpid(), fmt); + } else { + ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt); + } if ((ret < 0) || (ret >= (int) sizeof(fmt2))) { syslog(LOG_WARNING, "upsdebug_with_errno: snprintf needed more than %d bytes", LARGEBUF); @@ -631,6 +1621,7 @@ void s_upsdebugx(int level, const char *fmt, ...) { va_list va; char fmt2[LARGEBUF]; + static int NUT_DEBUG_PID = -1; if (nut_debug_level < level) return; @@ -638,7 +1629,19 @@ void s_upsdebugx(int level, const char *fmt, ...) /* See comments above in upsdebug_with_errno() - they apply here too. */ if (level > 0) { int ret; - ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt); + + if (NUT_DEBUG_PID < 0) { + NUT_DEBUG_PID = (getenv("NUT_DEBUG_PID") != NULL); + } + + if (NUT_DEBUG_PID) { + /* Note that we re-request PID every time as it can + * change during the run-time (forking etc.) */ + ret = snprintf(fmt2, sizeof(fmt2), "[D%d:%" PRIiMAX "] %s", level, (intmax_t)getpid(), fmt); + } else { + ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt); + } + if ((ret < 0) || (ret >= (int) sizeof(fmt2))) { syslog(LOG_WARNING, "upsdebugx: snprintf needed more than %d bytes", LARGEBUF); @@ -673,7 +1676,7 @@ void s_upsdebug_hex(int level, const char *msg, const void *buf, size_t len) int n; /* number of characters currently in line */ size_t i; /* number of bytes output from buffer */ - n = snprintf(line, sizeof(line), "%s: (%zu bytes) =>", msg, len); + n = snprintf(line, sizeof(line), "%s: (%" PRIuSIZE " bytes) =>", msg, len); if (n < 0) goto failed; for (i = 0; i < len; i++) { @@ -842,6 +1845,12 @@ void *xmalloc(size_t size) if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); + +#ifdef WIN32 + /* FIXME: This is what (x)calloc() is for! */ + memset(p, 0, size); +#endif + return p; } @@ -851,6 +1860,12 @@ void *xcalloc(size_t number, size_t size) if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); + +#ifdef WIN32 + /* FIXME: calloc() above should have initialized this already! */ + memset(p, 0, size * number); +#endif + return p; } @@ -865,7 +1880,14 @@ void *xrealloc(void *ptr, size_t size) char *xstrdup(const char *string) { - char *p = strdup(string); + char *p; + + if (string == NULL) { + upsdebugx(1, "%s: got null input", __func__); + return NULL; + } + + p = strdup(string); if (p == NULL) fatal_with_errno(EXIT_FAILURE, "%s", oom_msg); @@ -875,6 +1897,7 @@ char *xstrdup(const char *string) /* Read up to buflen bytes from fd and return the number of bytes read. If no data is available within d_sec + d_usec, return 0. On error, a value < 0 is returned (errno indicates error). */ +#ifndef WIN32 ssize_t select_read(const int fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec) { int ret; @@ -895,10 +1918,34 @@ ssize_t select_read(const int fd, void *buf, const size_t buflen, const time_t d return read(fd, buf, buflen); } +#else +ssize_t select_read(serial_handler_t *fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec) +{ + /* This function is only called by serial drivers right now */ + /* TODO: Assert below that resulting values fit in ssize_t range */ + /* DWORD bytes_read; */ + int res; + DWORD timeout; + COMMTIMEOUTS TOut; + + timeout = (d_sec*1000) + ((d_usec+999)/1000); + + GetCommTimeouts(fd->handle,&TOut); + TOut.ReadIntervalTimeout = MAXDWORD; + TOut.ReadTotalTimeoutMultiplier = 0; + TOut.ReadTotalTimeoutConstant = timeout; + SetCommTimeouts(fd->handle,&TOut); + + res = w32_serial_read(fd,buf,buflen,timeout); + + return res; +} +#endif /* Write up to buflen bytes to fd and return the number of bytes written. If no data is available within d_sec + d_usec, return 0. On error, a value < 0 is returned (errno indicates error). */ +#ifndef WIN32 ssize_t select_write(const int fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec) { int ret; @@ -919,7 +1966,19 @@ ssize_t select_write(const int fd, const void *buf, const size_t buflen, const t return write(fd, buf, buflen); } - +#else +/* Note: currently not implemented de-facto for Win32 */ +ssize_t select_write(serial_handler_t *fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec) +{ + NUT_UNUSED_VARIABLE(fd); + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(buflen); + NUT_UNUSED_VARIABLE(d_sec); + NUT_UNUSED_VARIABLE(d_usec); + upsdebugx(1, "WARNING: method %s() is not implemented yet for WIN32", __func__); + return 0; +} +#endif /* FIXME: would be good to get more from /etc/ld.so.conf[.d] and/or * LD_LIBRARY_PATH and a smarter dependency on build bitness; also @@ -935,11 +1994,24 @@ ssize_t select_write(const int fd, const void *buf, const size_t buflen, const t * linked against certain OS-provided libraries for accessing this or that * communications media and/or vendor protocol. */ -static const char * search_paths[] = { +static const char * search_paths_builtin[] = { /* Use the library path (and bitness) provided during ./configure first */ LIBDIR, - "/usr"LIBDIR, - "/usr/local"LIBDIR, + "/usr"LIBDIR, /* Note: this can lead to bogus strings like */ + "/usr/local"LIBDIR, /* "/usr/usr/lib" which would be ignored quickly */ +/* TOTHINK: Should AUTOTOOLS_* specs also be highly preferred? + * Currently they are listed after the "legacy" hard-coded paths... + */ +#ifdef MULTIARCH_TARGET_ALIAS +# ifdef BUILD_64 + "/usr/lib/64/" MULTIARCH_TARGET_ALIAS, + "/usr/lib64/" MULTIARCH_TARGET_ALIAS, + "/lib/64/" MULTIARCH_TARGET_ALIAS, + "/lib64/" MULTIARCH_TARGET_ALIAS, +# endif /* MULTIARCH_TARGET_ALIAS && BUILD_64 */ + "/usr/lib/" MULTIARCH_TARGET_ALIAS, + "/lib/" MULTIARCH_TARGET_ALIAS, +#endif /* MULTIARCH_TARGET_ALIAS */ #ifdef BUILD_64 /* Fall back to explicit preference of 64-bit paths as named on some OSes */ "/usr/lib/64", @@ -983,43 +2055,539 @@ static const char * search_paths[] = { "/usr/lib/gcc/" AUTOTOOLS_BUILD_ALIAS, # endif # endif +#endif +#ifdef WIN32 + /* TODO: Track the binary program name (many platform-specific solutions, + * or custom one to stash argv[0] in select programs, and derive its + * dirname (with realpath and apparent path) as well as "../lib". + * Perhaps a decent fallback idea for all platforms, not just WIN32. + */ + ".", #endif NULL }; -char * get_libname(const char* base_libname) -{ +static const char ** search_paths = search_paths_builtin; + +/* free this when a NUT program ends (common library is unloaded) + * IFF it is not the built-in version. */ +static void nut_free_search_paths(void) { + if (search_paths == NULL) { + search_paths = search_paths_builtin; + return; + } + + if (search_paths != search_paths_builtin) { + size_t i; + for (i = 0; search_paths[i] != NULL; i++) { + free((char *)search_paths[i]); + } + free(search_paths); + search_paths = search_paths_builtin; + } +} + +void nut_prepare_search_paths(void) { + /* Produce the search_paths[] with minimal confusion allowing + * for faster walks and fewer logs in NUT applications: + * * only existing paths + * * discard lower-priority duplicates if a path is already listed + * + * NOTE: Currently this only trims info from search_paths_builtin[] + * but might later supplant iterations in the get_libname(), + * get_libname_in_pathset() and upsdebugx_report_search_paths() + * methods. Surely would make their code easier, but at a cost of + * probably losing detailed logging of where something came from... + */ + static int atexit_hooked = 0; + size_t count_builtin = 0, count_filtered = 0, i, j, index = 0; + const char ** filtered_search_paths; + DIR *dp; + + /* As a starting point, allow at least as many items as before */ + /* TODO: somehow extend (xrealloc?) if we mix other paths later */ + for (i = 0; search_paths_builtin[i] != NULL; i++) {} + count_builtin = i + 1; /* +1 for the NULL */ + + /* Bytes inside should all be zeroed... */ + filtered_search_paths = xcalloc(sizeof(const char *), count_builtin); + + /* FIXME: here "count_builtin" means size of filtered_search_paths[] + * and may later be more, if we would consider other data sources */ + for (i = 0; search_paths_builtin[i] != NULL && count_filtered < count_builtin; i++) { + int dupe = 0; + const char *dirname = search_paths_builtin[i]; + + if ((dp = opendir(dirname)) == NULL) { + upsdebugx(5, "%s: SKIP " + "unreachable directory #%" PRIuSIZE " : %s", + __func__, index++, dirname); + continue; + } + index++; + +#if HAVE_DECL_REALPATH + /* allocates the buffer we free() later */ + dirname = (const char *)realpath(dirname, NULL); +#endif + + /* Revise for duplicates */ + /* Note: (count_filtered == 0) means first existing dir seen, no hassle */ + for (j = 0; j < count_filtered; j++) { + if (!strcmp(filtered_search_paths[j], dirname)) { +#if HAVE_DECL_REALPATH + if (strcmp(search_paths_builtin[i], dirname)) { + /* They differ, highlight it */ + upsdebugx(5, "%s: SKIP " + "duplicate directory #%" PRIuSIZE " : %s (%s)", + __func__, index, dirname, + search_paths_builtin[i]); + } else +#endif + upsdebugx(5, "%s: SKIP " + "duplicate directory #%" PRIuSIZE " : %s", + __func__, index, dirname); + + dupe = 1; +#if HAVE_DECL_REALPATH + free((char *)dirname); +#endif + break; + } + } + + if (!dupe) { + upsdebugx(5, "%s: ADD[#%" PRIuSIZE "] " + "existing unique directory: %s", + __func__, count_filtered, dirname); +#if !HAVE_DECL_REALPATH + dirname = (const char *)xstrdup(dirname); +#endif + filtered_search_paths[count_filtered++] = dirname; + } + } + + /* If we mangled this before, forget the old result: */ + nut_free_search_paths(); + + /* Better safe than sorry: */ + filtered_search_paths[count_filtered] = NULL; + search_paths = filtered_search_paths; + + if (!atexit_hooked) { + atexit(nut_free_search_paths); + atexit_hooked = 1; + } +} + +void upsdebugx_report_search_paths(int level, int report_search_paths_builtin) { + size_t index; + char *s, *varname; + const char ** reported_search_paths = ( + report_search_paths_builtin + ? search_paths_builtin + : search_paths); + + if (nut_debug_level < level) + return; + + upsdebugx(level, "Run-time loadable library search paths used by this build of NUT:"); + + /* NOTE: Reporting order follows get_libname(), and + * while some values are individual paths, others can + * be "pathsets" (e.g. coming envvars) with certain + * platform-dependent separator characters. */ +#ifdef BUILD_64 + varname = "LD_LIBRARY_PATH_64"; +#else + varname = "LD_LIBRARY_PATH_32"; +#endif + + if (((s = getenv(varname)) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tVia %s:\t%s", varname, s); + } + + varname = "LD_LIBRARY_PATH"; + if (((s = getenv(varname)) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tVia %s:\t%s", varname, s); + } + + for (index = 0; reported_search_paths[index] != NULL; index++) + { + if (index == 0) { + upsdebugx(level, "\tNOTE: Reporting %s built-in paths:", + (report_search_paths_builtin ? "raw" + : "filtered (existing unique)")); + } + upsdebugx(level, "\tBuilt-in:\t%s", reported_search_paths[index]); + } + +#ifdef WIN32 + if (((s = getfullpath(NULL)) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tWindows near EXE:\t%s", s); + } + +# ifdef PATH_LIB + if (((s = getfullpath(PATH_LIB)) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tWindows PATH_LIB (%s):\t%s", PATH_LIB, s); + } +# endif + + if (((s = getfullpath("/../lib")) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tWindows \"lib\" dir near EXE:\t%s", s); + } + + varname = "PATH"; + if (((s = getenv(varname)) != NULL) && strlen(s) > 0) { + upsdebugx(level, "\tWindows via %s:\t%s", varname, s); + } +#endif +} + +static char * get_libname_in_dir(const char* base_libname, size_t base_libname_length, const char* dirname, int index) { + /* Implementation detail for get_libname() below. + * Returns pointer to allocated copy of the buffer + * (caller must free later) if dir has lib, + * or NULL otherwise. + * base_libname_length is optimization to not recalculate length in a loop. + * index is for search_paths[] table looping; use negative to not log dir number + */ DIR *dp; struct dirent *dirp; - int index = 0; - char *libname_path = NULL; + char *libname_path = NULL, *libname_alias = NULL; char current_test_path[LARGEBUF]; - size_t base_libname_length = strlen(base_libname); - for(index = 0 ; (search_paths[index] != NULL) && (libname_path == NULL) ; index++) + memset(current_test_path, 0, LARGEBUF); + + if ((dp = opendir(dirname)) == NULL) { + if (index >= 0) { + upsdebugx(5,"NOT looking for lib %s in unreachable directory #%d : %s", + base_libname, index, dirname); + } else { + upsdebugx(5,"NOT looking for lib %s in unreachable directory : %s", + base_libname, dirname); + } + return NULL; + } + + if (index >= 0) { + upsdebugx(2,"Looking for lib %s in directory #%d : %s", base_libname, index, dirname); + } else { + upsdebugx(2,"Looking for lib %s in directory : %s", base_libname, dirname); + } + while ((dirp = readdir(dp)) != NULL) { - memset(current_test_path, 0, LARGEBUF); +#if !HAVE_DECL_REALPATH + struct stat st; +#endif + int compres; + + upsdebugx(5,"Comparing lib %s with dirpath entry %s", base_libname, dirp->d_name); + compres = strncmp(dirp->d_name, base_libname, base_libname_length); + if (compres == 0) { + /* avoid "*.dll.a", ".so.1.2.3" etc. */ + if (dirp->d_name[base_libname_length] != '\0') { + if (!libname_alias) { + libname_alias = xstrdup(dirp->d_name); + } + continue; + } - if ((dp = opendir(search_paths[index])) == NULL) - continue; + snprintf(current_test_path, LARGEBUF, "%s/%s", dirname, dirp->d_name); +#if HAVE_DECL_REALPATH + libname_path = realpath(current_test_path, NULL); +#else + /* Just check if candidate name is (points to?) valid file */ + libname_path = NULL; + if (stat(current_test_path, &st) == 0) { + if (st.st_size > 0) { + libname_path = xstrdup(current_test_path); + } + } - upsdebugx(2,"Looking for lib %s in directory #%d : %s", base_libname, index, search_paths[index]); - while ((dirp = readdir(dp)) != NULL) - { - upsdebugx(5,"Comparing lib %s with dirpath %s", base_libname, dirp->d_name); - int compres = strncmp(dirp->d_name, base_libname, base_libname_length); - if(compres == 0) { - snprintf(current_test_path, LARGEBUF, "%s/%s", search_paths[index], dirp->d_name); - libname_path = realpath(current_test_path, NULL); - upsdebugx(2,"Candidate path for lib %s is %s (realpath %s)", base_libname, current_test_path, (libname_path!=NULL)?libname_path:"NULL"); - if (libname_path != NULL) - break; +# ifdef WIN32 + if (!libname_path) { + char *p; + for (p = current_test_path; *p != '\0' && (p - current_test_path) < LARGEBUF; p++) { + if (*p == '/') *p = '\\'; + } + upsdebugx(3, "%s: WIN32: re-checking with %s", __func__, current_test_path); + if (stat(current_test_path, &st) == 0) { + if (st.st_size > 0) { + libname_path = xstrdup(current_test_path); + } + } } + if (!libname_path && strcmp(dirname, ".") == 0 && current_test_path[0] == '.' && current_test_path[1] == '\\' && current_test_path[2] != '\0') { + /* Seems mingw stat() only works for files in current dir, + * so for others a chdir() is needed (and memorizing the + * original dir, and no threading at this moment, to be safe!) + * https://stackoverflow.com/a/66096983/4715872 + */ + upsdebugx(3, "%s: WIN32: re-checking with %s", __func__, current_test_path + 2); + if (stat(current_test_path + 2, &st) == 0) { + if (st.st_size > 0) { + libname_path = xstrdup(current_test_path + 2); + } + } + } +# endif /* WIN32 */ +#endif /* HAVE_DECL_REALPATH */ + + upsdebugx(2,"Candidate path for lib %s is %s (realpath %s)", + base_libname, current_test_path, + (libname_path!=NULL)?libname_path:"NULL"); + if (libname_path != NULL) + break; + } + } /* while iterating dir */ + + closedir(dp); + + if (libname_alias) { + if (!libname_path) { + upsdebugx(1, "Got no strong candidate path for lib %s in %s" + ", but saw seemingly related names (are you missing" + " a symbolic link, perhaps?) e.g.: %s", + base_libname, dirname, libname_alias); + } + + free(libname_alias); + } + + return libname_path; +} + +static char * get_libname_in_pathset(const char* base_libname, size_t base_libname_length, char* pathset, int *counter) +{ + /* Note: this method iterates specified pathset, + * so it increments the counter by reference. + * A copy of original pathset is used, because + * strtok() tends to modify its input! */ + char *libname_path = NULL; + char *onedir = NULL; + char* pathset_tmp; + + if (!pathset || *pathset == '\0') + return NULL; + + /* First call to tokenization passes the string, others pass NULL */ + pathset_tmp = xstrdup(pathset); + while (NULL != (onedir = strtok( (onedir ? NULL : pathset_tmp), ":" ))) { + libname_path = get_libname_in_dir(base_libname, base_libname_length, onedir, (*counter)++); + if (libname_path != NULL) + break; + } + free(pathset_tmp); + +#ifdef WIN32 + /* Note: with mingw, the ":" separator above might have been resolvable */ + pathset_tmp = xstrdup(pathset); + if (!libname_path) { + onedir = NULL; /* probably is NULL already, but better ensure this */ + while (NULL != (onedir = strtok( (onedir ? NULL : pathset_tmp), ";" ))) { + libname_path = get_libname_in_dir(base_libname, base_libname_length, onedir, (*counter)++); + if (libname_path != NULL) + break; } - closedir(dp); } + free(pathset_tmp); +#endif /* WIN32 */ - upsdebugx(1,"Looking for lib %s, found %s", - base_libname, (libname_path!=NULL)?libname_path:"NULL"); return libname_path; } + +char * get_libname(const char* base_libname) +{ + /* NOTE: Keep changes to practical search order + * synced to upsdebugx_report_search_paths() */ + int index = 0, counter = 0; + char *libname_path = NULL; + size_t base_libname_length = strlen(base_libname); + + /* Normally these envvars should not be set, but if the user insists, + * we should prefer the override... */ +#ifdef BUILD_64 + libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH_64"), &counter); + if (libname_path != NULL) { + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_64", base_libname); + goto found; + } +#else + libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH_32"), &counter); + if (libname_path != NULL) { + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH_32", base_libname); + goto found; + } +#endif + + libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("LD_LIBRARY_PATH"), &counter); + if (libname_path != NULL) { + upsdebugx(2, "Looking for lib %s, found in LD_LIBRARY_PATH", base_libname); + goto found; + } + + for (index = 0 ; (search_paths[index] != NULL) && (libname_path == NULL) ; index++) + { + libname_path = get_libname_in_dir(base_libname, base_libname_length, search_paths[index], counter++); + if (libname_path != NULL) + break; + } + +#ifdef WIN32 + /* TODO: Need a reliable cross-platform way to get the full path + * of current executable -- possibly stash it when starting NUT + * programs... consider some way for `nut-scanner` too */ + if (!libname_path) { + /* First check near the EXE (if executing it from another + * working directory) */ + libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath(NULL), counter++); + } + +# ifdef PATH_LIB + if (!libname_path) { + libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath(PATH_LIB), counter++); + } +# endif + + if (!libname_path) { + /* Resolve "lib" dir near the one with current executable ("bin" or "sbin") */ + libname_path = get_libname_in_dir(base_libname, base_libname_length, getfullpath("/../lib"), counter++); + } +#endif /* WIN32 so far */ + +#ifdef WIN32 + /* Windows-specific: DLLs can be provided by common "PATH" envvar, + * at lowest search priority though (after EXE dir, system, etc.) */ + if (!libname_path) { + upsdebugx(2, "Looking for lib %s in PATH", base_libname); + libname_path = get_libname_in_pathset(base_libname, base_libname_length, getenv("PATH"), &counter); + } +#endif /* WIN32 */ + +found: + upsdebugx(1,"Looking for lib %s, found %s", base_libname, (libname_path!=NULL)?libname_path:"NULL"); + return libname_path; +} + +/* TODO: Extend for TYPE_FD and WIN32 eventually? */ +void set_close_on_exec(int fd) { + /* prevent fd leaking to child processes */ +#ifndef FD_CLOEXEC + /* Find a way, if possible at all old platforms */ + NUT_UNUSED_VARIABLE(fd); +#else +# ifdef WIN32 + /* Find a way, if possible at all (WIN32: get INT fd from the HANDLE?) */ + NUT_UNUSED_VARIABLE(fd); +# else + fcntl(fd, F_SETFD, FD_CLOEXEC); +# endif +#endif +} + +/**** REGEX helper methods ****/ + +int strcmp_null(const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) { + return 0; + } + + if (s1 == NULL) { + return -1; + } + + if (s2 == NULL) { + return 1; + } + + return strcmp(s1, s2); +} + +#if (defined HAVE_LIBREGEX && HAVE_LIBREGEX) +int compile_regex(regex_t **compiled, const char *regex, const int cflags) +{ + int r; + regex_t *preg; + + if (regex == NULL) { + *compiled = NULL; + return 0; + } + + preg = malloc(sizeof(*preg)); + if (!preg) { + return -1; + } + + r = regcomp(preg, regex, cflags); + if (r) { + free(preg); + return -2; + } + + *compiled = preg; + + return 0; +} + +int match_regex(const regex_t *preg, const char *str) +{ + int r; + size_t len = 0; + char *string; + regmatch_t match; + + if (!preg) { + return 1; + } + + if (!str) { + string = xstrdup(""); + } else { + /* skip leading whitespace */ + for (len = 0; len < strlen(str); len++) { + + if (!strchr(" \t\n", str[len])) { + break; + } + } + + string = xstrdup(str+len); + + /* skip trailing whitespace */ + for (len = strlen(string); len > 0; len--) { + + if (!strchr(" \t\n", string[len-1])) { + break; + } + } + + string[len] = '\0'; + } + + /* test the regular expression */ + r = regexec(preg, string, 1, &match, 0); + free(string); + if (r) { + return 0; + } + + /* check that the match is the entire string */ + if ((match.rm_so != 0) || (match.rm_eo != (int)len)) { + return 0; + } + + return 1; +} + +int match_regex_hex(const regex_t *preg, const int n) +{ + char buf[10]; + + snprintf(buf, sizeof(buf), "%04x", n); + + return match_regex(preg, buf); +} +#endif /* HAVE_LIBREGEX */ diff --git a/common/dmfcore.c b/common/dmfcore.c index 90cacf25f0..64eb8fd95d 100644 --- a/common/dmfcore.c +++ b/common/dmfcore.c @@ -11,6 +11,7 @@ * Copyright (C) 2016 Carlos Dominguez * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016 - 2017 Jim Klimov + * Copyright (C) 2024 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 @@ -115,7 +116,9 @@ static void (*xml_uninitenc)(void); /* Note: names for popular libneon implementations are hard-coded at this * time - and just one, so extra DLLs (if an OS requires several to load) * are not supported. (TODO: link this to LIBNEON_LIBS from configure) */ -int load_neon_lib(void){ +static +int load_neon_lib(void) +{ #ifdef WITH_NEON # if WITH_LIBLTDL char *neon_libname_path = get_libname("libneon.so"); @@ -123,12 +126,12 @@ int load_neon_lib(void){ upsdebugx(1, "load_neon_lib(): neon_libname_path = %s", neon_libname_path); if(!neon_libname_path) { - upslogx(LOG_NOTICE, "Error loading Neon library required for DMF: %s not found by dynamic loader; please verify it is in your /usr/lib or some otherwise searched dynamic-library path", "libneon.so"); + upslogx(LOG_NOTICE, "Error loading Neon library required for DMF: %s not found by dynamic loader; please verify it is in your /usr/lib or some otherwise searched dynamic-library path, under this exact name (maybe you just need a symlink?)", "libneon.so"); neon_libname_path = get_libname("libneon-gnutls.so"); upsdebugx(1, "load_neon_lib(): neon_libname_path = %s", neon_libname_path); if(!neon_libname_path) { - upslogx(LOG_ERR, "Error loading Neon library required for DMF: %s not found by dynamic loader; please verify it is in your /usr/lib or some otherwise searched dynamic-library path", "libneon-gnutls.so"); + upslogx(LOG_ERR, "Error loading Neon library required for DMF: %s not found by dynamic loader; please verify it is in your /usr/lib or some otherwise searched dynamic-library path, under this exact name (maybe you just need a symlink?)", "libneon-gnutls.so"); return ERR; } } @@ -221,10 +224,32 @@ int load_neon_lib(void){ else { upsdebugx(1, "load_neon_lib(): lt_dlerror() final succeeded, library loaded"); free(neon_libname_path); +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif if (xml_init != NULL) { upsdebugx(1, "load_neon_lib(): calling xmlInitParser()"); xml_init(); } +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic pop +# endif return OK; } @@ -238,10 +263,32 @@ int load_neon_lib(void){ return ERR; # else /* not WITH_LIBLTDL */ upsdebugx(1, "load_neon_lib(): no-op because ltdl was not enabled during compilation,\nusual dynamic linking should be in place instead"); +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif if (xml_init != NULL) { upsdebugx(1, "load_neon_lib(): calling xmlInitParser()"); xml_init(); } +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic pop +# endif return OK; # endif /* WITH_LIBLTDL */ @@ -252,8 +299,26 @@ int load_neon_lib(void){ #endif /* WITH_NEON */ } -void unload_neon_lib(){ +static +void unload_neon_lib(void) +{ #ifdef WITH_NEON +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" +# endif +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Waddress" +# endif if (xml_uninitenc != NULL) { upsdebugx(1, "unload_neon_lib(): calling xmlCleanupCharEncodingHandlers()"); xml_uninitenc(); @@ -262,12 +327,19 @@ void unload_neon_lib(){ upsdebugx(1, "unload_neon_lib(): calling xmlCleanupParser()"); xml_uninit(); } -#if WITH_LIBLTDL +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS) +# pragma GCC diagnostic pop +# endif + +# if WITH_LIBLTDL upsdebugx(1, "unload_neon_lib(): unloading the library"); lt_dlclose(dl_handle_libneon); dl_handle_libneon = NULL; lt_dlexit(); -#endif /* WITH_LIBLTDL */ +# endif /* WITH_LIBLTDL */ #endif /* WITH_NEON */ } @@ -280,7 +352,7 @@ void unload_neon_lib(){ * the format-specific parsed_data structure, and pass to dmfcore_parse*(). */ dmfcore_parser_t* -dmfcore_parser_new() +dmfcore_parser_new(void) { dmfcore_parser_t *self = (dmfcore_parser_t *) calloc (1, sizeof (dmfcore_parser_t)); assert (self); @@ -314,6 +386,7 @@ dmfcore_parse_file(char *file_name, dmfcore_parser_t *dcp) char buffer[4096]; /* Align with common cluster/FSblock size nowadays */ FILE *f; int result = 0; + ne_xml_parser *parser; #if WITH_LIBLTDL int flag_libneon = 0; #endif /* WITH_LIBLTDL */ @@ -358,7 +431,7 @@ dmfcore_parse_file(char *file_name, dmfcore_parser_t *dcp) } #endif /* WITH_LIBLTDL */ - ne_xml_parser *parser = xml_create (); + parser = xml_create (); xml_push_handler (parser, dcp->xml_dict_start_cb, dcp->xml_cdata_cb , dcp->xml_end_cb, dcp->parsed_data); @@ -414,6 +487,7 @@ dmfcore_parse_str (const char *dmf_string, dmfcore_parser_t *dcp) { int result = 0; size_t len; + ne_xml_parser *parser; #if WITH_LIBLTDL int flag_libneon = 0; #endif /* WITH_LIBLTDL */ @@ -457,7 +531,7 @@ dmfcore_parse_str (const char *dmf_string, dmfcore_parser_t *dcp) } #endif /* WITH_LIBLTDL */ - ne_xml_parser *parser = xml_create (); + parser = xml_create (); xml_push_handler (parser, dcp->xml_dict_start_cb, dcp->xml_cdata_cb , dcp->xml_end_cb, dcp->parsed_data); @@ -496,8 +570,9 @@ dmfcore_parse_str (const char *dmf_string, dmfcore_parser_t *dcp) int dmfcore_parse_dir (char *dir_name, dmfcore_parser_t *dcp) { - struct dirent **dir_ent; - int i = 0, x = 0, result = 0, n = 0; + DIR *dp = NULL; + struct dirent *dirp; + int i = 0, x = 0, result = 0, n = 0, c = 0; #if WITH_LIBLTDL int flag_libneon = 0; #endif /* WITH_LIBLTDL */ @@ -507,16 +582,17 @@ dmfcore_parse_dir (char *dir_name, dmfcore_parser_t *dcp) assert (dir_name); assert (dcp); - if ( (dir_name == NULL ) || \ - ( (n = scandir(dir_name, &dir_ent, NULL, alphasort)) == 0 ) ) - { - upslogx(LOG_ERR, "ERROR: DMF directory '%s' not found or not readable", - dir_name ? dir_name : ""); - return ENOENT; - } - - if (n < 0) { - result = errno; + errno = 0; + if ( (dir_name == NULL) || \ + ((dp = opendir(dir_name)) == NULL) || \ + errno != 0 + ) { + if (dir_name == NULL) { + result = ENOENT; + /* TOTHINK? result = EINVAL; EBADF? */ + } else { + result = errno; + } upslog_with_errno(LOG_ERR, "ERROR: DMF directory '%s' not found or not readable", dir_name ? dir_name : ""); return result; @@ -534,27 +610,49 @@ dmfcore_parse_dir (char *dir_name, dmfcore_parser_t *dcp) } #endif /* WITH_LIBLTDL */ + errno = 0; + while ((dirp = readdir(dp)) != NULL) + n++; + + if (errno) { + upsdebugx(1, "%s: had a problem counting directory entries: (%d) %s", + __func__, errno, strerror(errno)); + } + rewinddir(dp); + upsdebugx(2, "Got %d entries to parse in directory %s", n, dir_name); - int c; - for (c = 0; c < n; c++) + while ((dirp = readdir(dp)) != NULL) { - upsdebugx (5, "dmfcore_parse_dir(): dir_ent[%d]->d_name=%s", c, dir_ent[c]->d_name); - size_t fname_len = strlen(dir_ent[c]->d_name); + size_t fname_len; + + upsdebugx (5, "dmfcore_parse_dir(): dir_ent[%d]->d_name=%s", c, dirp->d_name); + fname_len = strlen(dirp->d_name); if ( (fname_len > 4) && - (strstr(dir_ent[c]->d_name + fname_len - 4, ".dmf") || - strstr(dir_ent[c]->d_name + fname_len - 4, ".DMF") ) ) + (strstr(dirp->d_name + fname_len - 4, ".dmf") || + strstr(dirp->d_name + fname_len - 4, ".DMF") ) ) { i++; - if(strlen(dir_name) + strlen(dir_ent[c]->d_name) < PATH_MAX_SIZE){ + if(strlen(dir_name) + strlen(dirp->d_name) < PATH_MAX_SIZE){ char *file_path = (char *) calloc(PATH_MAX_SIZE, sizeof(char)); if (!file_path) { - upslogx(LOG_ERR, "dmfcore_parse_dir(): calloc() failed"); + upslogx(LOG_ERR, "dqmfcore_parse_dir(): calloc() failed"); } else { - sprintf(file_path, "%s/%s", dir_name, dir_ent[c]->d_name); + int res; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif + snprintf(file_path, PATH_MAX_SIZE, "%s/%s", dir_name, dirp->d_name); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif assert(file_path); - int res = dmfcore_parse_file(file_path, dcp); + res = dmfcore_parse_file(file_path, dcp); upsdebugx (5, "dmfcore_parse_file (\"%s\", <%p>)=%d", file_path, (void*)dcp, res); if ( res != 0 ) { @@ -564,13 +662,12 @@ dmfcore_parse_dir (char *dir_name, dmfcore_parser_t *dcp) } free(file_path); } - }else{ + } else { upslogx(LOG_ERR, "dmfcore_parse_dir(): File path too long"); } } - free(dir_ent[c]); + c++; } - free(dir_ent); #if WITH_LIBLTDL if(flag_libneon == 1) diff --git a/common/dmfsnmp.c b/common/dmfsnmp.c index b1c001b369..f75e494c56 100644 --- a/common/dmfsnmp.c +++ b/common/dmfsnmp.c @@ -8,6 +8,7 @@ * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016 - 2021 Jim Klimov * Copyright (C) 2019 Arnaud Quette + * Copyright (C) 2024 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 @@ -46,8 +47,13 @@ int input_phases, output_phases, bypass_phases; #if WITH_DMF_FUNCTIONS - int functions_aux = 0; - char *function_text = NULL; + static int functions_aux = 0; + static char *function_text = NULL; +#endif + +/* Currently not defined in recipes outside */ +#ifndef WITH_DMF_SETVAR +# define WITH_DMF_SETVAR 0 #endif /*DEBUGGING*/ @@ -60,8 +66,10 @@ print_snmp_memory_struct(snmp_info_t *self) " // OID: %s // Default: %s", self->info_type, self->info_len, self->OID, self->dfl); -// if(self->setvar) -// upsdebugx(5, " // Setvar: %d\n", *self->setvar); +#if WITH_DMF_SETVAR + if(self->setvar) + upsdebugx(5, " // Setvar: %d\n", *self->setvar); +#endif /* WITH_DMF_SETVAR */ if (self->oid2info) { @@ -85,7 +93,7 @@ print_snmp_memory_struct(snmp_info_t *self) self->oid2info[i].fun_s2l ? "defined" : "(null)"); upsdebugx(5, " nuf_vp2s ---> %s", self->oid2info[i].nuf_vp2s ? "defined" : "(null)"); -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ i++; } } @@ -101,16 +109,18 @@ print_snmp_memory_struct(snmp_info_t *self) || (strcmp("lua", self->function_language)==0) ) { # if WITH_DMF_LUA + lua_State *f_aux; upsdebugx(5, "Dumping SNMP_INFO entry backed by dynamic code in '%s' language", self->function_language ? self->function_language : "LUA"); - lua_State *f_aux = luaL_newstate(); + f_aux = luaL_newstate(); luaL_openlibs(f_aux); if (luaL_loadstring(f_aux, self->function_code)){ upsdebugx(5, "Error loading LUA functions:\n%s\n", self->function_code); } else { + char *funcname; upsdebugx(5, "***********-> Luatext:\n%s\n", self->function_code); lua_pcall(f_aux,0,0,0); - char *funcname = snmp_info_type_to_main_function_name(self->info_type); + funcname = snmp_info_type_to_main_function_name(self->info_type); upsdebugx(5, "***********-> Going to call Lua funcname:\n%s\n", funcname ? funcname : "" ); lua_getglobal(f_aux, funcname); lua_pcall(f_aux,0,1,0); @@ -193,10 +203,12 @@ print_mib2nut_memory_struct(mib2nut_info_t *self) char * snmp_info_type_to_main_function_name(const char * info_type) { - assert(info_type); - char *result = (char *) calloc(strlen(info_type), sizeof(char)); + char *result; int i = 0; int j = 0; + + assert(info_type); + result = (char *) calloc(strlen(info_type), sizeof(char)); while(info_type[i]){ if(info_type[i] != '.'){ result[j] = info_type[i]; @@ -232,7 +244,7 @@ info_lkp_new (int oid, const char *value , long (*nuf_s2l)(const char *nut_value) , long (*fun_s2l)(const char *snmp_value) , const char *(*nuf_vp2s)(void *raw_nut_value) -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ ) { info_lkp_t *self = (info_lkp_t*) calloc (1, sizeof (info_lkp_t)); @@ -241,7 +253,7 @@ info_lkp_new (int oid, const char *value if (value) self->info_value = strdup (value); #if WITH_SNMP_LKP_FUN -// consider WITH_DMF_FUNCTIONS too? + /* TOTHINK: consider WITH_DMF_FUNCTIONS too? */ if (fun_vp2s || nuf_s2l || fun_s2l || nuf_vp2s) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_PEDANTIC) # pragma GCC diagnostic push @@ -266,7 +278,7 @@ info_lkp_new (int oid, const char *value self->nuf_s2l = NULL; self->fun_s2l = NULL; self->nuf_vp2s = NULL; -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ return self; } @@ -289,7 +301,9 @@ snmp_info_t * info_snmp_new (const char *name, int info_flags, double multiplier, const char *oid, const char *dfl, unsigned long flags, info_lkp_t *lookup - //, int *setvar +#if WITH_DMF_SETVAR + , int *setvar +#endif /* WITH_DMF_SETVAR */ #if WITH_DMF_FUNCTIONS , char **function_language, char **function_code #endif @@ -307,7 +321,10 @@ info_snmp_new (const char *name, int info_flags, double multiplier, self->info_flags = info_flags; self->flags = flags; self->oid2info = lookup; -// self->setvar = setvar; +#if WITH_DMF_SETVAR + self->setvar = setvar; +#endif /* WITH_DMF_SETVAR */ + #if WITH_DMF_FUNCTIONS /* Note: The DMF (XML) structure contains a "functionset" reference and * the "name" of the mapping field; these are looked up during parsing @@ -424,7 +441,7 @@ function_destroy (void **self_p){ } #endif /* WITH_DMF_FUNCTIONS */ -/*Destroy full array of lookup elements*/ +/* Destroy full array of lookup elements */ void info_lkp_destroy (void **self_p) { @@ -436,8 +453,9 @@ info_lkp_destroy (void **self_p) free ((char*)self->info_value); self->info_value = NULL; } -// FIXME: When DMF support for lookup functions comes to fruition, -// we may need to handle teardown of fun_vp2s/nuf_s2l/fun_s2l/nuf_vp2s here somehow. +/* FIXME: When DMF support for lookup functions comes to fruition, + * we may need to handle teardown of fun_vp2s/nuf_s2l/fun_s2l/nuf_vp2s + * here somehow. */ free (self); *self_p = NULL; } @@ -682,13 +700,20 @@ mibdmf_parser_new_list(mibdmf_parser_t *dmp) else dmp->list = (alist_t **) realloc(dmp->list, (dmp->sublist_elements + 1) * sizeof(alist_t *)); assert (dmp->list); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif dmp->list[dmp->sublist_elements - 1] = alist_new( NULL,(void (*)(void **))alist_destroy, NULL ); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif assert (dmp->list[dmp->sublist_elements - 1]); dmp->list[dmp->sublist_elements] = NULL; } mibdmf_parser_t * -mibdmf_parser_new() +mibdmf_parser_new(void) { mibdmf_parser_t *self = (mibdmf_parser_t *) calloc (1, sizeof (mibdmf_parser_t)); assert (self); @@ -756,10 +781,12 @@ mib2nut_info_node_handler (alist_t *list, const char **attrs) lkp->values[i])->dfl; else snmp[i].dfl = NULL; -// if( ((snmp_info_t*) lkp->values[i])->setvar ) -// snmp[i].setvar = ((snmp_info_t*) -// lkp->values[i])->setvar; -// else snmp[i].setvar = NULL; +#if WITH_DMF_SETVAR + if( ((snmp_info_t*) lkp->values[i])->setvar ) + snmp[i].setvar = ((snmp_info_t*) + lkp->values[i])->setvar; + else snmp[i].setvar = NULL; +#endif /* WITH_DMF_SETVAR */ if( ((snmp_info_t*) lkp->values[i])->oid2info ) snmp[i].oid2info = ((snmp_info_t*) @@ -793,7 +820,9 @@ mib2nut_info_node_handler (alist_t *list, const char **attrs) snmp[i].OID = NULL; snmp[i].flags = 0; snmp[i].dfl = NULL; -// snmp[i].setvar = NULL; +#if WITH_DMF_SETVAR + snmp[i].setvar = NULL; +#endif /* WITH_DMF_SETVAR */ snmp[i].oid2info = NULL; #if WITH_DMF_FUNCTIONS snmp[i].function_code = NULL; @@ -831,6 +860,10 @@ mib2nut_info_node_handler (alist_t *list, const char **attrs) alarm[i].alarm_value = NULL; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif if(arg[0]) { alist_append(element, ((mib2nut_info_t *(*) ( @@ -840,6 +873,9 @@ mib2nut_info_node_handler (alist_t *list, const char **attrs) (arg[0], arg[1], arg[3], arg[4], snmp, arg[2], alarm)); } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif for (i = 0; i < (INFO_MIB2NUT_MAX_ATTRS + 1); i++) free (arg[i]); @@ -860,10 +896,17 @@ alarm_info_node_handler(alist_t *list, const char **attrs) arg[1] = get_param_by_name(ALARM_STATUS, attrs); arg[2] = get_param_by_name(ALARM_OID, attrs); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif if(arg[0]) alist_append(element, ( (alarms_info_t *(*) (const char *, const char *, const char *) ) element->new_element) (arg[0], arg[1], arg[2])); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif for(i = 0; i < (INFO_ALARM_MAX_ATTRS + 1); i++) free (arg[i]); @@ -877,8 +920,19 @@ lookup_info_node_handler(alist_t *list, const char **attrs) alist_t *element = alist_get_last_element(list); int i = 0; char **arg = (char**) calloc ((INFO_LOOKUP_MAX_ATTRS + 1), sizeof (void**)); +#if WITH_SNMP_LKP_FUN + const char *(*fun_vp2s)(void *raw_snmp_value) = NULL; + long (*nuf_s2l)(const char *nut_value) = NULL; + long (*fun_s2l)(const char *snmp_value) = NULL; + const char *(*nuf_vp2s)(void *raw_nut_value) = NULL; +#endif /* WITH_SNMP_LKP_FUN */ + assert (arg); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif arg[0] = get_param_by_name(LOOKUP_OID, attrs); arg[1] = get_param_by_name(LOOKUP_VALUE, attrs); #if WITH_SNMP_LKP_FUN @@ -888,42 +942,41 @@ lookup_info_node_handler(alist_t *list, const char **attrs) arg[5] = get_param_by_name(LOOKUP_NUF_VP2S, attrs); arg[6] = get_param_by_name(LOOKUP_FUNCTIONSET, attrs); - const char *(*fun_vp2s)(void *raw_snmp_value) = NULL; - long (*nuf_s2l)(const char *nut_value) = NULL; - long (*fun_s2l)(const char *snmp_value) = NULL; - const char *(*nuf_vp2s)(void *raw_nut_value) = NULL; - if (arg[2] || arg[3] || arg[4] || arg[5] || arg[6]) { upslogx(1, "WARNING : DMF does not support lookup functions at this time, " "so the provided value is effectively ignored: " "oid='%s' value='%s' " "fun_vp2s='%s' nuf_s2l='%s' fun_s2l='%s' nuf_vp2s='%s' functionset='%s'", arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6]); - // FIXME : set up fun and nuf pointers based on functionset, language, - // being linked against LUA / with DMF functions, etc. Maybe use some - // code built into NUT binaries as a fallback via special "language" - // or even "functionset" value? + /* FIXME : set up fun and nuf pointers based on functionset, language, + * being linked against LUA / with DMF functions, etc. Maybe use some + * code built into NUT binaries as a fallback via special "language" + * or even "functionset" value? */ } -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ if(arg[0]) alist_append(element, ((info_lkp_t *(*) (int, const char * #if WITH_SNMP_LKP_FUN -// FIXME: Now these settings end up as no-ops; later these should be -// real pointers to a function, and if we get any strings from XML - -// see WITH_DMF_FUNCTIONS for conversion. Note that these are not -// really (char*) in info_lkp_t, as defined in array above... +/* FIXME: Now these settings end up as no-ops; later these should be + * real pointers to a function, and if we get any strings from XML - + * see WITH_DMF_FUNCTIONS for conversion. Note that these are not + * really (char*) in info_lkp_t, as defined in array above... + */ , const char *(*)(void *raw_snmp_value) , long (*)(const char *nut_value) , long (*)(const char *snmp_value) , const char *(*)(void *raw_nut_value) -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ ))element->new_element) (atoi(arg[0]), arg[1] #if WITH_SNMP_LKP_FUN , fun_vp2s, nuf_s2l, fun_s2l, nuf_vp2s -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ )); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif for(i = 0; i < (INFO_LOOKUP_MAX_ATTRS + 1); i++) free (arg[i]); @@ -936,12 +989,19 @@ void function_node_handler(alist_t *list, const char **attrs) { alist_t *element = alist_get_last_element(list); - char *argname = NULL, *arglang = NULL; // = (char*) calloc (32, sizeof (char *)); + char *argname = NULL, *arglang = NULL; /* = (char*) calloc (32, sizeof (char *)); */ argname = get_param_by_name(SNMP_NAME, attrs); arglang = get_param_by_name("language", attrs); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif if(argname != NULL) alist_append(element, ((dmf_function_t *(*) (const char *, const char *)) element->new_element) (argname, arglang)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif if(argname != NULL) free(argname); if(arglang != NULL) @@ -967,12 +1027,19 @@ snmp_info_node_handler(alist_t *list, const char **attrs) (INFO_SNMP_MAX_ATTRS + 1), sizeof (void**) ); assert (arg); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif + arg[0] = get_param_by_name(SNMP_NAME, attrs); arg[1] = get_param_by_name(SNMP_MULTIPLIER, attrs); arg[2] = get_param_by_name(SNMP_OID, attrs); arg[3] = get_param_by_name(SNMP_DEFAULT, attrs); arg[4] = get_param_by_name(SNMP_LOOKUP, attrs); -// arg[5] = get_param_by_name(SNMP_SETVAR, attrs); +#if WITH_DMF_SETVAR + arg[5] = get_param_by_name(SNMP_SETVAR, attrs); +#endif /* WITH_DMF_SETVAR */ #if WITH_DMF_FUNCTIONS arg[6] = get_param_by_name(TYPE_FUNCTIONSET, attrs); @@ -1021,7 +1088,7 @@ snmp_info_node_handler(alist_t *list, const char **attrs) multiplier = atof(arg[1]); } -/* +#if WITH_DMF_SETVAR if(arg[5]) { if(strcmp(arg[5], SETVAR_INPUT_PHASES) == 0) @@ -1069,8 +1136,9 @@ snmp_info_node_handler(alist_t *list, const char **attrs) , &func_lang, &func_code #endif )); - // End of arg[5] aka setvar - } else*/ + /* End of arg[5] aka setvar */ + } else +#endif /* WITH_DMF_SETVAR */ alist_append(element, ((snmp_info_t *(*) (const char *, int, double, const char *, const char *, unsigned long, info_lkp_t * /*, int * */ @@ -1086,6 +1154,10 @@ snmp_info_node_handler(alist_t *list, const char **attrs) #endif )); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif + for(i = 0; i < (INFO_SNMP_MAX_ATTRS + 1); i++) free (arg[i]); @@ -1270,16 +1342,23 @@ mibdmf_xml_dict_start_cb(void *userdata, int parent, const char *nspace, const char *name, const char **attrs) { + char *auxname; + mibdmf_parser_t *dmp; + alist_t *list; NUT_UNUSED_VARIABLE(parent); NUT_UNUSED_VARIABLE(nspace); if(!userdata) return ERR; - char *auxname = get_param_by_name("name",attrs); - mibdmf_parser_t *dmp = (mibdmf_parser_t*) userdata; - alist_t *list = *(mibdmf_get_aux_list_ptr(dmp)); + auxname = get_param_by_name("name",attrs); + dmp = (mibdmf_parser_t*) userdata; + list = *(mibdmf_get_aux_list_ptr(dmp)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif if(strcmp(name,DMFTAG_MIB2NUT) == 0) { alist_append(list, alist_new(auxname, info_mib2nut_destroy, @@ -1318,6 +1397,7 @@ mibdmf_xml_dict_start_cb(void *userdata, int parent, #if WITH_DMF_FUNCTIONS alist_append(list, alist_new(auxname, function_destroy, (void (*)(void)) function_new)); + functions_aux = 1; #else upsdebugx(2, "WARN: NUT was not compiled with DMF function feature, 'functions' DMF tag ignored."); @@ -1338,20 +1418,25 @@ mibdmf_xml_dict_start_cb(void *userdata, int parent, } free(auxname); return DMF_NEON_CALLBACK_OK; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif } int mibdmf_xml_end_cb(void *userdata, int state, const char *nspace, const char *name) { + mibdmf_parser_t *dmp; + alist_t *list, *element; NUT_UNUSED_VARIABLE(state); NUT_UNUSED_VARIABLE(nspace); if(!userdata) return ERR; - mibdmf_parser_t *dmp = (mibdmf_parser_t*) userdata; - alist_t *list = *(mibdmf_get_aux_list_ptr(dmp)); - alist_t *element = alist_get_last_element(list); + dmp = (mibdmf_parser_t*) userdata; + list = *(mibdmf_get_aux_list_ptr(dmp)); + element = alist_get_last_element(list); /* Currently, special handling in the DMF tag closure is for "mib2nut" * tags that are last in the file according to schema - so we know we @@ -1361,13 +1446,14 @@ mibdmf_xml_end_cb(void *userdata, int state, const char *nspace, const char *nam if(strcmp(name,DMFTAG_MIB2NUT) == 0) { int device_table_counter = mibdmf_get_device_table_counter(dmp); + snmp_device_id_t *device_table; *mibdmf_get_device_table_ptr(dmp) = (snmp_device_id_t *) realloc(*mibdmf_get_device_table_ptr(dmp), device_table_counter * sizeof(snmp_device_id_t)); *mibdmf_get_mib2nut_table_ptr(dmp) = (mib2nut_info_t **) realloc(*mibdmf_get_mib2nut_table_ptr(dmp), device_table_counter * sizeof(mib2nut_info_t*)); - snmp_device_id_t *device_table = mibdmf_get_device_table(dmp); + device_table = mibdmf_get_device_table(dmp); assert (device_table); /* Make sure the new last entry in the table is zeroed-out */ @@ -1400,8 +1486,8 @@ mibdmf_xml_end_cb(void *userdata, int state, const char *nspace, const char *nam }else if(strcmp(name,DMFTAG_FUNCTION) == 0) { - alist_t *element = alist_get_last_element(list); - dmf_function_t *func =(dmf_function_t *) alist_get_last_element(element); + alist_t *sub_element = alist_get_last_element(list); + dmf_function_t *func =(dmf_function_t *) alist_get_last_element(sub_element); func->code = strdup(function_text); free(function_text); function_text = NULL; @@ -1415,6 +1501,10 @@ int mibdmf_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len) { NUT_UNUSED_VARIABLE(state); +#if ! WITH_DMF_FUNCTIONS + NUT_UNUSED_VARIABLE(cdata); + NUT_UNUSED_VARIABLE(len); +#endif if(!userdata) return ERR; @@ -1431,8 +1521,9 @@ mibdmf_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len) function_text = (char*) calloc(len + 2, sizeof(char)); sprintf(function_text, "%.*s\n", (int) len, cdata); } else { + char *aux_str; function_text = (char*) realloc(function_text, (strlen(function_text) + len + 2) * sizeof(char)); - char *aux_str = (char*) calloc(len + 2, sizeof(char)); + aux_str = (char*) calloc(len + 2, sizeof(char)); sprintf(aux_str, "%.*s\n", (int) len, cdata); strcat(function_text, aux_str); free(aux_str); @@ -1444,6 +1535,7 @@ mibdmf_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len) return OK; } +static int mibdmf_parse_begin_cb(void *parsed_data) { @@ -1462,6 +1554,7 @@ mibdmf_parse_begin_cb(void *parsed_data) return OK; } +static int mibdmf_parse_finish_cb(void *parsed_data, int result) { @@ -1495,11 +1588,14 @@ mibdmf_parse_finish_cb(void *parsed_data, int result) int mibdmf_parse_file(char *file_name, mibdmf_parser_t *dmp) { + dmfcore_parser_t *dcp; + int result; + upsdebugx(1, "%s(%s)", __func__, file_name); assert(file_name); assert(dmp); - dmfcore_parser_t *dcp = dmfcore_parser_new_init( + dcp = dmfcore_parser_new_init( (void*)dmp, mibdmf_parse_begin_cb, mibdmf_parse_finish_cb, @@ -1509,7 +1605,7 @@ mibdmf_parse_file(char *file_name, mibdmf_parser_t *dmp) ); assert(dcp); - int result = dmfcore_parse_file(file_name, dcp); + result = dmfcore_parse_file(file_name, dcp); free(dcp); return result; } @@ -1518,11 +1614,14 @@ mibdmf_parse_file(char *file_name, mibdmf_parser_t *dmp) int mibdmf_parse_str (const char *dmf_string, mibdmf_parser_t *dmp) { + dmfcore_parser_t *dcp; + int result; + upsdebugx(1, "%s(string)", __func__); assert(dmf_string); assert(dmp); - dmfcore_parser_t *dcp = dmfcore_parser_new_init( + dcp = dmfcore_parser_new_init( (void*)dmp, mibdmf_parse_begin_cb, mibdmf_parse_finish_cb, @@ -1532,7 +1631,7 @@ mibdmf_parse_str (const char *dmf_string, mibdmf_parser_t *dmp) ); assert(dcp); - int result = dmfcore_parse_str(dmf_string, dcp); + result = dmfcore_parse_str(dmf_string, dcp); free(dcp); return result; } @@ -1542,11 +1641,14 @@ mibdmf_parse_str (const char *dmf_string, mibdmf_parser_t *dmp) int mibdmf_parse_dir (char *dir_name, mibdmf_parser_t *dmp) { + dmfcore_parser_t *dcp; + int result; + upsdebugx(1, "%s(%s)", __func__, dir_name); assert(dir_name); assert(dmp); - dmfcore_parser_t *dcp = dmfcore_parser_new_init( + dcp = dmfcore_parser_new_init( (void*)dmp, mibdmf_parse_begin_cb, mibdmf_parse_finish_cb, @@ -1556,7 +1658,7 @@ mibdmf_parse_dir (char *dir_name, mibdmf_parser_t *dmp) ); assert(dcp); - int result = dmfcore_parse_dir(dir_name, dcp); + result = dmfcore_parse_dir(dir_name, dcp); free(dcp); return result; } @@ -1564,6 +1666,8 @@ mibdmf_parse_dir (char *dir_name, mibdmf_parser_t *dmp) bool dmf_streq (const char* x, const char* y) { + int cmp; + if (!x && !y) return true; if (!x || !y) { @@ -1572,7 +1676,7 @@ dmf_streq (const char* x, const char* y) y ? y : ""); return false; } - int cmp = strcmp (x, y); + cmp = strcmp (x, y); if (cmp != 0) { upsdebugx(2, "\nDEBUG: strEQ(): Strings not equal (%i):\n\t%s\n\t%s\n", cmp, x, y); } @@ -1582,6 +1686,8 @@ dmf_streq (const char* x, const char* y) bool dmf_strneq (const char* x, const char* y) { + int cmp; + if (!x && !y) { upsdebugx(2, "\nDEBUG: strNEQ(): Both compared strings are NULL\n"); return false; @@ -1589,7 +1695,7 @@ dmf_strneq (const char* x, const char* y) if (!x || !y) { return true; } - int cmp = strcmp (x, y); + cmp = strcmp (x, y); if (cmp == 0) { upsdebugx(2, "\nDEBUG: strNEQ(): Strings are equal (%i):\n\t%s\n\t%s\n\n", cmp, x, y); } @@ -1609,7 +1715,7 @@ is_sentinel__snmp_info_t(const snmp_info_t *pstruct) { upsdebugx (5, "Entering is_sentinel__snmp_info_t()"); assert (pstruct); - return ( pstruct->info_type == NULL && pstruct->info_len == 0 && pstruct->OID == NULL + return ( pstruct->info_type == NULL && (int)(pstruct->info_len) == 0 && pstruct->OID == NULL && pstruct->dfl == NULL && pstruct->flags == 0 && pstruct->oid2info == NULL #if WITH_DMF_FUNCTIONS && pstruct->function_language == NULL diff --git a/common/nutconf.cpp b/common/nutconf.cpp index 2166ea1624..33adb3c47c 100644 --- a/common/nutconf.cpp +++ b/common/nutconf.cpp @@ -1,24 +1,27 @@ -/* nutconf.cpp - configuration API +/* + nutconf.cpp - configuration API - Copyright (C) + Copyright (C) 2012 Emilien Kia - 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 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. + 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 + 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 "nutconf.h" +#include "config.h" + +#include "nutconf.hpp" #include "nutwriter.hpp" #include @@ -32,6 +35,16 @@ namespace nut { +/* Trivial implementations out of class declaration to avoid + * error: 'ClassName' has no out-of-line virtual method definitions; its vtable + * will be emitted in every translation unit [-Werror,-Wweak-vtables] + */ +Serialisable::~Serialisable() {} +BaseConfiguration::~BaseConfiguration() {} +GenericConfiguration::~GenericConfiguration() {} +UpsConfiguration::~UpsConfiguration() {} +NutParser::~NutParser() {} + // // Tool functions // @@ -84,45 +97,45 @@ void NutParser::setOptions(unsigned int options, bool set) } char NutParser::get() { - if (_pos >= _buffer.size()) - return 0; - else - return _buffer[_pos++]; + if (_pos >= _buffer.size()) + return 0; + else + return _buffer[_pos++]; } char NutParser::peek() { - return _buffer[_pos]; + return _buffer[_pos]; } size_t NutParser::getPos()const { - return _pos; + return _pos; } void NutParser::setPos(size_t pos) { - _pos = pos; + _pos = pos; } char NutParser::charAt(size_t pos)const { - return _buffer[pos]; + return _buffer[pos]; } void NutParser::pushPos() { - _stack.push_back(_pos); + _stack.push_back(_pos); } size_t NutParser::popPos() { - size_t pos = _stack.back(); - _stack.pop_back(); - return pos; + size_t pos = _stack.back(); + _stack.pop_back(); + return pos; } void NutParser::rewind() { - _pos = popPos(); + _pos = popPos(); } void NutParser::back() { - if (_pos > 0) - --_pos; + if (_pos > 0) + --_pos; } /* Parse a string source for a CHARS and return its size if found or 0, if not. @@ -132,33 +145,33 @@ void NutParser::back() { * TODO: accept "\t", "\s", "\r", "\n" ?? */ std::string NutParser::parseCHARS() { - bool escaped = false; // Is char escaped ? - std::string res; // Stored string - - pushPos(); - - for (char c = get(); c != 0 /*EOF*/; c = get()) { - if (escaped) { - if (isspace(c) || c == '\\' || c == '"' || c == '#') { - res += c; - } else { - /* WTF ??? */ - } - escaped = false; - } else { - if (c == '\\') { - escaped = true; - } else if (isgraph(c) /*&& c != '\\'*/ && c != '"' && c != '#') { - res += c; - } else { - back(); - break; - } - } - } - - popPos(); - return res; + bool escaped = false; // Is char escaped ? + std::string res; // Stored string + + pushPos(); + + for (char c = get(); c != 0 /*EOF*/; c = get()) { + if (escaped) { + if (isspace(c) || c == '\\' || c == '"' || c == '#') { + res += c; + } else { + /* WTF ??? */ + } + escaped = false; + } else { + if (c == '\\') { + escaped = true; + } else if (isgraph(c) /*&& c != '\\'*/ && c != '"' && c != '#') { + res += c; + } else { + back(); + break; + } + } + } + + popPos(); + return res; } /* Parse a string source for a STRCHARS and return its size if found or 0, if not. @@ -168,33 +181,33 @@ std::string NutParser::parseCHARS() { * TODO: accept "\t", "\s", "\r", "\n" ?? */ std::string NutParser::parseSTRCHARS() { - bool escaped = false; // Is char escaped ? - std::string str; // Stored string - - pushPos(); - - for (char c = get(); c != 0 /*EOF*/; c = get()) { - if (escaped) { - if (isspace(c) || c == '\\' || c == '"') { - str += c; - } else { - /* WTF ??? */ - } - escaped = false; - } else { - if (c == '\\') { - escaped = true; - } else if (isprint(c) && c != '\\' && c != '"') { - str += c; - } else { - back(); - break; - } - } - } - - popPos(); - return str; + bool escaped = false; // Is char escaped ? + std::string str; // Stored string + + pushPos(); + + for (char c = get(); c != 0 /*EOF*/; c = get()) { + if (escaped) { + if (isspace(c) || c == '\\' || c == '"') { + str += c; + } else { + /* WTF ??? */ + } + escaped = false; + } else { + if (c == '\\') { + escaped = true; + } else if (isprint(c) && c != '\\' && c != '"') { + str += c; + } else { + back(); + break; + } + } + } + + popPos(); + return str; } /** Parse a string source for getting the next token, ignoring spaces. @@ -202,179 +215,203 @@ std::string NutParser::parseSTRCHARS() { */ NutParser::Token NutParser::parseToken() { - /** Lexical parsing machine state enumeration.*/ - typedef enum { - LEXPARSING_STATE_DEFAULT, - LEXPARSING_STATE_QUOTED_STRING, - LEXPARSING_STATE_STRING, - LEXPARSING_STATE_COMMENT - } LEXPARSING_STATE_e; - LEXPARSING_STATE_e state = LEXPARSING_STATE_DEFAULT; - - Token token; - bool escaped = false; - - pushPos(); - - for (char c = get(); c != 0 /*EOF*/; c = get()) { - switch (state) { - case LEXPARSING_STATE_DEFAULT: /* Wait for a non-space char */ - { - if (c == ' ' || c == '\t') { - /* Space : do nothing */ - } else if (c == '[') { - token = Token(Token::TOKEN_BRACKET_OPEN, c); - popPos(); - return token; - } else if (c == ']') { - token = Token(Token::TOKEN_BRACKET_CLOSE, c); - popPos(); - return token; - } else if (c == ':' && !hasOptions(OPTION_IGNORE_COLON)) { - token = Token(Token::TOKEN_COLON, c); - popPos(); - return token; - } else if (c == '=') { - token = Token(Token::TOKEN_EQUAL, c); - popPos(); - return token; - } else if (c == '\r' || c == '\n') { - token = Token(Token::TOKEN_EOL, c); - popPos(); - return token; - } else if (c == '#') { - token.type = Token::TOKEN_COMMENT; - state = LEXPARSING_STATE_COMMENT; - } else if (c == '"') { - /* Begin of QUOTED STRING */ - token.type = Token::TOKEN_QUOTED_STRING; - state = LEXPARSING_STATE_QUOTED_STRING; - } else if (c == '\\') { - /* Begin of STRING with escape */ - token.type = Token::TOKEN_STRING; - state = LEXPARSING_STATE_STRING; - escaped = true; - } else if (isgraph(c)) { - /* Begin of STRING */ - token.type = Token::TOKEN_STRING; - state = LEXPARSING_STATE_STRING; - token.str += c; - } else { - rewind(); - return Token(Token::TOKEN_UNKNOWN); - } - break; - } - case LEXPARSING_STATE_QUOTED_STRING: - { - if (c == '"') { - if (escaped) { - escaped = false; - token.str += '"'; - } else { - popPos(); - return token; - } - } else if (c == '\\') { - if (escaped) { - escaped = false; - token.str += '\\'; - } else { - escaped = true; - } - } else if (c == ' ' || c == '\t' || isgraph(c)) { - token.str += c; - } else if (c == '\r' || c == '\n') /* EOL */{ + /** Lexical parsing machine state enumeration.*/ + typedef enum { + LEXPARSING_STATE_DEFAULT, + LEXPARSING_STATE_QUOTED_STRING, + LEXPARSING_STATE_STRING, + LEXPARSING_STATE_COMMENT + } LEXPARSING_STATE_e; + LEXPARSING_STATE_e state = LEXPARSING_STATE_DEFAULT; + + Token token; + bool escaped = false; + + pushPos(); + + for (char c = get(); c != 0 /*EOF*/; c = get()) { + switch (state) { + case LEXPARSING_STATE_DEFAULT: /* Wait for a non-space char */ + { + if (c == ' ' || c == '\t') { + /* Space : do nothing */ + } else if (c == '[') { + token = Token(Token::TOKEN_BRACKET_OPEN, c); + popPos(); + return token; + } else if (c == ']') { + token = Token(Token::TOKEN_BRACKET_CLOSE, c); + popPos(); + return token; + } else if (c == ':' && !hasOptions(OPTION_IGNORE_COLON)) { + token = Token(Token::TOKEN_COLON, c); + popPos(); + return token; + } else if (c == '=') { + token = Token(Token::TOKEN_EQUAL, c); + popPos(); + return token; + } else if (c == '\r' || c == '\n') { + token = Token(Token::TOKEN_EOL, c); + popPos(); + return token; + } else if (c == '#') { + token.type = Token::TOKEN_COMMENT; + state = LEXPARSING_STATE_COMMENT; + } else if (c == '"') { + /* Begin of QUOTED STRING */ + token.type = Token::TOKEN_QUOTED_STRING; + state = LEXPARSING_STATE_QUOTED_STRING; + } else if (c == '\\') { + /* Begin of STRING with escape */ + token.type = Token::TOKEN_STRING; + state = LEXPARSING_STATE_STRING; + escaped = true; + } else if (isgraph(c)) { + /* Begin of STRING */ + token.type = Token::TOKEN_STRING; + state = LEXPARSING_STATE_STRING; + token.str += c; + } else { + rewind(); + return Token(Token::TOKEN_UNKNOWN); + } + break; + } + case LEXPARSING_STATE_QUOTED_STRING: + { + if (c == '"') { + if (escaped) { + escaped = false; + token.str += '"'; + } else { + popPos(); + return token; + } + } else if (c == '\\') { + if (escaped) { + escaped = false; + token.str += '\\'; + } else { + escaped = true; + } + } else if (c == ' ' || c == '\t' || isgraph(c)) { + token.str += c; + } else if (c == '\r' || c == '\n') /* EOL */{ /* WTF ? consider it as correct ? */ back(); - popPos(); - return token; - } else if (c == 0) /* EOF */ { - popPos(); - return token; - } else /* Bad character ?? */ { - /* WTF ? Keep, Ignore ? */ - } - /* TODO What about other escaped character ? */ - break; - } - case LEXPARSING_STATE_STRING: - { - if (c == ' ' || c == '\t' || c == '"' || c == '#' || c == '[' || c == ']' || - (c == ':' && !hasOptions(OPTION_IGNORE_COLON)) || - c == '=') { - if (escaped) { - escaped = false; - token.str += c; - } else { - back(); - popPos(); - return token; - } - } else if (c == '\\') { - if (escaped) { - escaped = false; - token.str += c; - } else { - escaped = true; - } - } else if (c == '\r' || c == '\n') /* EOL */{ + popPos(); + return token; + } else if (c == 0) /* EOF */ { + popPos(); + return token; + } else /* Bad character ?? */ { + /* WTF ? Keep, Ignore ? */ + } + /* TODO What about other escaped character ? */ + break; + } + case LEXPARSING_STATE_STRING: + { + if (c == ' ' || c == '\t' || c == '"' || c == '#' || c == '[' || c == ']' + || (c == ':' && !hasOptions(OPTION_IGNORE_COLON)) + || c == '=' + ) { + if (escaped) { + escaped = false; + token.str += c; + } else { + back(); + popPos(); + return token; + } + } else if (c == '\\') { + if (escaped) { + escaped = false; + token.str += c; + } else { + escaped = true; + } + } else if (c == '\r' || c == '\n') /* EOL */{ back(); - popPos(); - return token; - } else if (c == 0) /* EOF */ { - popPos(); - return token; - }else if (isgraph(c)) { - token.str += c; - } else /* Bad character ?? */ { - /* WTF ? Keep, Ignore ? */ - } - /* TODO What about escaped character ? */ - break; - } - case LEXPARSING_STATE_COMMENT: - { - if (c == '\r' || c == '\n') { - return token; - } else { - token.str += c; - } - break; - } - default: - /* Must not occur. */ - break; - } - } - popPos(); - return token; + popPos(); + return token; + } else if (c == 0) /* EOF */ { + popPos(); + return token; + }else if (isgraph(c)) { + token.str += c; + } else /* Bad character ?? */ { + /* WTF ? Keep, Ignore ? */ + } + /* TODO What about escaped character ? */ + break; + } + case LEXPARSING_STATE_COMMENT: + { + if (c == '\r' || c == '\n') { + return token; + } else { + token.str += c; + } + 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 + default: + /* Must not occur. */ + 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 + } + } + popPos(); + return token; } std::list NutParser::parseLine() { - std::list res; - - while (true) { - NutParser::Token token = parseToken(); - - switch (token.type) { - case Token::TOKEN_STRING: - case Token::TOKEN_QUOTED_STRING: - case Token::TOKEN_BRACKET_OPEN: - case Token::TOKEN_BRACKET_CLOSE: - case Token::TOKEN_EQUAL: - case Token::TOKEN_COLON: - res.push_back(token); - break; - case Token::TOKEN_COMMENT: - res.push_back(token); - // Do not break, should return (EOL)Token::TOKEN_COMMENT: - case Token::TOKEN_UNKNOWN: - case Token::TOKEN_NONE: - case Token::TOKEN_EOL: - return res; - } - } + std::list res; + + while (true) { + NutParser::Token token = parseToken(); + + switch (token.type) { + case Token::TOKEN_STRING: + case Token::TOKEN_QUOTED_STRING: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_BRACKET_CLOSE: + case Token::TOKEN_EQUAL: + case Token::TOKEN_COLON: + res.push_back(token); + break; + case Token::TOKEN_COMMENT: + res.push_back(token); + // Should return (EOL)Token::TOKEN_COMMENT: + return res; + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_EOL: + return res; + } + } } // @@ -389,209 +426,284 @@ NutConfigParser::NutConfigParser(const std::string& buffer, unsigned int options NutParser(buffer, options) { } +void NutConfigParser::parseConfig(BaseConfiguration* config) { + NUT_UNUSED_VARIABLE(config); + parseConfig(); +} + void NutConfigParser::parseConfig() { - onParseBegin(); - - enum ConfigParserState { - CPS_DEFAULT, - CPS_SECTION_OPENED, - CPS_SECTION_HAVE_NAME, - CPS_SECTION_CLOSED, - CPS_DIRECTIVE_HAVE_NAME, - CPS_DIRECTIVE_VALUES - } state = CPS_DEFAULT; - - Token tok; - std::string name; - std::list values; - char sep; - while (tok = parseToken()) { - switch (state) { - case CPS_DEFAULT: - switch (tok.type) { - case Token::TOKEN_COMMENT: - onParseComment(tok.str); - /* Clean and return to default */ - break; - case Token::TOKEN_BRACKET_OPEN: - state = CPS_SECTION_OPENED; - break; - case Token::TOKEN_STRING: - case Token::TOKEN_QUOTED_STRING: - name = tok.str; - state = CPS_DIRECTIVE_HAVE_NAME; - break; - default: - /* WTF ? */ - break; - } - break; - case CPS_SECTION_OPENED: - switch (tok.type) { - case Token::TOKEN_STRING: - case Token::TOKEN_QUOTED_STRING: - /* Should occur ! */ - name = tok.str; - state = CPS_SECTION_HAVE_NAME; - break; - case Token::TOKEN_BRACKET_CLOSE: - /* Empty section name */ - state = CPS_SECTION_CLOSED; - break; - case Token::TOKEN_COMMENT: - /* Lack of closing bracket !!! */ - onParseSectionName(name, tok.str); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - case Token::TOKEN_EOL: - /* Lack of closing bracket !!! */ - onParseSectionName(name); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - default: - /* WTF ? */ - break; - } - break; - case CPS_SECTION_HAVE_NAME: - switch (tok.type) { - case Token::TOKEN_BRACKET_CLOSE: - /* Must occur ! */ - state = CPS_SECTION_CLOSED; - break; - case Token::TOKEN_COMMENT: - /* Lack of closing bracket !!! */ - onParseSectionName(name, tok.str); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - case Token::TOKEN_EOL: - /* Lack of closing bracket !!! */ - onParseSectionName(name); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - default: - /* WTF ? */ - break; - } - break; - case CPS_SECTION_CLOSED: - switch (tok.type) { - case Token::TOKEN_COMMENT: - /* Could occur ! */ - onParseSectionName(name, tok.str); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - case Token::TOKEN_EOL: - /* Could occur ! */ - onParseSectionName(name); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - default: - /* WTF ? */ - break; - } - break; - case CPS_DIRECTIVE_HAVE_NAME: - switch (tok.type) { - case Token::TOKEN_COMMENT: - /* Could occur ! */ - onParseDirective(name, 0, std::list (), tok.str); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - case Token::TOKEN_EOL: - /* Could occur ! */ - onParseDirective(name); - /* Clean and return to default */ - name.clear(); - state = CPS_DEFAULT; - break; - case Token::TOKEN_COLON: - case Token::TOKEN_EQUAL: - /* Could occur ! */ - sep = tok.str[0]; - state = CPS_DIRECTIVE_VALUES; - break; - case Token::TOKEN_STRING: - case Token::TOKEN_QUOTED_STRING: - /* Could occur ! */ - values.push_back(tok.str); - state = CPS_DIRECTIVE_VALUES; - break; - default: - /* WTF ? */ - break; - } - break; - case CPS_DIRECTIVE_VALUES: - switch (tok.type) { - case Token::TOKEN_COMMENT: - /* Could occur ! */ - onParseDirective(name, sep, values, tok.str); - /* Clean and return to default */ - name.clear(); - values.clear(); - sep = 0; - state = CPS_DEFAULT; - break; - case Token::TOKEN_EOL: - /* Could occur ! */ - onParseDirective(name, sep, values); - /* Clean and return to default */ - name.clear(); - values.clear(); - sep = 0; - state = CPS_DEFAULT; - break; - case Token::TOKEN_STRING: - case Token::TOKEN_QUOTED_STRING: - /* Could occur ! */ - values.push_back(tok.str); - state = CPS_DIRECTIVE_VALUES; - break; - default: - /* WTF ? */ - break; - } - break; - } - } + onParseBegin(); + + enum ConfigParserState { + CPS_DEFAULT, + CPS_SECTION_OPENED, + CPS_SECTION_HAVE_NAME, + CPS_SECTION_CLOSED, + CPS_DIRECTIVE_HAVE_NAME, + CPS_DIRECTIVE_VALUES + } state = CPS_DEFAULT; + + Token tok; + std::string name; + std::list values; + char sep = 0; + +#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 + while (1) { + tok = parseToken(); + if (!tok) + break; + switch (state) { + case CPS_DEFAULT: + switch (tok.type) { + case Token::TOKEN_COMMENT: + onParseComment(tok.str); + /* Clean and return to default */ + break; + case Token::TOKEN_BRACKET_OPEN: + state = CPS_SECTION_OPENED; + break; + case Token::TOKEN_STRING: + case Token::TOKEN_QUOTED_STRING: + name = tok.str; + state = CPS_DIRECTIVE_HAVE_NAME; + break; + + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_BRACKET_CLOSE: + case Token::TOKEN_EQUAL: + case Token::TOKEN_COLON: + case Token::TOKEN_EOL: + default: + /* WTF ? */ + break; + } + break; + case CPS_SECTION_OPENED: + switch (tok.type) { + case Token::TOKEN_STRING: + case Token::TOKEN_QUOTED_STRING: + /* Should occur ! */ + name = tok.str; + state = CPS_SECTION_HAVE_NAME; + break; + case Token::TOKEN_BRACKET_CLOSE: + /* Empty section name */ + state = CPS_SECTION_CLOSED; + break; + case Token::TOKEN_COMMENT: + /* Lack of closing bracket !!! */ + onParseSectionName(name, tok.str); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + case Token::TOKEN_EOL: + /* Lack of closing bracket !!! */ + onParseSectionName(name); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_EQUAL: + case Token::TOKEN_COLON: + default: + /* WTF ? */ + break; + } + break; + case CPS_SECTION_HAVE_NAME: + switch (tok.type) { + case Token::TOKEN_BRACKET_CLOSE: + /* Must occur ! */ + state = CPS_SECTION_CLOSED; + break; + case Token::TOKEN_COMMENT: + /* Lack of closing bracket !!! */ + onParseSectionName(name, tok.str); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + case Token::TOKEN_EOL: + /* Lack of closing bracket !!! */ + onParseSectionName(name); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + + case Token::TOKEN_QUOTED_STRING: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_COLON: + case Token::TOKEN_EQUAL: + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_STRING: + default: + /* WTF ? */ + break; + } + break; + case CPS_SECTION_CLOSED: + switch (tok.type) { + case Token::TOKEN_COMMENT: + /* Could occur ! */ + onParseSectionName(name, tok.str); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + case Token::TOKEN_EOL: + /* Could occur ! */ + onParseSectionName(name); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + + case Token::TOKEN_QUOTED_STRING: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_BRACKET_CLOSE: + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_STRING: + case Token::TOKEN_COLON: + case Token::TOKEN_EQUAL: + default: + /* WTF ? */ + break; + } + break; + case CPS_DIRECTIVE_HAVE_NAME: + switch (tok.type) { + case Token::TOKEN_COMMENT: + /* Could occur ! */ + onParseDirective(name, 0, std::list (), tok.str); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + case Token::TOKEN_EOL: + /* Could occur ! */ + onParseDirective(name); + /* Clean and return to default */ + name.clear(); + state = CPS_DEFAULT; + break; + case Token::TOKEN_COLON: + case Token::TOKEN_EQUAL: + /* Could occur ! */ + sep = tok.str[0]; + state = CPS_DIRECTIVE_VALUES; + break; + case Token::TOKEN_STRING: + case Token::TOKEN_QUOTED_STRING: + /* Could occur ! */ + values.push_back(tok.str); + state = CPS_DIRECTIVE_VALUES; + break; + + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_BRACKET_CLOSE: + default: + /* WTF ? */ + break; + } + break; + case CPS_DIRECTIVE_VALUES: + switch (tok.type) { + case Token::TOKEN_COMMENT: + /* Could occur ! */ + onParseDirective(name, sep, values, tok.str); + /* Clean and return to default */ + name.clear(); + values.clear(); + sep = 0; + state = CPS_DEFAULT; + break; + case Token::TOKEN_EOL: + /* Could occur ! */ + onParseDirective(name, sep, values); + /* Clean and return to default */ + name.clear(); + values.clear(); + sep = 0; + state = CPS_DEFAULT; + break; + case Token::TOKEN_STRING: + case Token::TOKEN_QUOTED_STRING: + /* Could occur ! */ + values.push_back(tok.str); + state = CPS_DIRECTIVE_VALUES; + break; + + case Token::TOKEN_UNKNOWN: + case Token::TOKEN_NONE: + case Token::TOKEN_BRACKET_OPEN: + case Token::TOKEN_BRACKET_CLOSE: + case Token::TOKEN_EQUAL: + case Token::TOKEN_COLON: + default: + /* WTF ? */ + break; + } + break; + } + } switch(state) { - case CPS_SECTION_OPENED: - case CPS_SECTION_HAVE_NAME: - /* Lack of closing bracket !!! */ - onParseSectionName(name); - break; - case CPS_SECTION_CLOSED: - /* Could occur ! */ - onParseSectionName(name); - break; - case CPS_DIRECTIVE_HAVE_NAME: - /* Could occur ! */ - onParseDirective(name); - break; - case CPS_DIRECTIVE_VALUES: - /* Could occur ! */ - onParseDirective(name, sep, values); - break; + case CPS_SECTION_OPENED: + case CPS_SECTION_HAVE_NAME: + /* Lack of closing bracket !!! */ + onParseSectionName(name); + break; + case CPS_SECTION_CLOSED: + /* Could occur ! */ + onParseSectionName(name); + break; + case CPS_DIRECTIVE_HAVE_NAME: + /* Could occur ! */ + onParseDirective(name); + break; + case CPS_DIRECTIVE_VALUES: + /* Could occur ! */ + onParseDirective(name, sep, values); + break; + case CPS_DEFAULT: + /* TOTHINK: no-op? */ + 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 - onParseEnd(); + onParseEnd(); } @@ -609,44 +721,44 @@ NutConfigParser(buffer) { } void DefaultConfigParser::onParseBegin() { - // Start with empty section (ie global one) - _section.clear(); + // Start with empty section (i.e. global one) + _section.clear(); } void DefaultConfigParser::onParseComment(const std::string& /*comment*/) { - // Comment are ignored for now + // Comments are ignored for now } void DefaultConfigParser::onParseSectionName(const std::string& sectionName, const std::string& /*comment*/) { - // Comment are ignored for now + // Comments are ignored for now - // Process current section. - if (!_section.empty()) { - onParseSection(_section); - _section.clear(); - } + // Process current section. + if (!_section.empty()) { + onParseSection(_section); + _section.clear(); + } - // Start a new section - _section.name = sectionName; + // Start a new section + _section.name = sectionName; } void DefaultConfigParser::onParseDirective(const std::string& directiveName, char /*sep*/, const ConfigParamList& values, const std::string& /*comment*/) { - // Comment are ignored for now - // Separator has no specific semantic in this context + // Comments are ignored for now + // Separator has no specific semantic in this context - // Save values - _section.entries[directiveName].name = directiveName; - _section.entries[directiveName].values = values; + // Save values + _section.entries[directiveName].name = directiveName; + _section.entries[directiveName].values = values; // TODO Can probably be optimized. } void DefaultConfigParser::onParseEnd() { - // Process current (last) section - if (!_section.empty()) { - onParseSection(_section); - _section.clear(); - } + // Process current (last) section + if (!_section.empty()) { + onParseSection(_section); + _section.clear(); + } } @@ -655,12 +767,12 @@ void DefaultConfigParser::onParseEnd() { // bool GenericConfigSection::empty()const { - return name.empty() && entries.empty(); + return name.empty() && entries.empty(); } void GenericConfigSection::clear() { - name.clear(); - entries.clear(); + name.clear(); + entries.clear(); } // @@ -669,30 +781,30 @@ void GenericConfigSection::clear() { GenericConfigParser::GenericConfigParser(const char* buffer): DefaultConfigParser(buffer), -_config(NULL) +_config(nullptr) { } GenericConfigParser::GenericConfigParser(const std::string& buffer): DefaultConfigParser(buffer), -_config(NULL) +_config(nullptr) { } void GenericConfigParser::parseConfig(BaseConfiguration* config) { - if(config!=NULL) + if(config!=nullptr) { _config = config; NutConfigParser::parseConfig(); - _config = NULL; + _config = nullptr; } } void GenericConfigParser::onParseSection(const GenericConfigSection& section) { - if(_config!=NULL) + if(_config!=nullptr) { _config->setGenericConfigSection(section); } @@ -896,7 +1008,7 @@ bool GenericConfiguration::str2bool(const std::string & str) { if ("true" == str) return true; if ("on" == str) return true; - if ("1" == str) return true; + if ("1" == str) return true; if ("yes" == str) return true; if ("ok" == str) return true; @@ -923,8 +1035,8 @@ UpsmonConfiguration::UpsmonConfiguration() void UpsmonConfiguration::parseFromString(const std::string& str) { - UpsmonConfigParser parser(str); - parser.parseUpsmonConfig(this); + UpsmonConfigParser parser(str); + parser.parseUpsmonConfig(this); } UpsmonConfiguration::NotifyFlag UpsmonConfiguration::NotifyFlagFromString(const std::string& str) @@ -1006,29 +1118,29 @@ NutConfigParser(buffer) void UpsmonConfigParser::parseUpsmonConfig(UpsmonConfiguration* config) { - if(config!=NULL) + if(config!=nullptr) { _config = config; NutConfigParser::parseConfig(); - _config = NULL; + _config = nullptr; } } void UpsmonConfigParser::onParseBegin() { - // Do nothing + // Do nothing } void UpsmonConfigParser::onParseComment(const std::string& /*comment*/) { - // Comment are ignored for now + // Comments are ignored for now } void UpsmonConfigParser::onParseSectionName(const std::string& /*sectionName*/, const std::string& /*comment*/) { - // There must not have sections in upsm.conf. - // Ignore it - // TODO Add error reporting ? + // There must not be sections in upsmon.conf. + // Ignore it + // TODO Add error reporting ? } @@ -1051,11 +1163,17 @@ void UpsmonConfigParser::onParseDirective(const std::string& directiveName, char { UpsmonConfiguration::Monitor monitor; ConfigParamList::const_iterator it = values.begin(); - std::stringstream system(*it++); - std::string word; - monitor.upsname = (getline(system, word, '@'), word); - monitor.hostname = (getline(system, word), word); - monitor.port = (values.size() == 6 ? *StringToSettableNumber(*it++) : 0u); + std::stringstream upsAtHost(*it++); + std::string wordToken; + /* + * Why didn't the original author just receive the words + * into their target strings?.. e.g.: + * std::getline(upsAtHost, monitor.upsname, '@'); + * std::getline(upsAtHost, monitor.hostname); + */ + monitor.upsname = (static_cast(std::getline(upsAtHost, wordToken, '@')), wordToken); + monitor.hostname = (static_cast(std::getline(upsAtHost, wordToken)), wordToken); + monitor.port = (values.size() == 6 ? *StringToSettableNumber(*it++) : 0u); monitor.powerValue = StringToSettableNumber(*it++); monitor.username = *it++; monitor.password = *it++; @@ -1126,7 +1244,7 @@ void UpsmonConfigParser::onParseDirective(const std::string& directiveName, char UpsmonConfiguration::NotifyType type = UpsmonConfiguration::NotifyTypeFromString(values.front()); if(type!=UpsmonConfiguration::NOTIFY_TYPE_MAX) { - _config->notifyMessages[(unsigned int)type] = *(++values.begin()); + _config->notifyMessages[static_cast(type)] = *(++values.begin()); } } } @@ -1140,11 +1258,11 @@ void UpsmonConfigParser::onParseDirective(const std::string& directiveName, char unsigned int flags = 0; std::string word; std::stringstream stream(*(++values.begin())); - while( getline(stream, word, '+') ) + while( std::getline(stream, word, '+') ) { flags |= UpsmonConfiguration::NotifyFlagFromString(word); } - _config->notifyFlags[(unsigned int)type] = flags; + _config->notifyFlags[static_cast(type)] = flags; } } } @@ -1178,7 +1296,7 @@ void UpsmonConfigParser::onParseDirective(const std::string& directiveName, char void UpsmonConfigParser::onParseEnd() { - // Do nothing + // Do nothing } // @@ -1192,8 +1310,8 @@ NutConfiguration::NutConfiguration(): void NutConfiguration::parseFromString(const std::string& str) { - NutConfConfigParser parser(str); - parser.parseNutConfConfig(this); + NutConfConfigParser parser(str); + parser.parseNutConfConfig(this); } NutConfiguration::NutMode NutConfiguration::NutModeFromString(const std::string& str) @@ -1253,34 +1371,34 @@ NutConfigParser(buffer) void NutConfConfigParser::parseNutConfConfig(NutConfiguration* config) { - if(config!=NULL) + if(config!=nullptr) { _config = config; NutConfigParser::parseConfig(); - _config = NULL; + _config = nullptr; } } void NutConfConfigParser::onParseBegin() { - // Do nothing + // Do nothing } void NutConfConfigParser::onParseComment(const std::string& /*comment*/) { - // Comment are ignored for now + // Comments are ignored for now } void NutConfConfigParser::onParseSectionName(const std::string& /*sectionName*/, const std::string& /*comment*/) { - // There must not have sections in upsm.conf. - // Ignore it - // TODO Add error reporting ? + // There must not be sections in nutconf.conf. + // Ignore it + // TODO Add error reporting ? } void NutConfConfigParser::onParseDirective(const std::string& directiveName, char /*sep*/, const ConfigParamList& values, const std::string& /*comment*/) { - // Comment are ignored for now + // Comments are ignored for now // NOTE: although sep must be '=', sep is not verified. if(_config && directiveName=="MODE" && values.size()==1) { @@ -1297,7 +1415,7 @@ void NutConfConfigParser::onParseDirective(const std::string& directiveName, cha void NutConfConfigParser::onParseEnd() { - // Do nothing + // Do nothing } @@ -1311,8 +1429,8 @@ UpsdConfiguration::UpsdConfiguration() void UpsdConfiguration::parseFromString(const std::string& str) { - UpsdConfigParser parser(str); - parser.parseUpsdConfig(this); + UpsdConfigParser parser(str); + parser.parseUpsdConfig(this); } @@ -1354,34 +1472,39 @@ NutConfigParser(buffer, NutParser::OPTION_IGNORE_COLON) void UpsdConfigParser::parseUpsdConfig(UpsdConfiguration* config) { - if(config!=NULL) + if(config!=nullptr) { _config = config; NutConfigParser::parseConfig(); - _config = NULL; + _config = nullptr; } } void UpsdConfigParser::onParseBegin() { - // Do nothing + // Do nothing } void UpsdConfigParser::onParseComment(const std::string& comment) { - // Comment are ignored for now + // Comments are ignored for now + NUT_UNUSED_VARIABLE(comment); } void UpsdConfigParser::onParseSectionName(const std::string& sectionName, const std::string& comment) { - // There must not have sections in upsm.conf. - // Ignore it - // TODO Add error reporting ? + // There must not be sections in upsd.conf. + // Ignore it + // TODO Add error reporting ? + NUT_UNUSED_VARIABLE(sectionName); + NUT_UNUSED_VARIABLE(comment); } void UpsdConfigParser::onParseDirective(const std::string& directiveName, char sep, const ConfigParamList& values, const std::string& comment) { // NOTE: separators are always ignored + NUT_UNUSED_VARIABLE(sep); + NUT_UNUSED_VARIABLE(comment); if(_config) { @@ -1421,7 +1544,7 @@ void UpsdConfigParser::onParseDirective(const std::string& directiveName, char s listen.address = values.front(); if(values.size()==2) { - listen.port = StringToSettableNumber(*(++values.begin())); + listen.port = StringToSettableNumber(*(++values.begin())); } _config->listens.push_back(listen); } @@ -1435,7 +1558,7 @@ void UpsdConfigParser::onParseDirective(const std::string& directiveName, char s void UpsdConfigParser::onParseEnd() { - // Do nothing + // Do nothing } diff --git a/common/nutipc.cpp b/common/nutipc.cpp index 0b75632193..eaa2829f8e 100644 --- a/common/nutipc.cpp +++ b/common/nutipc.cpp @@ -1,40 +1,76 @@ -/* nutipc.cpp - NUT IPC +/* + nutipc.cpp - NUT IPC - Copyright (C) 2012 Eaton + Copyright (C) 2012 Eaton - Author: Vaclav Krpec + Author: Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + +/* For C++ code below, we do not actually use the fallback time methods + * (on mingw mostly), but in C++ context they happen to conflict with + * time.h or ctime headers, while native-C does not. Just disable the + * fallback localtime_r(), gmtime_r() etc. if/when NUT common.h gets + * included by the header chain: + */ +#ifndef HAVE_GMTIME_R +# define HAVE_GMTIME_R 111 +#endif +#ifndef HAVE_LOCALTIME_R +# define HAVE_LOCALTIME_R 111 +#endif + #include "nutipc.hpp" #include "nutstream.hpp" -#include "config.h" #include namespace nut { -pid_t Process::getPID() throw() { +/* Trivial implementations out of class declaration to avoid + * error: 'ClassName' has no out-of-line virtual method definitions; its vtable + * will be emitted in every translation unit [-Werror,-Wweak-vtables] + */ +Process::Main::~Main() {} +Signal::Handler::~Handler() {} + +pid_t Process::getPID() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ return getpid(); } -pid_t Process::getPPID() throw() { +pid_t Process::getPPID() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ +#ifdef WIN32 + /* FIXME: Detect HAVE_GETPPID in configure; throw exceptions here?.. + * NOTE: Does not seem to be currently used in nutconf codebase. */ + return -1; +#else return getppid(); +#endif } @@ -133,7 +169,7 @@ Process::Executor::Executor(const std::string & command) { int Process::Executor::operator () () -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif { @@ -151,14 +187,14 @@ int Process::Executor::operator () () args_c_str[i] = (*arg).c_str(); } - args_c_str[i] = NULL; + args_c_str[i] = nullptr; - int status = ::execvp(bin_c_str, (char * const *)args_c_str); + int status = ::execvp(bin_c_str, const_cast(args_c_str)); // Upon successful execution, the execvp function never returns // (since the process context is replaced, completely) - delete args_c_str; + delete[] args_c_str; std::stringstream e; @@ -169,7 +205,7 @@ int Process::Executor::operator () () int sigPipeWriteCmd(int fh, void * cmd, size_t cmd_size) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif { @@ -178,11 +214,11 @@ int sigPipeWriteCmd(int fh, void * cmd, size_t cmd_size) do { ssize_t written = ::write(fh, cmd_bytes, cmd_size); - if (-1 == written) + if (written < 0) return errno; cmd_bytes += written; - cmd_size -= written; + cmd_size -= static_cast(written); } while (cmd_size); @@ -191,12 +227,21 @@ int sigPipeWriteCmd(int fh, void * cmd, size_t cmd_size) int Signal::send(Signal::enum_t signame, pid_t pid) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif { - int sig = (int)signame; + int sig = static_cast(signame); +#ifdef WIN32 + /* FIXME: Implement (for NUT processes) via pipes? + * See e.g. upsdrvctl implementation. */ + std::stringstream e; + + e << "Can't send signal " << sig << " to PID " << pid << + ": not implemented on this platform yet"; + throw std::logic_error(e.str()); +#else int status = ::kill(pid, sig); if (0 == status) @@ -210,6 +255,7 @@ int Signal::send(Signal::enum_t signame, pid_t pid) e << "Can't send invalid signal " << sig; throw std::logic_error(e.str()); +#endif } diff --git a/common/nutstream.cpp b/common/nutstream.cpp index c713c6e39c..555cc44f15 100644 --- a/common/nutstream.cpp +++ b/common/nutstream.cpp @@ -1,23 +1,26 @@ -/* nutstream.cpp - NUT stream +/* + nutstream.cpp - NUT stream - Copyright (C) + Copyright (C) 2012 Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + #include "nutstream.hpp" #include @@ -28,18 +31,117 @@ #include extern "C" { + +/* For C++ code below, we do not actually use the fallback time methods + * (on mingw mostly), but in C++ context they happen to conflict with + * time.h or ctime headers, while native-C does not. Just disable the + * fallback localtime_r(), gmtime_r() etc. if/when NUT timehead.h gets + * included by the header chain from common.h: + */ +#ifndef HAVE_GMTIME_R +# define HAVE_GMTIME_R 111 +#endif +#ifndef HAVE_LOCALTIME_R +# define HAVE_LOCALTIME_R 111 +#endif +#include "common.h" + #include -#include -#include +#include #include #include -#include -#include + +/* Windows/Linux Socket compatibility layer, lifted from nutclient.cpp + * (note we do not use wincompat.h here as it slightly conflicts, with + * extern vs. static etc.) */ +/* Thanks to Benjamin Roux (http://broux.developpez.com/articles/c/sockets/) */ +#ifdef WIN32 +# define SOCK_OPT_CAST(x) reinterpret_cast(x) + +/* equivalent of W32_NETWORK_CALL_OVERRIDE + * invoked by wincompat.h in upsclient.c: + */ +static inline int sktconnect(int fh, struct sockaddr * name, int len) +{ + int ret = connect(fh,name,len); + errno = WSAGetLastError(); + return ret; +} +static inline int sktread(int fh, void *buf, int size) +{ + int ret = recv(fh,(char*)buf,size,0); + errno = WSAGetLastError(); + return ret; +} +static inline int sktwrite(int fh, const void*buf, int size) +{ + int ret = send(fh,(char*)buf,size,0); + errno = WSAGetLastError(); + return ret; +} +static inline int sktclose(int fh) +{ + int ret = closesocket((SOCKET)fh); + errno = WSAGetLastError(); + return ret; +} + +#else /* not WIN32 */ + +# include +# include +# include +# include +# include +# include /* gethostbyname */ +# include +# ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +# endif +# ifndef SOCKET_ERROR +# define SOCKET_ERROR -1 +# endif +# ifndef closesocket +# define closesocket(s) close(s) +# endif +# define SOCK_OPT_CAST(x) reinterpret_cast(x) + typedef int SOCKET; + typedef struct sockaddr_in SOCKADDR_IN; + typedef struct sockaddr SOCKADDR; + typedef struct in_addr IN_ADDR; + +# define sktconnect(h,n,l) ::connect(h,n,l) +# define sktread(h,b,s) ::read(h,b,s) +# define sktwrite(h,b,s) ::write(h,b,s) +# define sktclose(h) ::close(h) + +/* WA for Solaris/i386 bug: non-blocking connect sets errno to ENOENT */ +# if (defined NUT_PLATFORM_SOLARIS) +# define SOLARIS_i386_NBCONNECT_ENOENT(status) ( (!strcmp("i386", CPU_TYPE)) ? (ENOENT == (status)) : 0 ) +# else +# define SOLARIS_i386_NBCONNECT_ENOENT(status) (0) +# endif /* end of Solaris/i386 WA for non-blocking connect */ + +/* WA for AIX bug: non-blocking connect sets errno to 0 */ +# if (defined NUT_PLATFORM_AIX) +# define AIX_NBCONNECT_0(status) (0 == (status)) +# else +# define AIX_NBCONNECT_0(status) (0) +# endif /* end of AIX WA for non-blocking connect */ + +#endif /* WIN32 */ +/* End of Windows/Linux Socket compatibility layer */ } namespace nut { +/* Trivial implementation out of class declaration to avoid + * error: 'ClassName' has no out-of-line virtual method definitions; its vtable + * will be emitted in every translation unit [-Werror,-Wweak-vtables] + */ +NutStream::~NutStream() {} + NutStream::status_t NutMemory::getChar(char & ch) { if (m_pos == m_impl.size()) return NUTS_EOF; @@ -87,31 +189,186 @@ NutStream::status_t NutMemory::putData(const std::string & data) { } -const std::string NutFile::m_tmp_dir("/var/tmp"); +/* Here we align with OS envvars like TMPDIR or TEMPDIR, + * consider portability to Windows, or use of tmpfs like + * /dev/shm or (/var)/run on some platforms - e.g. NUT + * STATEPATH, (ALT)PIDPATH or similar locations desired + * by packager who knows their system. */ + +static bool checkExistsWritableDir(const char *s) { +#ifdef DEBUG + std::cerr << "checkExistsWritableDir(" << (s ? s : "") << "): "; +#endif + if (!s || *s == '\0') { +#ifdef DEBUG + std::cerr << "null or empty string" << std::endl; +#endif + return false; + } + + if (!opendir(s)) { +#ifdef DEBUG + std::cerr << "not a dir" << std::endl; +#endif + return false; + } + + /* POSIX: If the requested access is permitted, access() succeeds + * and shall return 0; otherwise, -1 shall be returned and errno + * shall be set to indicate the error. */ + if (access(s, X_OK)) { +#ifdef DEBUG + std::cerr << "not traversable" << std::endl; +#endif + return false; + } + + if (access(s, W_OK)) { +#ifdef DEBUG + std::cerr << "not writeable" << std::endl; +#endif + return false; + } + +#ifdef DEBUG + std::cerr << "is ok" << std::endl; +#endif + return true; +} + +static const char* getTmpDirPath() { + const char *s; + +#ifdef WIN32 + /* Suggestions from https://sourceforge.net/p/mingw/bugs/666/ */ + static char pathbuf[MAX_PATH]; + int i; +#endif + + if (checkExistsWritableDir(s = ::altpidpath())) + return s; + + /* NOTE: For C++17 or newer we might also call + * https://en.cppreference.com/w/cpp/filesystem/temp_directory_path + */ +#ifdef WIN32 + i = GetTempPathA(sizeof(pathbuf), pathbuf); + if ((i > 0) && (i < MAX_PATH) && checkExistsWritableDir(pathbuf)) + return (const char *)pathbuf; +#endif + + if (checkExistsWritableDir(s = ::getenv("TMPDIR"))) + return s; + if (checkExistsWritableDir(s = ::getenv("TEMPDIR"))) + return s; + if (checkExistsWritableDir(s = ::getenv("TEMP"))) + return s; + if (checkExistsWritableDir(s = ::getenv("TMP"))) + return s; + + /* Some OS-dependent locations */ +#ifdef WIN32 + if (checkExistsWritableDir(s = "C:\\Temp")) + return s; + if (checkExistsWritableDir(s = "C:\\Windows\\Temp")) + return s; + if (checkExistsWritableDir(s = "/c/Temp")) + return s; + if (checkExistsWritableDir(s = "/c/Windows/Temp")) + return s; +#else + if (checkExistsWritableDir(s = "/dev/shm")) + return s; + if (checkExistsWritableDir(s = "/run")) + return s; + if (checkExistsWritableDir(s = "/var/run")) + return s; +#endif + + /* May be applicable to WIN32 depending on emulation environment/mapping */ + if (checkExistsWritableDir(s = "/tmp")) + return s; + if (checkExistsWritableDir(s = "/var/tmp")) + return s; + + /* Maybe ok, for tests at least: a current working directory */ + if (checkExistsWritableDir(s = ".")) + return s; + + return "/tmp"; +} + +const std::string NutFile::m_tmp_dir(getTmpDirPath()); NutFile::NutFile(anonymous_t): - m_impl(NULL), + m_name(""), + m_impl(nullptr), m_current_ch('\0'), m_current_ch_valid(false) { +#ifdef WIN32 + /* Suggestions from https://sourceforge.net/p/mingw/bugs/666/ because + * msvcrt tmpfile() uses C: root dir and lacks permissions to actually + * use it, and mingw tends to call that OS method so far */ + char filename[MAX_PATH]; + memset(filename, 0, sizeof(filename)); + + GetTempFileNameA(m_tmp_dir.c_str(), "nuttemp", 0, filename); + /* if (verbose) std::cerr << "TMP FILE: " << filename << std::endl; */ + std::string mode_str = std::string(strAccessMode(READ_WRITE_CLEAR)); + /* ...Still, we ask to auto-delete where supported: */ + mode_str += std::string("D"); + /* Per https://en.cppreference.com/w/cpp/io/c/tmpfile it is binary + * for POSIX code, so match the behavior here: */ + mode_str += std::string("b"); + m_impl = ::fopen(filename, mode_str.c_str()); + /* If it were not "const" we might assign it. But got no big need to. + * m_name = std::string(filename); + */ +#else + /* TOTHINK: How to make this use m_tmp_dir? Is it possible generally? */ + /* Safer than tmpnam() but we don't know the filename here. + * Not that we need it, system should auto-delete it. */ m_impl = ::tmpfile(); +#endif - if (NULL == m_impl) { + if (nullptr == m_impl) { int err_code = errno; std::stringstream e; - e << "Failed to create temporary file: " << err_code << ": " << ::strerror(err_code); + e << "Failed to create temporary file: " << err_code + << ": " << ::strerror(err_code); + +#ifdef WIN32 + e << ": tried using temporary location " << m_tmp_dir; + if (filename[0] != '\0') + e << ": OS suggested filename " << filename; +#endif throw std::runtime_error(e.str()); } } -bool NutFile::exists(int & err_code, std::string & err_msg) const throw() { +bool NutFile::exists(int & err_code, std::string & err_msg) const +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ struct stat info; + int status; + + /* An opened file pointer is assumed to be backed by a file at this + * moment (even if unlinked, temporary with unknown name, etc.) */ + if (m_impl != nullptr) + return true; + + /* Can not stat an unknown file name */ + if (m_name.empty()) + return false; - int status = ::stat(m_name.c_str(), &info); + status = ::stat(m_name.c_str(), &info); if (!status) return true; @@ -123,7 +380,13 @@ bool NutFile::exists(int & err_code, std::string & err_msg) const throw() { } -bool NutFile::open(access_t mode, int & err_code, std::string & err_msg) throw() { +const char * NutFile::strAccessMode(access_t mode) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + /* NOTE: Currently we use OS-default binary/text choice of content mode + * since we primarily expect to manipulate user-editable config files */ static const char *read_only = "r"; static const char *write_only = "w"; static const char *read_write = "r+"; @@ -131,7 +394,7 @@ bool NutFile::open(access_t mode, int & err_code, std::string & err_msg) throw() static const char *append_only = "a"; static const char *read_append = "a+"; - const char *mode_str = NULL; + const char *mode_str = nullptr; switch (mode) { case READ_ONLY: @@ -154,21 +417,98 @@ bool NutFile::open(access_t mode, int & err_code, std::string & err_msg) throw() break; } - assert(NULL != mode_str); + assert(nullptr != mode_str); + + return mode_str; +} + +bool NutFile::open(access_t mode, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + const char *mode_str; + /* Can not open an unknown file name */ + if (m_name.empty()) { + err_code = ENOENT; + err_msg = std::string("No file name was specified"); + return false; + } + + if (nullptr != m_impl) { + /* TOTHINK: Should we care about errors in this close()? */ + ::fclose(m_impl); + } + +#ifdef WIN32 + /* This currently fails with mingw due to looking at POSIXified paths: + * - Failed to open file /c/Users/abuild/Documents/FOSS/nut/conf/nut.conf.sample: 2: No such file or directory + * while the file does exist (for git-bash and mingw shells): + * $ ls -la /c/Users/abuild/Documents/FOSS/nut/conf/nut.conf.sample + * -rw-r--r-- 1 abuild Users 4774 Jan 28 03:38 /c/Users/abuild/Documents/FOSS/nut/conf/nut.conf.sample + * + * For the test suite it is not a great problem, can be fixed + * by using `cygpath` or `pwd -W` in `configure` script. + * The run-time behavior is more troublesome: per discussion at + * https://sourceforge.net/p/mingw/mailman/mingw-users/thread/gq8fi0$pk0$2@ger.gmane.org/ + * mingw uses fopen() from msvcrt directly, and it does not know + * such paths (e.g. '/c/Users/...' means "C:\\c\\Users\\..." to it). + * Paths coming from MSYS shell arguments are handled by the shell, + * and this is more than about slash type (WINNT is okay with both), + * but also e.g. prefixing an msys installation path 'C:\\msys64' or + * similar when using absolute POSIX-style paths. Do we need a private + * converter?.. Would an end user have MSYS installed at all? + */ +#endif + + mode_str = strAccessMode(mode); m_impl = ::fopen(m_name.c_str(), mode_str); - if (NULL != m_impl) + if (nullptr != m_impl) return true; err_code = errno; - err_msg = std::string(::strerror(err_code)); + err_msg = "Failed to open file '" + m_name + "': " + + std::string(::strerror(err_code)); return false; } -bool NutFile::close(int & err_code, std::string & err_msg) throw() { +bool NutFile::flush(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + if (nullptr == m_impl) { + err_code = EBADF; + err_msg = std::string(::strerror(err_code)); + return false; + } + + err_code = ::fflush(m_impl); + + if (0 != err_code) { + err_msg = std::string(::strerror(err_code)); + + return false; + } + + return true; +} + + +bool NutFile::close(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + /* Already closed (or never opened) - goal achieved */ + if (nullptr == m_impl) { + return true; + } + err_code = ::fclose(m_impl); if (0 != err_code) { @@ -177,13 +517,28 @@ bool NutFile::close(int & err_code, std::string & err_msg) throw() { return false; } - m_impl = NULL; + m_impl = nullptr; return true; } -bool NutFile::remove(int & err_code, std::string & err_msg) throw() { +bool NutFile::remove(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + /* Can not unlink an unknown file name */ + if (m_name.empty()) { + err_code = ENOENT; + err_msg = std::string("No file name was specified"); + return false; + } + + /* FWIW, note that if the file descriptor is opened by this + * or any other process(es), it remains usable only by them + * after un-linking, and the file system resources would be + * released only when everyone closes it. */ err_code = ::unlink(m_name.c_str()); if (0 != err_code) { @@ -200,7 +555,7 @@ bool NutFile::remove(int & err_code, std::string & err_msg) throw() { NutFile::NutFile(const std::string & name, access_t mode): m_name(name), - m_impl(NULL), + m_impl(nullptr), m_current_ch('\0'), m_current_ch_valid(false) { @@ -208,32 +563,22 @@ NutFile::NutFile(const std::string & name, access_t mode): } -std::string NutFile::tmpName() -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) -#endif +NutFile::NutFile(access_t mode): + NutFile(ANONYMOUS) { - char *tmp_name = ::tempnam(m_tmp_dir.c_str(), NULL); + const char *mode_str = strAccessMode(mode); + m_impl = ::freopen(nullptr, mode_str, m_impl); - if (NULL == tmp_name) - throw std::runtime_error( - "Failed to create temporary file name"); - - std::string tmp_name_str(tmp_name); - - ::free(tmp_name); - - return tmp_name_str; -} + if (nullptr == m_impl) { + int err_code = errno; + std::stringstream e; + e << "Failed to re-open temporary file with mode '" << mode_str + << "': " << err_code + << ": " << ::strerror(err_code); -NutFile::NutFile(access_t mode): - m_name(tmpName()), - m_impl(NULL), - m_current_ch('\0'), - m_current_ch_valid(false) -{ - openx(mode); + throw std::runtime_error(e.str()); + } } @@ -248,7 +593,7 @@ NutFile::NutFile(access_t mode): * \retval NUTS_ERROR on read error */ inline static NutStream::status_t fgetcWrapper(FILE * file, char & ch) { - assert(NULL != file); + assert(nullptr != file); errno = 0; @@ -267,14 +612,18 @@ inline static NutStream::status_t fgetcWrapper(FILE * file, char & ch) { } -NutStream::status_t NutFile::getChar(char & ch) throw() { +NutStream::status_t NutFile::getChar(char & ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ if (m_current_ch_valid) { ch = m_current_ch; return NUTS_OK; } - if (NULL == m_impl) + if (nullptr == m_impl) return NUTS_ERROR; status_t status = fgetcWrapper(m_impl, ch); @@ -290,18 +639,26 @@ NutStream::status_t NutFile::getChar(char & ch) throw() { } -void NutFile::readChar() throw() { +void NutFile::readChar() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ m_current_ch_valid = false; } -NutStream::status_t NutFile::getString(std::string & str) throw() { +NutStream::status_t NutFile::getString(std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ if (m_current_ch_valid) str += m_current_ch; m_current_ch_valid = false; - if (NULL == m_impl) + if (nullptr == m_impl) return NUTS_ERROR; // Note that ::fgetc is used instead of ::fgets @@ -322,10 +679,14 @@ NutStream::status_t NutFile::getString(std::string & str) throw() { } -NutStream::status_t NutFile::putChar(char ch) throw() { +NutStream::status_t NutFile::putChar(char ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ int c; - if (NULL == m_impl) + if (nullptr == m_impl) return NUTS_ERROR; c = ::fputc(static_cast(ch), m_impl); @@ -334,10 +695,14 @@ NutStream::status_t NutFile::putChar(char ch) throw() { } -NutStream::status_t NutFile::putString(const std::string & str) throw() { +NutStream::status_t NutFile::putString(const std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ int c; - if (NULL == m_impl) + if (nullptr == m_impl) return NUTS_ERROR; c = ::fputs(str.c_str(), m_impl); @@ -346,7 +711,11 @@ NutStream::status_t NutFile::putString(const std::string & str) throw() { } -NutStream::status_t NutFile::putData(const std::string & data) throw() { +NutStream::status_t NutFile::putData(const std::string & data) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ // Unfortunately, C FILE interface doesn't have non C-string // put function (i.e. function for raw data output with size specifier for (size_t i = 0; i < data.size(); ++i) { @@ -361,15 +730,26 @@ NutStream::status_t NutFile::putData(const std::string & data) throw() { NutFile::~NutFile() { - if (NULL != m_impl) + if (nullptr != m_impl) closex(); } void NutSocket::Address::init_unix(Address & addr, const std::string & path) { - struct sockaddr_un * un_addr = (struct sockaddr_un *)::malloc(sizeof(struct sockaddr_un)); +#ifdef WIN32 + /* FIXME: Windows pipes where available? See e.g. clone drivers */ + NUT_UNUSED_VARIABLE(addr); + NUT_UNUSED_VARIABLE(path); + + std::stringstream e; + e << "Unix sockets not implemented for this platform yet: " << path; +// addr.str() << ":" << path; - if (NULL == un_addr) + throw std::logic_error(e.str()); +#else + struct sockaddr_un * un_addr = reinterpret_cast(::malloc(sizeof(struct sockaddr_un))); + + if (nullptr == un_addr) throw std::bad_alloc(); un_addr->sun_family = AF_UNIX; @@ -383,6 +763,7 @@ void NutSocket::Address::init_unix(Address & addr, const std::string & path) { addr.m_sock_addr = reinterpret_cast(un_addr); addr.m_length = sizeof(*un_addr); +#endif } @@ -391,9 +772,9 @@ void NutSocket::Address::init_ipv4(Address & addr, const std::vector(::malloc(sizeof(struct sockaddr_in))); - if (NULL == in4_addr) + if (nullptr == in4_addr) throw std::bad_alloc(); packed_qb = static_cast(qb.at(0)); @@ -413,9 +794,9 @@ void NutSocket::Address::init_ipv4(Address & addr, const std::vector & hb, uint16_t port) { assert(16 == hb.size()); - struct sockaddr_in6 * in6_addr = (struct sockaddr_in6 *)::malloc(sizeof(struct sockaddr_in6)); + struct sockaddr_in6 * in6_addr = reinterpret_cast(::malloc(sizeof(struct sockaddr_in6))); - if (NULL == in6_addr) + if (nullptr == in6_addr) throw std::bad_alloc(); in6_addr->sin6_family = AF_INET6; @@ -451,7 +832,7 @@ NutSocket::Address::Address( NutSocket::Address::Address(const std::vector & bytes, uint16_t port) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif { @@ -474,10 +855,10 @@ NutSocket::Address::Address(const std::vector & bytes, uint16_t p } -NutSocket::Address::Address(const Address & orig): m_sock_addr(NULL), m_length(orig.m_length) { +NutSocket::Address::Address(const Address & orig): m_sock_addr(nullptr), m_length(orig.m_length) { void * copy = ::malloc(m_length); - if (NULL == copy) + if (nullptr == copy) throw std::bad_alloc(); ::memcpy(copy, orig.m_sock_addr, m_length); @@ -496,10 +877,10 @@ NutSocket::Address::Address(const Address & orig): m_sock_addr(NULL), m_length(o static std::string formatIPv4addr(uint32_t packed) { std::stringstream ss; - ss << (packed && 0x000000ff) << "."; - ss << (packed >> 8 && 0x000000ff) << "."; - ss << (packed >> 16 && 0x000000ff) << "."; - ss << (packed >> 24 && 0x000000ff); + ss << (packed & 0x000000ff) << "."; + ss << (packed >> 8 & 0x000000ff) << "."; + ss << (packed >> 16 & 0x000000ff) << "."; + ss << (packed >> 24 & 0x000000ff); return ss.str(); } @@ -543,11 +924,11 @@ static std::string formatIPv6addr(unsigned char const bytes[16]) { } // Standard form - // TODO: ommition of lengthy zero word strings + // TODO: ommition (REVIEW: omission?) of lengthy zero word strings ss << std::uppercase << std::hex << std::setfill('0'); for (size_t i = 0; ; ) { - uint16_t w = ((uint16_t)(bytes[2 * i]) << 8) || bytes[2 * i + 1]; + uint16_t w = static_cast(static_cast(bytes[2 * i]) << 8) | static_cast(bytes[2 * i + 1]); ss << std::setw(4) << w; @@ -562,14 +943,17 @@ static std::string formatIPv6addr(unsigned char const bytes[16]) { std::string NutSocket::Address::str() const { - assert(NULL != m_sock_addr); - - sa_family_t family = m_sock_addr->sa_family; + assert(nullptr != m_sock_addr); + /* Note: we do not cache a copy of "family" because + * its data type varies per platform, easier to just + * request it each time - not a hot spot anyway. */ std::stringstream ss; - ss << "nut::NutSocket::Address(family: " << family; + ss << "nut::NutSocket::Address(family: " << m_sock_addr->sa_family; - switch (family) { + switch (m_sock_addr->sa_family) { +#ifndef WIN32 + /* FIXME: Add support for pipes on Windows? */ case AF_UNIX: { struct sockaddr_un * addr = reinterpret_cast(m_sock_addr); @@ -577,6 +961,7 @@ std::string NutSocket::Address::str() const { break; } +#endif case AF_INET: { struct sockaddr_in * addr = reinterpret_cast(m_sock_addr); @@ -596,7 +981,7 @@ std::string NutSocket::Address::str() const { default: { std::stringstream e; - e << "NOT IMPLEMENTED: Socket address family " << family << " unsupported"; + e << "NOT IMPLEMENTED: Socket address family " << m_sock_addr->sa_family << " unsupported"; throw std::logic_error(e.str()); } @@ -613,12 +998,13 @@ NutSocket::Address::~Address() { } +/* NOTE: static class method */ bool NutSocket::accept( NutSocket & sock, const NutSocket & listen_sock, int & err_code, std::string & err_msg) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif { @@ -628,6 +1014,8 @@ bool NutSocket::accept( socklen_t sock_addr_size = sizeof(sock_addr); sock.m_impl = ::accept(listen_sock.m_impl, &sock_addr, &sock_addr_size); + sock.m_domain = listen_sock.m_domain; + sock.m_type = listen_sock.m_type; if (-1 != sock.m_impl) return true; @@ -655,6 +1043,8 @@ bool NutSocket::accept( NutSocket::NutSocket(domain_t dom, type_t type, proto_t proto): m_impl(-1), + m_domain(dom), + m_type(type), m_current_ch('\0'), m_current_ch_valid(false) { @@ -677,7 +1067,27 @@ NutSocket::NutSocket(domain_t dom, type_t type, proto_t proto): } -bool NutSocket::bind(const Address & addr, int & err_code, std::string & err_msg) throw() { +bool NutSocket::bind(const Address & addr, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + if (m_domain == NUTSOCKD_UNDEFINED) { + m_domain = static_cast(addr.m_sock_addr->sa_family); + } + else if (static_cast(m_domain) != static_cast(addr.m_sock_addr->sa_family)) { + err_code = EINVAL; + err_msg = std::string(::strerror(err_code)) + + ": bind() with a different socket address family than this object was created for"; + } + + if (m_type == NUTSOCKT_UNDEFINED) { + /* We should have this from constructor or accept() */ + err_code = EINVAL; + err_msg = std::string(::strerror(err_code)) + + ": bind() with bad socket type"; + } + err_code = ::bind(m_impl, addr.m_sock_addr, addr.m_length); if (0 == err_code) @@ -690,7 +1100,11 @@ bool NutSocket::bind(const Address & addr, int & err_code, std::string & err_msg } -bool NutSocket::listen(int backlog, int & err_code, std::string & err_msg) throw() { +bool NutSocket::listen(int backlog, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ err_code = ::listen(m_impl, backlog); if (0 == err_code) @@ -703,8 +1117,28 @@ bool NutSocket::listen(int backlog, int & err_code, std::string & err_msg) throw } -bool NutSocket::connect(const Address & addr, int & err_code, std::string & err_msg) throw() { - err_code = ::connect(m_impl, addr.m_sock_addr, addr.m_length); +bool NutSocket::connect(const Address & addr, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + if (m_domain == NUTSOCKD_UNDEFINED) { + m_domain = static_cast(addr.m_sock_addr->sa_family); + } + else if (static_cast(m_domain) != static_cast(addr.m_sock_addr->sa_family)) { + err_code = EINVAL; + err_msg = std::string(::strerror(err_code)) + + ": connect() with a different socket address family than this object was created for"; + } + + if (m_type == NUTSOCKT_UNDEFINED) { + /* We should have this from constructor or accept() */ + err_code = EINVAL; + err_msg = std::string(::strerror(err_code)) + + ": connect() with bad socket type"; + } + + err_code = sktconnect(m_impl, addr.m_sock_addr, addr.m_length); if (0 == err_code) return true; @@ -716,8 +1150,48 @@ bool NutSocket::connect(const Address & addr, int & err_code, std::string & err_ } -bool NutSocket::close(int & err_code, std::string & err_msg) throw() { - err_code = ::close(m_impl); +bool NutSocket::flush(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + if (-1 == m_impl) { + err_code = EBADF; + err_msg = std::string(::strerror(err_code)); + return false; + } + + if (m_type == NUTSOCKT_STREAM && m_domain == NUTSOCKD_INETv4) { + /* Assume IPv4 TCP: https://stackoverflow.com/a/71417876/4715872 */ + int flag = 1; + if (!setsockopt(m_impl, IPPROTO_TCP, TCP_NODELAY, SOCK_OPT_CAST(&flag), sizeof(int))) { + err_code = errno; + err_msg = std::string(::strerror(err_code)); + return false; + } + if (!sktwrite(m_impl, nullptr, 0)) { + err_code = errno; + err_msg = std::string(::strerror(err_code)); + return false; + } + flag = 0; + if (!setsockopt(m_impl, IPPROTO_TCP, TCP_NODELAY, SOCK_OPT_CAST(&flag), sizeof(int))) { + err_code = errno; + err_msg = std::string(::strerror(err_code)); + return false; + } + } /* else Unix, UDP (or several other socket families generally); PRs welcome */ + + return true; +} + + +bool NutSocket::close(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + err_code = sktclose(m_impl); if (0 == err_code) { m_impl = -1; @@ -738,7 +1212,11 @@ NutSocket::~NutSocket() { } -NutStream::status_t NutSocket::getChar(char & ch) throw() { +NutStream::status_t NutSocket::getChar(char & ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ if (m_current_ch_valid) { ch = m_current_ch; @@ -770,12 +1248,20 @@ NutStream::status_t NutSocket::getChar(char & ch) throw() { } -void NutSocket::readChar() throw() { +void NutSocket::readChar() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ m_current_ch_valid = false; } -NutStream::status_t NutSocket::getString(std::string & str) throw() { +NutStream::status_t NutSocket::getString(std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ if (m_current_ch_valid) str += m_current_ch; @@ -784,21 +1270,25 @@ NutStream::status_t NutSocket::getString(std::string & str) throw() { char buffer[512]; for (;;) { - ssize_t read_cnt = ::read(m_impl, buffer, sizeof(buffer) / sizeof(buffer[0])); + ssize_t read_cnt = sktread(m_impl, buffer, sizeof(buffer) / sizeof(buffer[0])); - if (-1 == read_cnt) + if (read_cnt < 0) return NUTS_ERROR; if (0 == read_cnt) return NUTS_OK; - str.append(buffer, read_cnt); + str.append(buffer, static_cast(read_cnt)); } } -NutStream::status_t NutSocket::putChar(char ch) throw() { - ssize_t write_cnt = ::write(m_impl, &ch, 1); +NutStream::status_t NutSocket::putChar(char ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + ssize_t write_cnt = sktwrite(m_impl, &ch, 1); if (1 == write_cnt) return NUTS_OK; @@ -811,25 +1301,29 @@ NutStream::status_t NutSocket::putChar(char ch) throw() { } -NutStream::status_t NutSocket::putString(const std::string & str) throw() { - ssize_t str_len = str.size(); +NutStream::status_t NutSocket::putString(const std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif +{ + size_t str_len = str.size(); // Avoid the costly system call unless necessary if (0 == str_len) return NUTS_OK; - ssize_t write_cnt = ::write(m_impl, str.data(), str_len); - - if (write_cnt == str_len) - return NUTS_OK; + ssize_t write_cnt = sktwrite(m_impl, str.data(), str_len); // TODO: Under certain circumstances, less than the whole // string might be written // Review the code if async. I/O is supported (in which case // the function shall have to implement the blocking using - // select/ poll/ epoll on its own (probably select for portability) + // select/poll/epoll on its own (probably select for portability) - assert(-1 == write_cnt); + assert(write_cnt > 0); + + if (static_cast(write_cnt) == str_len) + return NUTS_OK; // TODO: At least logging of the error (errno), if not propagation diff --git a/common/nutwriter.cpp b/common/nutwriter.cpp index 590cde54a2..8cc232bcdc 100644 --- a/common/nutwriter.cpp +++ b/common/nutwriter.cpp @@ -1,23 +1,26 @@ -/* nutwriter.cpp - NUT writer +/* + nutwriter.cpp - NUT writer - Copyright (C) + Copyright (C) 2012 Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + #include "nutwriter.hpp" #include @@ -63,6 +66,16 @@ namespace nut { + +/* Trivial implementations out of class declaration to avoid + * error: 'ClassName' has no out-of-line virtual method definitions; its vtable + * will be emitted in every translation unit [-Werror,-Wweak-vtables] + */ +NutConfigWriter::~NutConfigWriter() {} +NutConfConfigWriter::~NutConfConfigWriter() {} +UpsmonConfigWriter::~UpsmonConfigWriter() {} +UpsdConfigWriter::~UpsdConfigWriter() {} + // End-of-Line separators (arch. dependent) /** UNIX style EoL */ @@ -135,7 +148,7 @@ NutWriter::status_t NutConfConfigWriter::writeConfig(const NutConfiguration & co status_t status; // Mode - // TBD: How should I serialise an unknown mode? + // TBD: How should I serialize an unknown mode? if (config.mode.set()) { std::string mode_str; @@ -198,7 +211,7 @@ struct NotifyFlagsStrings { static const FlagStrings flag_str; /** - * \brief Initialise notify flag strings + * \brief Initialize notify flag strings * * \return Notify flag strings map */ @@ -235,14 +248,14 @@ const NotifyFlagsStrings::FlagStrings NotifyFlagsStrings::flag_str = /** - * \brief upsmon notify flags serialiser + * \brief upsmon notify flags serializer * * \param type Notification type * \param flags Notification flags * * \return NOTIFYFLAG directive string */ -static std::string serialiseNotifyFlags(UpsmonConfiguration::NotifyType type, unsigned short flags) { +static std::string serializeNotifyFlags(UpsmonConfiguration::NotifyType type, unsigned int flags) { static const NotifyFlagsStrings::FlagStrings::const_iterator ignore_str_iter = NotifyFlagsStrings::flag_str.find(UpsmonConfiguration::NOTIFY_IGNORE); @@ -281,14 +294,14 @@ static std::string serialiseNotifyFlags(UpsmonConfiguration::NotifyType type, un /** - * \brief upsmon notify messages serialiser + * \brief upsmon notify messages serializer * * \param type Notification type * \param msg Notification message * * \return NOTIFYMSG directive string */ -static std::string serialiseNotifyMessage(UpsmonConfiguration::NotifyType type, const std::string & msg) { +static std::string serializeNotifyMessage(UpsmonConfiguration::NotifyType type, const std::string & msg) { assert(type < UpsmonConfiguration::NOTIFY_TYPE_MAX); std::string directive("NOTIFYMSG "); @@ -304,7 +317,7 @@ static std::string serialiseNotifyMessage(UpsmonConfiguration::NotifyType type, /** * \brief Get notify type successor * - * TBD: Should be in nutconf.h + * TBD: Should be in nutconf.hpp * * \param type Notify type * @@ -320,9 +333,9 @@ inline static UpsmonConfiguration::NotifyType nextNotifyType(UpsmonConfiguration /** - * \brief Notify type pre-incrementation + * \brief Notify type pre-increment * - * TBD: Should be in nutconf.h + * TBD: Should be in nutconf.hpp * * \param[in,out] type Notify type * @@ -334,14 +347,15 @@ inline static UpsmonConfiguration::NotifyType operator ++(UpsmonConfiguration::N /** - * \brief Notify type post-incrementation + * \brief Notify type post-increment * - * TBD: Should be in nutconf.h + * TBD: Should be in nutconf.hpp * * \param[in,out] type Notify type * * \return \c type */ +/* // CURRENTLY UNUSED inline static UpsmonConfiguration::NotifyType operator ++(UpsmonConfiguration::NotifyType & type, int) { UpsmonConfiguration::NotifyType type_copy = type; @@ -349,16 +363,16 @@ inline static UpsmonConfiguration::NotifyType operator ++(UpsmonConfiguration::N return type_copy; } - +*/ /** - * \brief UPS monitor definition serialiser + * \brief UPS monitor definition serializer * * \param monitor Monitor * * \return Monitor config. directive */ -static std::string serialiseMonitor(const UpsmonConfiguration::Monitor & monitor) { +static std::string serializeMonitor(const UpsmonConfiguration::Monitor & monitor) { std::stringstream directive; directive << "MONITOR "; @@ -404,6 +418,19 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & #define UPSMON_DIRECTIVEX(name, arg_t, arg, quote_arg) \ CONFIG_DIRECTIVEX(name, arg_t, arg, quote_arg) +/* The "false" arg in macro below evaluates to `if (false) ...` after + * pre-processing, and causes warnings about unreachable code */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (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 +/* 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" +#endif UPSMON_DIRECTIVEX("RUN_AS_USER", std::string, config.runAsUser, false); UPSMON_DIRECTIVEX("SHUTDOWNCMD", std::string, config.shutdownCmd, true); UPSMON_DIRECTIVEX("NOTIFYCMD", std::string, config.notifyCmd, true); @@ -416,6 +443,12 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & UPSMON_DIRECTIVEX("RBWARNTIME", unsigned int, config.rbWarnTime, false); UPSMON_DIRECTIVEX("NOCOMMWARNTIME", unsigned int, config.noCommWarnTime, false); UPSMON_DIRECTIVEX("FINALDELAY", unsigned int, config.finalDelay, false); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +#endif #undef UPSMON_DIRECTIVEX @@ -426,7 +459,7 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & for (; type < UpsmonConfiguration::NOTIFY_TYPE_MAX; ++type) { if (config.notifyFlags[type].set()) { - std::string directive = serialiseNotifyFlags(type, config.notifyFlags[type]); + std::string directive = serializeNotifyFlags(type, config.notifyFlags[type]); status_t status = writeDirective(directive); @@ -440,7 +473,7 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & for (; type < UpsmonConfiguration::NOTIFY_TYPE_MAX; ++type) { if (config.notifyMessages[type].set()) { - std::string directive = serialiseNotifyMessage(type, config.notifyMessages[type]); + std::string directive = serializeNotifyMessage(type, config.notifyMessages[type]); status_t status = writeDirective(directive); @@ -453,7 +486,7 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & std::list::const_iterator mon_iter = config.monitors.begin(); for (; mon_iter != config.monitors.end(); ++mon_iter) { - std::string directive = serialiseMonitor(*mon_iter); + std::string directive = serializeMonitor(*mon_iter); status_t status = writeDirective(directive); @@ -466,13 +499,13 @@ NutWriter::status_t UpsmonConfigWriter::writeConfig(const UpsmonConfiguration & /** - * \brief upsd listen address serialiser + * \brief upsd listen address serializer * * \param address Listen address * - * \return Serialised listen address + * \return Serialized listen address */ -static std::string serialiseUpsdListenAddress(const UpsdConfiguration::Listen & address) { +static std::string serializeUpsdListenAddress(const UpsdConfiguration::Listen & address) { std::stringstream directive; directive << "LISTEN " << address.address; @@ -502,10 +535,29 @@ NutWriter::status_t UpsdConfigWriter::writeConfig(const UpsdConfiguration & conf #define UPSD_DIRECTIVEX(name, arg_t, arg) \ CONFIG_DIRECTIVEX(name, arg_t, arg, false) +/* The "false" arg in macro below evaluates to `if (false) ...` after + * pre-processing, and causes warnings about unreachable code */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (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 +/* 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" +#endif UPSD_DIRECTIVEX("MAXAGE", unsigned int, config.maxAge); UPSD_DIRECTIVEX("MAXCONN", unsigned int, config.maxConn); UPSD_DIRECTIVEX("STATEPATH", std::string, config.statePath); UPSD_DIRECTIVEX("CERTFILE", std::string, config.certFile); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +#endif #undef UPSD_DIRECTIVEX @@ -513,7 +565,7 @@ NutWriter::status_t UpsdConfigWriter::writeConfig(const UpsdConfiguration & conf std::list::const_iterator la_iter = config.listens.begin(); for (; la_iter != config.listens.end(); ++la_iter) { - std::string directive = serialiseUpsdListenAddress(*la_iter); + std::string directive = serializeUpsdListenAddress(*la_iter); status_t status = writeDirective(directive); @@ -553,7 +605,7 @@ NutWriter::status_t DefaultConfigWriter::writeDirective(const std::string & str) * * \param val Value string * - * \return Value string ready for serialisation + * \return Value string ready for serialization */ static std::string encodeValue(const std::string & val) { // Check the string for spaces and '=' @@ -643,7 +695,7 @@ NutWriter::status_t GenericConfigWriter::writeSection(const GenericConfigSection NutWriter::status_t GenericConfigWriter::writeConfig(const GenericConfiguration & config) { // Write sections // Note that lexicographic ordering places the global - // (i.e. empty-name) section as the 1st one + // (i.e. empty-name) section as the first one GenericConfiguration::SectionMap::const_iterator section_iter = config.sections.begin(); for (; section_iter != config.sections.end(); ++section_iter) { @@ -688,7 +740,7 @@ NutWriter::status_t UpsdUsersConfigWriter::writeSection(const GenericConfigSecti upsmon_entry_separator); } - // Standard entry serialisation + // Standard entry serialization else { status = writeSectionEntry(entry_iter->second); } diff --git a/common/parseconf.c b/common/parseconf.c index fe8f172b96..f7d6eec61b 100644 --- a/common/parseconf.c +++ b/common/parseconf.c @@ -76,6 +76,8 @@ * */ +#include "config.h" /* should be first */ + #include "common.h" #include @@ -89,6 +91,7 @@ #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" /* possible states */ @@ -241,7 +244,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 +344,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; @@ -451,7 +454,7 @@ int pconf_file_begin(PCONF_CTX_t *ctx, const char *fn) } /* prevent fd leaking to child processes */ - fcntl(fileno(ctx->f), F_SETFD, FD_CLOEXEC); + set_close_on_exec(fileno(ctx->f)); return 1; /* OK */ } diff --git a/common/setenv.c b/common/setenv.c index 85b8974abc..7379a7cf6b 100644 --- a/common/setenv.c +++ b/common/setenv.c @@ -1,4 +1,6 @@ /* setenv.c Ben Collver */ +#include "config.h" /* must be first */ + #ifndef HAVE_SETENV #include #include diff --git a/common/snprintf.c b/common/snprintf.c index 0caa04ed25..c8c514578b 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -395,6 +395,12 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) break; case 'p': strvalue = va_arg (args, void *); + /* FIXME: in 64-bit (and Windows-targeted) builds this code yields: + * warning: cast from pointer to integer of different size + * so probably prints a truncated pointer value. Should check + * if sizeof(void*) == sizeof(long) and output e.g. two pieces + * (lower and upper long for bytes of the pointer). + */ fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); break; case 'n': @@ -593,11 +599,15 @@ static LDOUBLE abs_val (LDOUBLE value) } #endif -#ifndef HAVE_FCVT +#ifndef WIN32 +# ifndef HAVE_FCVT /* The two routines that may get defined below are only used if we also don't * have a fcvt() in the system. Defining and not using the routines may be a - * warning (fatal with -Werror), so we hide them here. */ -# ifndef HAVE_POW10 + * warning (fatal with -Werror), so we hide them here. + * FIXME: They are blindly expected (assumed?) to be available on Windows, + * maybe better trust configure script macros on this? + */ +# if ! HAVE_DECL_POW10 static LDOUBLE pow10 (int exp) { LDOUBLE result = 1; @@ -610,9 +620,9 @@ static LDOUBLE pow10 (int exp) return result; } -# endif +# endif /* HAVE_DECL_POW10 */ -# ifndef HAVE_ROUND +# if ! HAVE_DECL_ROUND static long round (LDOUBLE value) { long intpart; @@ -624,8 +634,9 @@ static long round (LDOUBLE value) return intpart; } -# endif -#endif /* HAVE_FCVT */ +# endif /* HAVE_DECL_ROUND */ +# endif /* HAVE_FCVT */ +#endif /* WIN32 */ static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags) @@ -962,4 +973,3 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) printf ("%d tests failed out of %d.\n", fail, num); } #endif /* SNPRINTF_TEST */ - diff --git a/common/state.c b/common/state.c index 280eac5181..6eaf18ab6c 100644 --- a/common/state.c +++ b/common/state.c @@ -26,13 +26,17 @@ #include #include #include +#ifndef WIN32 #include #include +#endif #include "common.h" #include "state.h" #include "parseconf.h" +/* internal helpers */ + static void val_escape(st_tree_t *node) { char etmp[ST_MAX_VALUE_LEN]; @@ -126,6 +130,65 @@ static void st_tree_node_add(st_tree_t **nptr, st_tree_t *sptr) *nptr = sptr; } +static int st_tree_node_refresh_timestamp(const st_tree_t *node) +{ + if (!node) + return -1; + + return state_get_timestamp((st_tree_timespec_t *)&node->lastset); +} + +/* interface */ + +/* As underlying system methods: + * return 0 on success, -1 and errno on error + */ +int state_get_timestamp(st_tree_timespec_t *now) +{ + if (!now) + return -1; + +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC + return clock_gettime(CLOCK_MONOTONIC, now); +#else + return gettimeofday(now, NULL); +#endif +} + +/* Returns -1 if the node->lastset is "older" than cutoff, + * 0 if it is equal, or +1 if it is newer. + * Returns -2 or -3 if node or cutoff are null. + */ +int st_tree_node_compare_timestamp( + const st_tree_t *node, + const st_tree_timespec_t *cutoff +) { + double d; + + if (!node) + return -2; + + if (!cutoff) + return -3; + + /* Like in difftime(), the first arg is "finish" and + * the second arg is "start" of a time range (below), + * so if the diff is negative, then "lastset" happened + * before "cutoff": + */ +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC + d = difftimespec(node->lastset, *cutoff); +#else + d = difftimeval(node->lastset, *cutoff); +#endif + + if (d < 0) + return -1; + if (d > 0) + return 1; + return 0; +} + /* remove a variable from a tree * except for variables with ST_FLAG_IMMUTABLE * (for override.* to survive) per issue #737 @@ -165,7 +228,46 @@ int state_delinfo(st_tree_t **nptr, const char *var) return 0; /* not found */ } -/* interface */ +int state_delinfo_olderthan(st_tree_t **nptr, const char *var, const st_tree_timespec_t *cutoff) +{ + while (*nptr) { + + st_tree_t *node = *nptr; + + if (strcasecmp(node->var, var) > 0) { + nptr = &node->left; + continue; + } + + if (strcasecmp(node->var, var) < 0) { + nptr = &node->right; + continue; + } + + if (node->flags & ST_FLAG_IMMUTABLE) { + upsdebugx(6, "%s: not deleting immutable variable [%s]", __func__, var); + return 0; + } + + if (st_tree_node_compare_timestamp(node, cutoff) >= 0) { + upsdebugx(6, "%s: not deleting recently updated variable [%s]", __func__, var); + return 0; + } + upsdebugx(6, "%s: deleting variable [%s] last updated too long ago", __func__, var); + + /* whatever is on the left, hang it off current right */ + st_tree_node_add(&node->right, node->left); + + /* now point the parent at the old right child */ + *nptr = node->right; + + st_tree_node_free(node); + + return 1; + } + + return 0; /* not found */ +} int state_setinfo(st_tree_t **nptr, const char *var, const char *val) { @@ -183,6 +285,9 @@ int state_setinfo(st_tree_t **nptr, const char *var, const char *val) continue; } + /* refresh even if "skip-writing" same info value */ + st_tree_node_refresh_timestamp(node); + /* updating an existing entry */ if (!strcasecmp(node->raw, val)) { return 0; /* no change */ @@ -212,6 +317,7 @@ int state_setinfo(st_tree_t **nptr, const char *var, const char *val) (*nptr)->var = xstrdup(var); (*nptr)->raw = xstrdup(val); (*nptr)->rawsize = strlen(val) + 1; + st_tree_node_refresh_timestamp(*nptr); val_escape(*nptr); @@ -259,6 +365,7 @@ int state_addenum(st_tree_t *root, const char *var, const char *val) /* smooth over any oddities in the enum value */ pconf_encode(val, enc, sizeof(enc)); + st_tree_node_refresh_timestamp(sttmp); return st_tree_enum_add(&sttmp->enum_list, enc); } @@ -307,6 +414,7 @@ int state_addrange(st_tree_t *root, const char *var, const int min, const int ma return 0; /* failed */ } + st_tree_node_refresh_timestamp(sttmp); return st_tree_range_add(&sttmp->range_list, min, max); } @@ -324,6 +432,7 @@ int state_setaux(st_tree_t *root, const char *var, const char *auxs) return -1; /* failed */ } + st_tree_node_refresh_timestamp(sttmp); aux = strtol(auxs, (char **) NULL, 10); /* silently ignore matches */ @@ -420,6 +529,7 @@ void state_setflags(st_tree_t *root, const char *var, size_t numflags, char **fl return; } + st_tree_node_refresh_timestamp(sttmp); sttmp->flags = 0; for (i = 0; i < numflags; i++) { @@ -560,6 +670,7 @@ int state_delenum(st_tree_t *root, const char *var, const char *val) return 0; } + st_tree_node_refresh_timestamp(sttmp); return st_tree_del_enum(&sttmp->enum_list, val); } @@ -597,6 +708,7 @@ int state_delrange(st_tree_t *root, const char *var, const int min, const int ma return 0; } + st_tree_node_refresh_timestamp(sttmp); return st_tree_del_range(&sttmp->range_list, min, max); } diff --git a/common/str.c b/common/str.c index 35666bb1bf..d2c83fb2da 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; @@ -617,6 +617,19 @@ int str_to_double_strict(const char *string, double *number, const int base) return 1; } +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)); +} + /* Based on code by "mmdemirbas" posted "Jul 9 '12 at 11:41" to forum page * http://stackoverflow.com/questions/8465006/how-to-concatenate-2-strings-in-c * This concatenates the given number of strings into one freshly allocated diff --git a/common/strerror.c b/common/strerror.c index e00f208aba..e2ff952823 100644 --- a/common/strerror.c +++ b/common/strerror.c @@ -6,6 +6,11 @@ #ifndef HAVE_STRERROR #include +#ifdef HAVE_STDIO_H +# include /* for snprintf() */ +#else +# include "proto.h" +#endif char *strerror(int errnum) { @@ -232,8 +237,10 @@ char *strerror(int errnum) return "Invalid slot"; #endif #if defined (EDEADLOCK) +# if (!defined(EDEADLK)) || EDEADLK != EDEADLOCK case EDEADLOCK: return "File locking deadlock error"; +# endif #endif #if defined (EBFONT) case EBFONT: diff --git a/common/strnlen.c b/common/strnlen.c new file mode 100644 index 0000000000..1ad0340b4a --- /dev/null +++ b/common/strnlen.c @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2009 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* // Includes amended for NUT cross-platform purposes +#include +__FBSDID("$FreeBSD: src/lib/libc/string/strnlen.c,v 1.1 2009/02/28 06:00:58 das Exp $"); +*/ + +#include +#include + +size_t +strnlen(const char *s, size_t maxlen) +{ + size_t len; + + for (len = 0; len < maxlen; len++, s++) { + if (!*s) + break; + } + return (len); +} + diff --git a/common/strptime.c b/common/strptime.c new file mode 100644 index 0000000000..8909aea6cf --- /dev/null +++ b/common/strptime.c @@ -0,0 +1,625 @@ +/* $NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $ */ + +/* Fetched into NUT codebase from MSYS2 packaging of MINGW: + * https://raw.githubusercontent.com/msys2/MINGW-packages/master/mingw-w64-libkml/strptime.c + */ + +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Use NUT build configuration */ +#include "config.h" + +/* +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $"); +#endif + +#include "namespace.h" +#include +*/ +#include +#include +#include +#include +#if defined HAVE_STDINT_H +# include +#else +# include "nut_stdint.h" +#endif +/* +#include +#include "private.h" + +#ifdef __weak_alias +__weak_alias(strptime,_strptime) +#endif +*/ +typedef unsigned char u_char; +typedef unsigned int uint; +typedef unsigned __int64 uint64_t; + +#define _ctloc(x) (_CurrentTimeLocale->x) + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } + +static int TM_YEAR_BASE = 1900; +static char gmt[] = { "GMT" }; +static char utc[] = { "UTC" }; +/* RFC-822/RFC-2822 */ +static const char * const nast[5] = { + "EST", "CST", "MST", "PST", "\0\0\0" +}; +static const char * const nadt[5] = { + "EDT", "CDT", "MDT", "PDT", "\0\0\0" +}; +static const char * const am_pm[2] = { + "am", "pm" +}; +static const char * const day[7] = { + "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" +}; +static const char * const abday[7] = { + "sun", "mon", "tue", "wed", "thu", "fri", "sat" +}; +static const char * const mon[12] = { + "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" +}; +static const char * const abmon[12] = { + "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" +}; + +static const u_char *conv_num(const unsigned char *, int *, uint, uint); +static const u_char *find_string(const u_char *, int *, const char * const *, + const char * const *, int); + +/* +#ifndef MO_MINGW32 +static int +strncasecmp(const char *a, const char *b, size_t c) +{ + return _strnicmp(a, b, c); +} +#endif +*/ + +char * +strptime(const char *buf, const char *fmt, struct tm *tm) +{ + unsigned char c; + const unsigned char *bp, *ep; + int alt_format, i, split_year = 0, neg = 0, offs; + const char *new_fmt; + + bp = (const u_char *)buf; + + while (bp != NULL && (c = *fmt++) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + i = 0; + + /* Eat up white-space. */ + if (isspace(c)) { + while (isspace(*bp)) + bp++; + continue; + } + + if (c != '%') + goto literal; + + +again: switch (c = *fmt++) { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return NULL; + LEGAL_ALT(0); + continue; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ +/* // we do not need 'c': + case 'c': Date and time, using the locale's format. + new_fmt = _ctloc(d_t_fmt); + goto recurse; +*/ + + case 'D': /* The date as "%m/%d/%y". */ + new_fmt = "%m/%d/%y"; + LEGAL_ALT(0); + goto recurse; + + case 'F': /* The date as "%Y-%m-%d". */ + new_fmt = "%Y-%m-%d"; + LEGAL_ALT(0); + goto recurse; + + case 'R': /* The time as "%H:%M". */ + new_fmt = "%H:%M"; + LEGAL_ALT(0); + goto recurse; + + case 'r': /* The time in 12-hour clock representation. */ + new_fmt = "%I:%M:S %p";/*_ctloc(t_fmt_ampm); */ + LEGAL_ALT(0); + goto recurse; + + case 'T': /* The time as "%H:%M:%S". */ + new_fmt = "%H:%M:%S"; + LEGAL_ALT(0); + goto recurse; + +/* // we don't use 'X' + case 'X': The time, using the locale's format. + new_fmt =_ctloc(t_fmt); + goto recurse; +*/ + +/* // we do not need 'x' + case 'x': The date, using the locale's format. + new_fmt =_ctloc(d_fmt); +*/ + +recurse: + bp = (const u_char *)strptime((const char *)bp, + new_fmt, tm); + LEGAL_ALT(ALT_E); + continue; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + bp = find_string(bp, &tm->tm_wday, day, abday, 7); + LEGAL_ALT(0); + continue; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + bp = find_string(bp, &tm->tm_mon, mon, abmon, 12); + LEGAL_ALT(0); + continue; + + case 'C': /* The century number. */ + i = 20; + bp = conv_num(bp, &i, 0, 99); + + i = i * 100 - TM_YEAR_BASE; + if (split_year) + i += tm->tm_year % 100; + split_year = 1; + tm->tm_year = i; + LEGAL_ALT(ALT_E); + continue; + + case 'd': /* The day of month. */ + case 'e': + bp = conv_num(bp, &tm->tm_mday, 1, 31); + LEGAL_ALT(ALT_O); + continue; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + bp = conv_num(bp, &tm->tm_hour, 0, 23); + LEGAL_ALT(ALT_O); + continue; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + bp = conv_num(bp, &tm->tm_hour, 1, 12); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + LEGAL_ALT(ALT_O); + continue; + + case 'j': /* The day of year. */ + i = 1; + bp = conv_num(bp, &i, 1, 366); + tm->tm_yday = i - 1; + LEGAL_ALT(0); + continue; + + case 'M': /* The minute. */ + bp = conv_num(bp, &tm->tm_min, 0, 59); + LEGAL_ALT(ALT_O); + continue; + + case 'm': /* The month. */ + i = 1; + bp = conv_num(bp, &i, 1, 12); + tm->tm_mon = i - 1; + LEGAL_ALT(ALT_O); + continue; + + case 'p': /* The locale's equivalent of AM/PM. */ + bp = find_string(bp, &i, am_pm, NULL, 2); + if (tm->tm_hour > 11) + return NULL; + tm->tm_hour += i * 12; + LEGAL_ALT(0); + continue; + + case 'S': /* The seconds. */ + bp = conv_num(bp, &tm->tm_sec, 0, 61); + LEGAL_ALT(ALT_O); + continue; + +#ifndef TIME_MAX +#define TIME_MAX INT64_MAX +#endif + case 's': /* seconds since the epoch */ + { + time_t sse = 0; + uint64_t rulim = TIME_MAX; + + if (*bp < '0' || *bp > '9') { + bp = NULL; + continue; + } + + do { + sse *= 10; + sse += *bp++ - '0'; + rulim /= 10; + } while (((uint64_t)sse * 10 <= TIME_MAX) && + rulim && *bp >= '0' && *bp <= '9'); + + if (sse < 0 || (uint64_t)sse > TIME_MAX) { + bp = NULL; + continue; + } + + tm = localtime(&sse); + if (tm == NULL) + bp = NULL; + } + continue; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + bp = conv_num(bp, &i, 0, 53); + LEGAL_ALT(ALT_O); + continue; + + case 'w': /* The day of week, beginning on sunday. */ + bp = conv_num(bp, &tm->tm_wday, 0, 6); + LEGAL_ALT(ALT_O); + continue; + + case 'u': /* The day of week, monday = 1. */ + bp = conv_num(bp, &i, 1, 7); + tm->tm_wday = i % 7; + LEGAL_ALT(ALT_O); + continue; + + case 'g': /* The year corresponding to the ISO week + * number but without the century. + */ + bp = conv_num(bp, &i, 0, 99); + continue; + + case 'G': /* The year corresponding to the ISO week + * number with century. + */ + do { + bp++; + } while (isdigit(*bp)); + continue; + + case 'V': /* The ISO 8601:1988 week number as decimal */ + bp = conv_num(bp, &i, 0, 53); + continue; + + case 'Y': /* The year. */ + i = TM_YEAR_BASE; /* just for data sanity... */ + bp = conv_num(bp, &i, 0, 9999); + tm->tm_year = i - TM_YEAR_BASE; + LEGAL_ALT(ALT_E); + continue; + + case 'y': /* The year within 100 years of the epoch. */ + /* LEGAL_ALT(ALT_E | ALT_O); */ + bp = conv_num(bp, &i, 0, 99); + + if (split_year) + /* preserve century */ + i += (tm->tm_year / 100) * 100; + else { + split_year = 1; + if (i <= 68) + i = i + 2000 - TM_YEAR_BASE; + else + i = i + 1900 - TM_YEAR_BASE; + } + tm->tm_year = i; + continue; + + case 'Z': + _tzset(); + if (strncasecmp((const char *)bp, gmt, 3) == 0 + || strncasecmp((const char *)bp, utc, 3) == 0 + ) { + tm->tm_isdst = 0; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = 0; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = gmt; +#endif + bp += 3; + } else { + ep = find_string(bp, &i, + (const char * const *)tzname, + NULL, 2); + if (ep != NULL) { + tm->tm_isdst = i; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -(timezone); +#endif +#ifdef TM_ZONE + tm->TM_ZONE = tzname[i]; +#endif + } + bp = ep; + } + continue; + + case 'z': + /* + * We recognize all ISO 8601 formats: + * Z = Zulu time/UTC + * [+-]hhmm + * [+-]hh:mm + * [+-]hh + * We recognize all RFC-822/RFC-2822 formats: + * UT|GMT + * North American : UTC offsets + * E[DS]T = Eastern : -4 | -5 + * C[DS]T = Central : -5 | -6 + * M[DS]T = Mountain: -6 | -7 + * P[DS]T = Pacific : -7 | -8 + * Military + * [A-IL-M] = -1 ... -9 (J not used) + * [N-Y] = +1 ... +12 + */ + while (isspace(*bp)) + bp++; + + switch (*bp++) { + case 'G': + if (*bp++ != 'M') + return NULL; + /*FALLTHROUGH*/ + case 'U': + if (*bp++ != 'T') + return NULL; + /*FALLTHROUGH*/ + case 'Z': + tm->tm_isdst = 0; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = 0; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = utc; +#endif + continue; + case '+': + neg = 0; + break; + case '-': + neg = 1; + break; + default: + --bp; + ep = find_string(bp, &i, nast, NULL, 4); + if (ep != NULL) { +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -5 - i; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nast[i]); +#endif + bp = ep; + continue; + } + ep = find_string(bp, &i, nadt, NULL, 4); + if (ep != NULL) { + tm->tm_isdst = 1; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -4 - i; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nadt[i]); +#endif + bp = ep; + continue; + } + + if ((*bp >= 'A' && *bp <= 'I') || + (*bp >= 'L' && *bp <= 'Y') + ) { +#ifdef TM_GMTOFF + /* Argh! No 'J'! */ + if (*bp >= 'A' && *bp <= 'I') + tm->TM_GMTOFF = + ('A' - 1) - (int)*bp; + else if (*bp >= 'L' && *bp <= 'M') + tm->TM_GMTOFF = 'A' - (int)*bp; + else if (*bp >= 'N' && *bp <= 'Y') + tm->TM_GMTOFF = (int)*bp - 'M'; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + bp++; + continue; + } + return NULL; + } + offs = 0; + for (i = 0; i < 4; ) { + if (isdigit(*bp)) { + offs = offs * 10 + (*bp++ - '0'); + i++; + continue; + } + if (i == 2 && *bp == ':') { + bp++; + continue; + } + break; + } + switch (i) { + case 2: + offs *= 100; + break; + case 4: + i = offs % 100; + if (i >= 60) + return NULL; + /* Convert minutes into decimal */ + offs = (offs / 100) * 100 + (i * 50) / 30; + break; + default: + return NULL; + } + if (neg) + offs = -offs; + tm->tm_isdst = 0; /* XXX */ +#ifdef TM_GMTOFF + tm->TM_GMTOFF = offs; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + continue; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + while (isspace(*bp)) + bp++; + LEGAL_ALT(0); + continue; + + + default: /* Unknown/unsupported conversion. */ + return NULL; + } + } + + return (char *)(bp); +} + + +static const u_char * +conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim) +{ + uint result = 0; + unsigned char ch; + + /* The limit also determines the number of valid digits. */ + uint rulim = ulim; + + ch = *buf; + if (ch < '0' || ch > '9') + return NULL; + + do { + result *= 10; + result += ch - '0'; + rulim /= 10; + ch = *++buf; + } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); + + if (result < llim || result > ulim) + return NULL; + + *dest = result; + return buf; +} + +static const u_char * +find_string(const u_char *bp, int *tgt, const char * const *n1, + const char * const *n2, int c) +{ + int i; + size_t len; + + /* check full name - then abbreviated ones */ + for (; n1 != NULL; n1 = n2, n2 = NULL) { + for (i = 0; i < c; i++, n1++) { + len = strlen(*n1); + if (strncasecmp(*n1, (const char *)bp, len) == 0) { + *tgt = i; + return bp + len; + } + } + } + + /* Nothing matched */ + return NULL; +} diff --git a/common/strsep.c b/common/strsep.c new file mode 100644 index 0000000000..6b9b503740 --- /dev/null +++ b/common/strsep.c @@ -0,0 +1,19 @@ +#include + +/* Simple implem courtesy of https://stackoverflow.com/a/58244503 + * Note: like the (BSD) standard implem, this changes the original stringp! + * Result is undefined (segfault here) if stringp==NULL + * (it is supposed to be address of string after all) + */ +char *strsep(char **stringp, const char *delim) { + char *rv = *stringp; + if (rv) { + *stringp += strcspn(*stringp, delim); + if (**stringp) + *(*stringp)++ = '\0'; + else + *stringp = NULL; + } + return rv; +} + diff --git a/common/upsconf.c b/common/upsconf.c index bb7fd35108..c5716124f1 100644 --- a/common/upsconf.c +++ b/common/upsconf.c @@ -68,8 +68,12 @@ static void upsconf_err(const char *errmsg) upslogx(LOG_ERR, "Fatal error in parseconf(ups.conf): %s", errmsg); } -/* open the ups.conf, parse it, and call back do_upsconf_args() */ -void read_upsconf(void) +/* open the ups.conf, parse it, and call back do_upsconf_args() + * returns -1 (or aborts the program) in case of errors; + * returns 1 if processing finished successfully + * See also reload_flag support in main.c for live-reload feature + */ +int read_upsconf(int fatal_errors) { char fn[SMALLBUF]; PCONF_CTX_t ctx; @@ -79,8 +83,14 @@ void read_upsconf(void) pconf_init(&ctx, upsconf_err); - if (!pconf_file_begin(&ctx, fn)) - fatalx(EXIT_FAILURE, "Can't open %s: %s", fn, ctx.errmsg); + if (!pconf_file_begin(&ctx, fn)) { + if (fatal_errors) { + fatalx(EXIT_FAILURE, "Can't open %s: %s", fn, ctx.errmsg); + } else { + upslogx(LOG_WARNING, "Can't open %s: %s", fn, ctx.errmsg); + return -1; + } + } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { @@ -95,4 +105,6 @@ void read_upsconf(void) pconf_finish(&ctx); free(ups_section); + + return 1; /* Handled OK */ } diff --git a/common/wincompat.c b/common/wincompat.c new file mode 100644 index 0000000000..745b0bf9e3 --- /dev/null +++ b/common/wincompat.c @@ -0,0 +1,1642 @@ +/* + + Copyright (C) 1999 Russell Kroll + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, WRITE to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef WIN32 +#include "config.h" /* should be first */ + +#include "wincompat.h" +#include "nut_stdint.h" + +#if ! HAVE_INET_PTON +# include +# include +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_WS2TCPIP_H +# include +# endif +#endif + +#if (0) +extern int errno; +#endif + +const char * EventLogName = NULL; + +struct passwd wincompat_passwd; +char wincompat_user_name[SMALLBUF]; +char wincompat_password[SMALLBUF]; + +uid_t getuid(void) +{ + DWORD size = sizeof(wincompat_user_name); + if( !GetUserName(wincompat_user_name,&size) ) { + return NULL; + } + + return wincompat_user_name; +} + +struct passwd *getpwuid(uid_t uid) +{ + wincompat_passwd.pw_name = uid; + wincompat_passwd.pw_uid = 0; + return &wincompat_passwd; +} + +char *getpass( const char *prompt) +{ + HANDLE hStdin; + DWORD mode; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + if(hStdin == INVALID_HANDLE_VALUE) { + return NULL; + } + + printf("%s",prompt); + + GetConsoleMode( hStdin, &mode ); + mode &= ~ENABLE_ECHO_INPUT; + SetConsoleMode( hStdin , mode); + + if (fgets(wincompat_password, sizeof(wincompat_password), stdin) == NULL) { + upsdebug_with_errno(LOG_INFO, "%s", __func__); + return NULL; + } + + /* deal with that pesky newline */ + if (strlen(wincompat_password) > 1) { + wincompat_password[strlen(wincompat_password) - 1] = '\0'; + }; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode( hStdin, &mode ); + mode |= ENABLE_ECHO_INPUT; + SetConsoleMode( hStdin , mode); + + return wincompat_password; +} + +#ifndef HAVE_USLEEP +/* Verbatim from +http://cygwin.com/cgi-bin/cvsweb.cgi/~checkout~/src/winsup/mingw/mingwex/usleep.c?rev=1.2&cvsroot=src */ +/* int __cdecl usleep(unsigned int useconds) */ +int __cdecl usleep(useconds_t useconds) +{ + if(useconds == 0) + return 0; + + if(useconds >= 1000000) + return EINVAL; + + Sleep((useconds + 999) / 1000); + + return 0; +} +#endif /* !HAVE_USLEEP */ + +char * strtok_r(char *str, const char *delim, char **saveptr) +{ + char *token_start, *token_end; + + /* Subsequent call ? */ + token_start = str ? str : *saveptr; + + /* Skip delim characters */ + token_start += strspn(token_start, delim); + if (*token_start == '\0') { + /* No more token */ + *saveptr = ""; + return NULL; + } + + /* Skip NO delim characters */ + token_end = token_start + strcspn(token_start, delim); + + /* Prepare token to be a null terminated string */ + if (*token_end != '\0') + *token_end++ = '\0'; + + *saveptr = token_end; + + return token_start; +} + +int sktconnect(int fh, struct sockaddr * name, int len) +{ + int ret = connect(fh,name,len); + errno = WSAGetLastError(); + return ret; +} +int sktread(int fh, char *buf, int size) +{ + int ret = recv(fh,buf,size,0); + errno = WSAGetLastError(); + return ret; +} +int sktwrite(int fh, char *buf, int size) +{ + int ret = send(fh,buf,size,0); + errno = WSAGetLastError(); + return ret; +} +int sktclose(int fh) +{ + int ret = closesocket((SOCKET)fh); + errno = WSAGetLastError(); + return ret; +} + +#if ! HAVE_INET_NTOP +# if (0) +/* Some old winapi? or just sloppy original commits? */ +const char* inet_ntop(int af, const void* src, char* dst, int cnt) +# else +const char* inet_ntop(int af, const void* src, char* dst, size_t cnt) +# endif +{ +/* Instead of WSAAddressToString() consider getnameinfo() if this would in fact + * return decorated addresses (brackets, ports...) as discussed below: + * https://users.ipv6.narkive.com/RXpR5aML/windows-and-inet-ntop-vs-wsaaddresstostring + * https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getnameinfo + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaaddresstostringa + */ + switch (af) { + case AF_INET: + { + struct sockaddr_in srcaddr; + memset(&srcaddr, 0, sizeof(struct sockaddr_in)); + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + srcaddr.sin_family = af; + if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0) { + WSAGetLastError(); + return NULL; + } + } + break; + + case AF_INET6: + /* NOTE: Since WinXP SP1, with IPv6 installed on the system */ + { + struct sockaddr_in6 srcaddr; + memset(&srcaddr, 0, sizeof(struct sockaddr_in6)); + memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr)); + srcaddr.sin6_family = af; + if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in6), 0, dst, (LPDWORD) &cnt) != 0) { + WSAGetLastError(); + return NULL; + } + } + break; + + default: + errno = EAFNOSUPPORT; + return NULL; + } /* switch */ + + return dst; +} +#endif /* HAVE_INET_NTOP */ + +#if ! HAVE_INET_PTON +/* Fallback implementation of inet_pton() for systems that lack it, + * such as older versions of Windows (including MinGW builds that do + * not specifically target _WIN32_WINNT or newer). + * + * Based on code attributed to Paul Vixie, 1996, + * sourced from https://stackoverflow.com/a/15370175/4715872 + */ + +#define NS_INADDRSZ sizeof(struct in_addr) /* 4 */ +#define NS_IN6ADDRSZ sizeof(struct in6_addr) /* 16 */ +#define NS_INT16SZ sizeof(uint16_t) /* 2 */ + +static int inet_pton4(const char *src, void *dst) +{ + uint8_t tmp[NS_INADDRSZ], *tp; /* for struct in_addr *dst */ + + int saw_digit = 0; + int octets = 0; + int ch; + + *(tp = tmp) = 0; + + while ((ch = *src++) != '\0') + { + if (ch >= '0' && ch <= '9') + { + uint32_t n = *tp * 10 + (ch - '0'); + + if (saw_digit && *tp == 0) + return 0; + + if (n > 255) + return 0; + + *tp = n; + if (!saw_digit) + { + if (++octets > 4) + return 0; + saw_digit = 1; + } + } + else if (ch == '.' && saw_digit) + { + if (octets == 4) + return 0; + *++tp = 0; + saw_digit = 0; + } + else + return 0; + } + if (octets < 4) + return 0; + + memcpy(dst, tmp, NS_INADDRSZ); + + return 1; +} + +static int inet_pton6(const char *src, void *dst) +{ + static const char xdigits[] = "0123456789abcdef"; + uint8_t tmp[NS_IN6ADDRSZ]; /* for struct in6_addr *dst */ + + uint8_t *tp = (uint8_t*) memset(tmp, '\0', NS_IN6ADDRSZ); + uint8_t *endp = tp + NS_IN6ADDRSZ; + uint8_t *colonp = NULL; + + const char *curtok = NULL; + int saw_xdigit = 0; + uint32_t val = 0; + int ch; + + /* Leading :: requires some special handling. */ + if (*src == ':') + { + if (*++src != ':') + return 0; + } + + curtok = src; + + while ((ch = tolower(*src++)) != '\0') + { + const char *pch = strchr(xdigits, ch); + if (pch != NULL) + { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return 0; + saw_xdigit = 1; + continue; + } + if (ch == ':') + { + curtok = src; + if (!saw_xdigit) + { + if (colonp) + return 0; + colonp = tp; + continue; + } + else if (*src == '\0') + { + return 0; + } + if (tp + NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, (char*) tp) > 0) + { + tp += NS_INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return 0; + } + if (saw_xdigit) + { + if (tp + NS_INT16SZ > endp) + return 0; + *tp++ = (uint8_t) (val >> 8) & 0xff; + *tp++ = (uint8_t) val & 0xff; + } + if (colonp != NULL) + { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + return 0; + + for (i = 1; i <= n; i++) + { + endp[-i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return 0; + + memcpy(dst, tmp, NS_IN6ADDRSZ); + + return 1; +} + +int inet_pton(int af, const char *src, void *dst) +{ + switch (af) + { + case AF_INET: + return inet_pton4(src, dst); + case AF_INET6: + return inet_pton6(src, dst); + default: + return -1; + } +} +#endif /* ! HAVE_INET_PTON */ + +/* "system" call seems to handle path with blank name incorrectly */ +int win_system(const char * command) +{ + BOOL res; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si,0,sizeof(si)); + si.cb = sizeof(si); + memset(&pi,0,sizeof(pi)); + + res = CreateProcess(NULL,(char *)command,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + + if( res != 0 ) { + return 0; + } + + return -1; +} + +/* the " character is forbiden in Windows files , so we filter this character +in data file paths to be coherent with command line which require " to +distinguish the command from its parameter. This avoid complicated +explanation in the documentation */ +char * filter_path(const char * source) +{ + char * res; + unsigned int i,j; + + if( source == NULL ) { + return NULL; + } + + res = xmalloc(strlen(source)+1); + for(i=0,j=0;i<=strlen(source);i++) { + if(source[i] != '"') { + res[j] = source[i]; + j++; + } + } + + return res; +} + + +/* syslog sends a message through a pipe to the wininit service. Which is + in charge of adding an event in the Windows event logger. + The message is made of 4 bytes containing the priority followed by an array + of chars containing the message to display (no terminal 0 required here) */ +void syslog(int priority, const char *fmt, ...) +{ + char pipe_name[] = "\\\\.\\pipe\\"EVENTLOG_PIPE_NAME; + char buf1[LARGEBUF+sizeof(DWORD)]; + char buf2[LARGEBUF]; + va_list ap; + HANDLE pipe; + DWORD bytesWritten = 0; + + if( EventLogName == NULL ) { + return; + } + + /* Format message */ + va_start(ap,fmt); + vsnprintf(buf1, sizeof(buf1), fmt, ap); + va_end(ap); + + /* Add progname to the formated message */ + snprintf(buf2, sizeof(buf2), "%s - %s", EventLogName, buf1); + + /* Create the frame */ + /* first 4 bytes are priority */ + memcpy(buf1,&priority,sizeof(DWORD)); + /* then comes the message */ + memcpy(buf1+sizeof(DWORD),buf2,sizeof(buf2)); + + pipe = CreateFile( + pipe_name, /* pipe name */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + + if (pipe == INVALID_HANDLE_VALUE) { + return; + } + + WriteFile (pipe,buf1,strlen(buf2)+sizeof(DWORD),&bytesWritten,NULL); + + /* testing result is useless. If we have an error and try to report it, + this will probably lead to a call to this function and an infinite + loop */ + CloseHandle(pipe); +} + +/* Signal emulation via NamedPipe */ + +static HANDLE pipe_connection_handle; +OVERLAPPED pipe_connection_overlapped; +pipe_conn_t *pipe_connhead = NULL; +static const char *named_pipe_name=NULL; + +void pipe_create(const char * pipe_name) +{ + BOOL ret; + char pipe_full_name[SMALLBUF]; + + /* save pipe name for further use in pipe_connect */ + if( pipe_name == NULL ) { + if( named_pipe_name == NULL ) { + return; + } + } + else { + named_pipe_name = pipe_name; + } + + snprintf(pipe_full_name, sizeof(pipe_full_name), + "\\\\.\\pipe\\%s", named_pipe_name); + + if( pipe_connection_overlapped.hEvent != 0 ) { + CloseHandle(pipe_connection_overlapped.hEvent); + } + memset(&pipe_connection_overlapped,0,sizeof(pipe_connection_overlapped)); + pipe_connection_handle = CreateNamedPipe( + pipe_full_name, + PIPE_ACCESS_INBOUND | /* to server only */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + LARGEBUF, /* output buffer size */ + LARGEBUF, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (pipe_connection_handle == INVALID_HANDLE_VALUE) { + upslogx(LOG_ERR, "Error creating named pipe"); + fatal_with_errno(EXIT_FAILURE, + "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + pipe_connection_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(pipe_connection_overlapped.hEvent == NULL ) { + upslogx(LOG_ERR, "Error creating event"); + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ret = ConnectNamedPipe(pipe_connection_handle,&pipe_connection_overlapped); + if(ret == 0 && GetLastError() != ERROR_IO_PENDING ) { + upslogx(LOG_ERR,"ConnectNamedPipe error"); + } +} + +void pipe_connect() +{ + /* We have detected a connection on the opened pipe. So we start by saving its handle and create a new pipe for future connections */ + pipe_conn_t *conn; + + conn = xcalloc(1,sizeof(*conn)); + conn->handle = pipe_connection_handle; + + /* restart a new listening pipe */ + pipe_create(NULL); + + /* A new pipe waiting for new client connection has been created. We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait on the event associated to this IO */ + memset(&conn->overlapped,0,sizeof(conn->overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(conn->overlapped.hEvent == NULL ) { + upslogx(LOG_ERR,"Can't create event for reading event log"); + return; + } + + ReadFile (conn->handle, conn->buf, + sizeof(conn->buf)-1, /* -1 to be sure to have a trailling 0 */ + NULL, &(conn->overlapped)); + + if (pipe_connhead) { + conn->next = pipe_connhead; + pipe_connhead->prev = conn; + } + + pipe_connhead = conn; +} + +void pipe_disconnect(pipe_conn_t *conn) +{ + if( conn->overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->overlapped.hEvent); + conn->overlapped.hEvent = INVALID_HANDLE_VALUE; + } + if( conn->handle != INVALID_HANDLE_VALUE) { + if ( DisconnectNamedPipe(conn->handle) == 0 ) { + upslogx(LOG_ERR, + "DisconnectNamedPipe error : %d", + (int)GetLastError()); + } + CloseHandle(conn->handle); + conn->handle = INVALID_HANDLE_VALUE; + } + if (conn->prev) { + conn->prev->next = conn->next; + } else { + pipe_connhead = conn->next; + } + + if (conn->next) { + conn->next->prev = conn->prev; + } else { + /* conntail = conn->prev; */ + } + + free(conn); +} + +int pipe_ready(pipe_conn_t *conn) +{ + DWORD bytesRead; + BOOL res; + + res = GetOverlappedResult(conn->handle, &conn->overlapped, &bytesRead, FALSE); + if( res == 0 ) { + upslogx(LOG_ERR, "Pipe read error"); + pipe_disconnect(conn); + return 0; + } + return 1; +} + +/* return 1 on error, 0 if OK */ +int send_to_named_pipe(const char * pipe_name, const char * data) +{ + HANDLE pipe; + BOOL result = FALSE; + DWORD bytesWritten = 0; + char buf[SMALLBUF]; + + snprintf(buf, sizeof(buf), "\\\\.\\pipe\\%s", pipe_name); + + pipe = CreateFile( + buf, + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + + if (pipe == INVALID_HANDLE_VALUE) { + return 1; + } + + result = WriteFile (pipe,data,strlen(data)+1,&bytesWritten,NULL); + + if (result == 0 || bytesWritten != strlen(data)+1 ) { + CloseHandle(pipe); + return 1; + } + + CloseHandle(pipe); + return 0; +} + +int w32_setcomm ( serial_handler_t * h, int * flags ) +{ + int ret = 0; + + if( *flags & TIOCM_DTR ) { + if( !EscapeCommFunction(h->handle,SETDTR) ) { + errno = EIO; + ret = -1; + } + } + else { + if( !EscapeCommFunction(h->handle,CLRDTR) ) { + errno = EIO; + ret = -1; + } + } + + if( *flags & TIOCM_RTS ) { + if( !EscapeCommFunction(h->handle,SETRTS) ) { + errno = EIO; + ret = -1; + } + } + else { + if( !EscapeCommFunction(h->handle,CLRRTS) ) { + errno = EIO; + ret = -1; + } + } + + return ret; +} + +int w32_getcomm ( serial_handler_t * h, int * flags ) +{ + BOOL ret_val; + DWORD f; + + ret_val = GetCommModemStatus(h->handle, &f); + if (ret_val == 0) { + errno = EIO; + return -1; + } + + *flags = f; + + return 0; +} + +/* Serial port wrapper inspired by : +http://serial-programming-in-win32-os.blogspot.com/2008/07/convert-linux-code-to-windows-serial.html */ + +void overlapped_setup (serial_handler_t * sh) +{ + memset (&sh->io_status, 0, sizeof (sh->io_status)); + sh->io_status.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + sh->overlapped_armed = 0; +} + +int w32_serial_read (serial_handler_t * sh, void *ptr, size_t ulen, DWORD timeout) +{ + int tot; + DWORD num; + HANDLE w4; + DWORD minchars = sh->vmin_ ? sh->vmin_ : ulen; + + errno = 0; + + w4 = sh->io_status.hEvent; + + upsdebugx(4, + "w32_serial_read : ulen %" PRIuSIZE ", vmin_ %d, vtime_ %d, hEvent %p", + ulen, sh->vmin_, sh->vtime_, sh->io_status.hEvent); + if (!sh->overlapped_armed) { + SetCommMask (sh->handle, EV_RXCHAR); + ResetEvent (sh->io_status.hEvent); + } + + for (num = 0, tot = 0; ulen; ulen -= num, ptr = (char *)ptr + num) { + DWORD ev; + COMSTAT st; + DWORD inq = 1; + + num = 0; + + if (!sh->vtime_ && !sh->vmin_) { + inq = ulen; + } + else if (sh->vtime_) { + /* non-interruptible -- have to use kernel timeouts + also note that this is not strictly correct. + if vmin > ulen then things won't work right. + sh->overlapped_armed = -1; + */ + inq = ulen; + } + + if (!ClearCommError (sh->handle, &ev, &st)) { + goto err; + } + else if (ev) { + upsdebugx(4, + "w32_serial_read : error detected %x", + (int)ev); + } + else if (st.cbInQue) { + inq = st.cbInQue; + } + else if (!sh->overlapped_armed) { + if ((size_t)tot >= minchars) { + break; + } + else if (WaitCommEvent (sh->handle, &ev, &sh->io_status)) { + /* WaitCommEvent succeeded */ + if (!ev) { + continue; + } + } + else if (GetLastError () != ERROR_IO_PENDING) { + goto err; + } + else { + sh->overlapped_armed = 1; + switch (WaitForSingleObject (w4, timeout)) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult (sh->handle, &sh->io_status, &num, FALSE)) { + goto err; + } + upsdebugx(4, + "w32_serial_read : characters are available on input buffer"); + break; + case WAIT_TIMEOUT: + if(!tot) { + CancelIo(sh->handle); + sh->overlapped_armed = 0; + ResetEvent (sh->io_status.hEvent); + upsdebugx(4, + "w32_serial_read : timeout %d ms elapsed", + (int)timeout); + SetLastError(WAIT_TIMEOUT); + errno = 0; + return 0; + } + default: + goto err; + } + } + } + + sh->overlapped_armed = 0; + ResetEvent (sh->io_status.hEvent); + if (inq > ulen) { + inq = ulen; + } + upsdebugx(4, + "w32_serial_read : Reading %d characters", + (int)inq); + if (ReadFile (sh->handle, ptr, min (inq, ulen), &num, &sh->io_status)) { + /* Got something */; + } + else if (GetLastError () != ERROR_IO_PENDING) { + goto err; + } + else if (!GetOverlappedResult (sh->handle, &sh->io_status, &num, TRUE)) { + goto err; + } + + tot += num; + upsdebugx(4, + "w32_serial_read : total characters read = %d", + tot); + if (sh->vtime_ || !sh->vmin_ || !num) { + break; + } + continue; + +err: + PurgeComm (sh->handle, PURGE_RXABORT); + upsdebugx(4, + "w32_serial_read : err %d", + (int)GetLastError()); + if (GetLastError () == ERROR_OPERATION_ABORTED) { + num = 0; + } + else + { + errno = EIO; + tot = -1; + break; + } + } + + return tot; +} + +/* Cover function to WriteFile to provide Posix interface and semantics + (as much as possible). */ +int w32_serial_write (serial_handler_t * sh, const void *ptr, size_t len) +{ + DWORD bytes_written; + OVERLAPPED write_status; + + errno = 0; + + memset (&write_status, 0, sizeof (write_status)); + write_status.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + + for (;;) + { + if (WriteFile (sh->handle, ptr, len, &bytes_written, &write_status)) + break; + + switch (GetLastError ()) + { + case ERROR_OPERATION_ABORTED: + continue; + case ERROR_IO_PENDING: + break; + default: + goto err; + } + + if (!GetOverlappedResult (sh->handle, &write_status, &bytes_written, TRUE)) + goto err; + + break; + } + + CloseHandle(write_status.hEvent); + + return bytes_written; + +err: + CloseHandle(write_status.hEvent); + errno = EIO; + return -1; +} + +serial_handler_t * w32_serial_open (const char *name, int flags) +{ + /* flags are currently ignored, it's here just to have the same + interface as POSIX open */ + NUT_UNUSED_VARIABLE(flags); + COMMTIMEOUTS to; + + errno = 0; + + upslogx(LOG_INFO, "w32_serial_open (%s)", name); + + serial_handler_t * sh; + + sh = xmalloc(sizeof(serial_handler_t)); + memset(sh,0,sizeof(serial_handler_t)); + + sh->handle = CreateFile(name, + GENERIC_READ|GENERIC_WRITE, + 0, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + 0); + + if(sh->handle == INVALID_HANDLE_VALUE) { + upslogx(LOG_ERR, "could not open %s", name); + errno = EPERM; + return NULL; + } + + SetCommMask (sh->handle, EV_RXCHAR); + + overlapped_setup (sh); + + memset (&to, 0, sizeof (to)); + SetCommTimeouts (sh->handle, &to); + + /* Reset serial port to known state of 9600-8-1-no flow control + on open for better behavior under Win 95. + */ + DCB state; + GetCommState (sh->handle, &state); + upslogx (LOG_INFO, "setting initial state on %s", name); + state.BaudRate = CBR_9600; + state.ByteSize = 8; + state.StopBits = ONESTOPBIT; + state.Parity = NOPARITY; /* FIXME: correct default? */ + state.fBinary = TRUE; /* binary xfer */ + state.EofChar = 0; /* no end-of-data in binary mode */ + state.fNull = FALSE; /* don't discard nulls in binary mode */ + state.fParity = FALSE; /* ignore parity errors */ + state.fErrorChar = FALSE; + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + state.fOutX = FALSE; /* disable transmission flow control */ + state.fInX = FALSE; /* disable reception flow control */ + state.XonChar = 0x11; + state.XoffChar = 0x13; + state.fOutxDsrFlow = FALSE; /* disable DSR flow control */ + state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except + DTR */ + state.fOutxCtsFlow = FALSE; /* disable output flow control */ + state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */ + state.fDsrSensitivity = FALSE; /* don't assert DSR */ + state.fAbortOnError = TRUE; + + if (!SetCommState (sh->handle, &state)) { + upslogx (LOG_ERR, + "couldn't set initial state for %s", + name); + } + + SetCommMask (sh->handle, EV_RXCHAR); + + upslogx (LOG_INFO, + "%p = w32_serial_open (%s)", + sh->handle, name); + return sh; +} + +int w32_serial_close (serial_handler_t * sh) +{ + if( sh->io_status.hEvent != INVALID_HANDLE_VALUE ) { + CloseHandle (sh->io_status.hEvent); + } + if( sh->handle != INVALID_HANDLE_VALUE ) { + CloseHandle (sh->handle); + } + free(sh); + + errno = 0; + + return 0; +} + +/* tcsendbreak: POSIX 7.2.2.1 */ +/* Break for 250-500 milliseconds if duration == 0 */ +/* Otherwise, units for duration are undefined */ +int tcsendbreak (serial_handler_t * sh, int duration) +{ + unsigned int sleeptime = 300000; + + errno = 0; + + if (duration > 0) + sleeptime *= duration; + + if (SetCommBreak (sh->handle) == 0) { + errno = EIO; + return -1; + } + + /* FIXME: need to send zero bits during duration */ + usleep (sleeptime); + + if (ClearCommBreak (sh->handle) == 0) { + errno = EIO; + return -1; + } + + upslogx(LOG_DEBUG, "0 = tcsendbreak (%d)", duration); + + return 0; +} + +/* tcdrain: POSIX 7.2.2.1 */ +int tcdrain (serial_handler_t * sh) +{ + errno = 0; + + if (FlushFileBuffers (sh->handle) == 0) { + errno = EIO; + return -1; + } + + return 0; +} + +/* tcflow: POSIX 7.2.2.1 */ +int tcflow (serial_handler_t * sh, int action) +{ + DWORD win32action = 0; + DCB dcb; + char xchar; + + errno = 0; + + upslogx(LOG_DEBUG, "action %d", action); + + switch (action) + { + case TCOOFF: + win32action = SETXOFF; + break; + case TCOON: + win32action = SETXON; + break; + case TCION: + case TCIOFF: + if (GetCommState (sh->handle, &dcb) == 0) + return -1; + if (action == TCION) + xchar = (dcb.XonChar ? dcb.XonChar : 0x11); + else + xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13); + if (TransmitCommChar (sh->handle, xchar) == 0) + return -1; + return 0; + break; + default: + return -1; + break; + } + + if (EscapeCommFunction (sh->handle, win32action) == 0) { + errno = EIO; + return -1; + } + + return 0; +} + +/* tcflush: POSIX 7.2.2.1 */ +int tcflush (serial_handler_t * sh, int queue) +{ + int max; + + errno = 0; + + if (queue == TCOFLUSH || queue == TCIOFLUSH) + PurgeComm (sh->handle, PURGE_TXABORT | PURGE_TXCLEAR); + + if ((queue == TCIFLUSH) | (queue == TCIOFLUSH)) + /* Input flushing by polling until nothing turns up + (we stop after 1000 chars anyway) */ + for (max = 1000; max > 0; max--) + { + DWORD ev; + COMSTAT st; + if (!PurgeComm (sh->handle, PURGE_RXABORT | PURGE_RXCLEAR)) + break; + Sleep (100); + if (!ClearCommError (sh->handle, &ev, &st) || !st.cbInQue) + break; + } + + return 0; +} + +/* tcsetattr: POSIX 7.2.1.1 */ +int tcsetattr (serial_handler_t * sh, int action, const struct termios *t) +{ + /* Possible actions: +TCSANOW: immediately change attributes. +TCSADRAIN: flush output, then change attributes. +TCSAFLUSH: flush output and discard input, then change attributes. + */ + + BOOL dropDTR = FALSE; + COMMTIMEOUTS to; + DCB ostate, state; + unsigned int ovtime = sh->vtime_, ovmin = sh->vmin_; + + errno = 0; + + upslogx(LOG_DEBUG, "action %d", action); + if ((action == TCSADRAIN) || (action == TCSAFLUSH)) + { + FlushFileBuffers (sh->handle); + upslogx(LOG_DEBUG, "flushed file buffers"); + } + if (action == TCSAFLUSH) + PurgeComm (sh->handle, (PURGE_RXABORT | PURGE_RXCLEAR)); + + /* get default/last comm state */ + if (!GetCommState (sh->handle, &ostate)) { + errno = EIO; + return -1; + } + + state = ostate; + + /* -------------- Set baud rate ------------------ */ + /* FIXME: WIN32 also has 14400, 56000, 128000, and 256000. + Unix also has 230400. */ + + switch (t->c_ospeed) + { + case B0: /* drop DTR */ + dropDTR = TRUE; + state.BaudRate = 0; + break; + case B110: + state.BaudRate = CBR_110; + break; + case B300: + state.BaudRate = CBR_300; + break; + case B600: + state.BaudRate = CBR_600; + break; + case B1200: + state.BaudRate = CBR_1200; + break; + case B2400: + state.BaudRate = CBR_2400; + break; + case B4800: + state.BaudRate = CBR_4800; + break; + case B9600: + state.BaudRate = CBR_9600; + break; + case B19200: + state.BaudRate = CBR_19200; + break; + case B38400: + state.BaudRate = CBR_38400; + break; + case B57600: + state.BaudRate = CBR_57600; + break; + case B115200: + state.BaudRate = CBR_115200; + break; + default: + /* Unsupported baud rate! */ + upslogx(LOG_ERR, + "Invalid t->c_ospeed %d", + t->c_ospeed); + errno = EINVAL; + return -1; + } + + /* -------------- Set byte size ------------------ */ + + switch (t->c_cflag & CSIZE) + { + case CS5: + state.ByteSize = 5; + break; + case CS6: + state.ByteSize = 6; + break; + case CS7: + state.ByteSize = 7; + break; + case CS8: + state.ByteSize = 8; + break; + default: + /* Unsupported byte size! */ + upslogx(LOG_ERR, + "Invalid t->c_cflag byte size %d", + t->c_cflag & CSIZE); + errno = EINVAL; + return -1; + } + + /* -------------- Set stop bits ------------------ */ + + if (t->c_cflag & CSTOPB) + state.StopBits = TWOSTOPBITS; + else + state.StopBits = ONESTOPBIT; + + /* -------------- Set parity ------------------ */ + + if (t->c_cflag & PARENB) + state.Parity = (t->c_cflag & PARODD) ? ODDPARITY : EVENPARITY; + else + state.Parity = NOPARITY; + + state.fBinary = TRUE; /* Binary transfer */ + state.EofChar = 0; /* No end-of-data in binary mode */ + state.fNull = FALSE; /* Don't discard nulls in binary mode */ + + /* -------------- Parity errors ------------------ */ + /* fParity combines the function of INPCK and NOT IGNPAR */ + + if ((t->c_iflag & INPCK) && !(t->c_iflag & IGNPAR)) + state.fParity = TRUE; /* detect parity errors */ + else + state.fParity = FALSE; /* ignore parity errors */ + + /* Only present in Win32, Unix has no equivalent */ + state.fErrorChar = FALSE; + state.ErrorChar = 0; + + /* -------------- Set software flow control ------------------ */ + /* Set fTXContinueOnXoff to FALSE. This prevents the triggering of a + premature XON when the remote device interprets a received character + as XON (same as IXANY on the remote side). Otherwise, a TRUE + value separates the TX and RX functions. */ + + state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */ + + /* Transmission flow control */ + if (t->c_iflag & IXON) + state.fOutX = TRUE; /* enable */ + else + state.fOutX = FALSE; /* disable */ + + /* Reception flow control */ + if (t->c_iflag & IXOFF) + state.fInX = TRUE; /* enable */ + else + state.fInX = FALSE; /* disable */ + + /* XoffLim and XonLim are left at default values */ + + state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11); + state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13); + + /* -------------- Set hardware flow control ------------------ */ + + /* Disable DSR flow control */ + state.fOutxDsrFlow = FALSE; + + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* RTS/CTS flow control */ + if (t->c_cflag & CRTSCTS) + { /* enable */ + state.fOutxCtsFlow = TRUE; + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + } + else + { /* disable */ + state.fRtsControl = RTS_CONTROL_ENABLE; + state.fOutxCtsFlow = FALSE; + } + + /* + if (t->c_cflag & CRTSXOFF) + state.fRtsControl = RTS_CONTROL_HANDSHAKE; + */ + + /* -------------- DTR ------------------ */ + /* Assert DTR on device open */ + + state.fDtrControl = DTR_CONTROL_ENABLE; + + /* -------------- DSR ------------------ */ + /* Assert DSR at the device? */ + + if (t->c_cflag & CLOCAL) + state.fDsrSensitivity = FALSE; /* no */ + else + state.fDsrSensitivity = TRUE; /* yes */ + + /* -------------- Error handling ------------------ */ + /* Since read/write operations terminate upon error, we + will use ClearCommError() to resume. */ + + state.fAbortOnError = TRUE; + + /* -------------- Set state and exit ------------------ */ + if (memcmp (&ostate, &state, sizeof (state)) != 0) + SetCommState (sh->handle, &state); + + sh->r_binary = ((t->c_iflag & IGNCR) ? 0 : 1); + sh->w_binary = ((t->c_oflag & ONLCR) ? 0 : 1); + + if (dropDTR == TRUE) + EscapeCommFunction (sh->handle, CLRDTR); + else + { + /* FIXME: Sometimes when CLRDTR is set, setting + state.fDtrControl = DTR_CONTROL_ENABLE will fail. This + is a problem since a program might want to change some + parameters while DTR is still down. */ + + EscapeCommFunction (sh->handle, SETDTR); + } + + /* + The following documentation on was taken from "Linux Serial Programming + HOWTO". It explains how MIN (t->c_cc[VMIN] || vmin_) and TIME + (t->c_cc[VTIME] || vtime_) is to be used. + + In non-canonical input processing mode, input is not assembled into + lines and input processing (erase, kill, delete, etc.) does not + occur. Two parameters control the behavior of this mode: c_cc[VTIME] + sets the character timer, and c_cc[VMIN] sets the minimum number of + characters to receive before satisfying the read. + + If MIN > 0 and TIME = 0, MIN sets the number of characters to receive + before the read is satisfied. As TIME is zero, the timer is not used. + + If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will + be satisfied if a single character is read, or TIME is exceeded (t = + TIME *0.1 s). If TIME is exceeded, no character will be returned. + + If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The + read will be satisfied if MIN characters are received, or the time + between two characters exceeds TIME. The timer is restarted every time + a character is received and only becomes active after the first + character has been received. + + If MIN = 0 and TIME = 0, read will be satisfied immediately. The + number of characters currently available, or the number of characters + requested will be returned. According to Antonino (see contributions), + you could issue a fcntl(fd, F_SETFL, FNDELAY); before reading to get + the same result. + */ + + if (t->c_lflag & ICANON) + { + sh->vmin_ = MAXDWORD; + sh->vtime_ = 0; + } + else + { + sh->vtime_ = t->c_cc[VTIME] * 100; + sh->vmin_ = t->c_cc[VMIN]; + } + + upslogx(LOG_DEBUG, + "vtime %d, vmin %d\n", + sh->vtime_, sh->vmin_); + + if (ovmin == sh->vmin_ && ovtime == sh->vtime_) { + errno = EINVAL; + return 0; + } + + memset (&to, 0, sizeof (to)); + + if ((sh->vmin_ > 0) && (sh->vtime_ == 0)) + { + /* Returns immediately with whatever is in buffer on a ReadFile(); + or blocks if nothing found. We will keep calling ReadFile(); until + vmin_ characters are read */ + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + to.ReadTotalTimeoutConstant = MAXDWORD - 1; + } + else if ((sh->vmin_ == 0) && (sh->vtime_ > 0)) + { + /* set timeoout constant appropriately and we will only try to + read one character in ReadFile() */ + to.ReadTotalTimeoutConstant = sh->vtime_; + to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD; + } + else if ((sh->vmin_ > 0) && (sh->vtime_ > 0)) + { + /* time applies to the interval time for this case */ + to.ReadIntervalTimeout = sh->vtime_; + } + else if ((sh->vmin_ == 0) && (sh->vtime_ == 0)) + { + /* returns immediately with whatever is in buffer as per + Time-Outs docs in Win32 SDK API docs */ + to.ReadIntervalTimeout = MAXDWORD; + } + + upslogx(LOG_DEBUG, + "ReadTotalTimeoutConstant %d, " + "ReadIntervalTimeout %d, " + "ReadTotalTimeoutMultiplier %d", + (int)to.ReadTotalTimeoutConstant, + (int)to.ReadIntervalTimeout, + (int)to.ReadTotalTimeoutMultiplier); + + int res = SetCommTimeouts(sh->handle, &to); + if (!res) + { + upslogx(LOG_ERR, "SetCommTimeout failed"); + errno = EIO; + return -1; + } + + return 0; +} + +/* tcgetattr: POSIX 7.2.1.1 */ +int tcgetattr (serial_handler_t * sh, struct termios *t) +{ + DCB state; + + errno = 0; + + /* Get current Win32 comm state */ + if (GetCommState (sh->handle, &state) == 0) { + errno = EIO; + return -1; + } + + /* for safety */ + memset (t, 0, sizeof (*t)); + + /* -------------- Baud rate ------------------ */ + + switch (state.BaudRate) + { + case 0: + /* FIXME: need to drop DTR */ + t->c_cflag = t->c_ospeed = t->c_ispeed = B0; + break; + case CBR_110: + t->c_cflag = t->c_ospeed = t->c_ispeed = B110; + break; + case CBR_300: + t->c_cflag = t->c_ospeed = t->c_ispeed = B300; + break; + case CBR_600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B600; + break; + case CBR_1200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B1200; + break; + case CBR_2400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B2400; + break; + case CBR_4800: + t->c_cflag = t->c_ospeed = t->c_ispeed = B4800; + break; + case CBR_9600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B9600; + break; + case CBR_19200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B19200; + break; + case CBR_38400: + t->c_cflag = t->c_ospeed = t->c_ispeed = B38400; + break; + case CBR_57600: + t->c_cflag = t->c_ospeed = t->c_ispeed = B57600; + break; + case CBR_115200: + t->c_cflag = t->c_ospeed = t->c_ispeed = B115200; + break; + default: + /* Unsupported baud rate! */ + upslogx(LOG_ERR, + "Invalid baud rate %d", + (int)state.BaudRate); + errno = EINVAL; + return -1; + } + + /* -------------- Byte size ------------------ */ + + switch (state.ByteSize) + { + case 5: + t->c_cflag |= CS5; + break; + case 6: + t->c_cflag |= CS6; + break; + case 7: + t->c_cflag |= CS7; + break; + case 8: + t->c_cflag |= CS8; + break; + default: + /* Unsupported byte size! */ + upslogx(LOG_ERR, + "Invalid byte size %d", + state.ByteSize); + errno = EINVAL; + return -1; + } + + /* -------------- Stop bits ------------------ */ + + if (state.StopBits == TWOSTOPBITS) + t->c_cflag |= CSTOPB; + + /* -------------- Parity ------------------ */ + + if (state.Parity == ODDPARITY) + t->c_cflag |= (PARENB | PARODD); + if (state.Parity == EVENPARITY) + t->c_cflag |= PARENB; + + /* -------------- Parity errors ------------------ */ + + /* fParity combines the function of INPCK and NOT IGNPAR */ + if (state.fParity == TRUE) + t->c_iflag |= INPCK; + else + t->c_iflag |= IGNPAR; /* not necessarily! */ + + /* -------------- Software flow control ------------------ */ + + /* transmission flow control */ + if (state.fOutX) + t->c_iflag |= IXON; + + /* reception flow control */ + if (state.fInX) + t->c_iflag |= IXOFF; + + t->c_cc[VSTART] = (state.XonChar ? state.XonChar : 0x11); + t->c_cc[VSTOP] = (state.XoffChar ? state.XoffChar : 0x13); + + /* -------------- Hardware flow control ------------------ */ + /* Some old flavors of Unix automatically enabled hardware flow + control when software flow control was not enabled. Since newer + Unices tend to require explicit setting of hardware flow-control, + this is what we do. */ + + /* Input flow-control */ + if ((state.fRtsControl == RTS_CONTROL_HANDSHAKE) && + (state.fOutxCtsFlow == TRUE)) + t->c_cflag |= CRTSCTS; + /* + if (state.fRtsControl == RTS_CONTROL_HANDSHAKE) + t->c_cflag |= CRTSXOFF; + */ + + /* -------------- CLOCAL --------------- */ + /* DSR is only lead toggled only by CLOCAL. Check it to see if + CLOCAL was called. */ + /* FIXME: If tcsetattr() hasn't been called previously, this may + give a false CLOCAL. */ + + if (state.fDsrSensitivity == FALSE) + t->c_cflag |= CLOCAL; + + /* FIXME: need to handle IGNCR */ +#if 0 + if (!sh->r_binary ()) + t->c_iflag |= IGNCR; +#endif + + if (!sh->w_binary) + t->c_oflag |= ONLCR; + + upslogx (LOG_DEBUG, + "vmin_ %d, vtime_ %d", + sh->vmin_, sh->vtime_); + if (sh->vmin_ == MAXDWORD) + { + t->c_lflag |= ICANON; + t->c_cc[VTIME] = t->c_cc[VMIN] = 0; + } + else + { + t->c_cc[VTIME] = sh->vtime_ / 100; + t->c_cc[VMIN] = sh->vmin_; + } + + return 0; +} + +/* FIXME no difference between ispeed and ospeed */ +void cfsetispeed(struct termios * t, speed_t speed) +{ + t->c_ispeed = t->c_ospeed = speed; +} +void cfsetospeed(struct termios * t, speed_t speed) +{ + t->c_ispeed = t->c_ospeed = speed; +} +speed_t cfgetispeed(const struct termios *t) +{ + return t->c_ispeed; +} + +speed_t cfgetospeed(const struct termios *t) +{ + return t->c_ospeed; +} + +#else /* !WIN32 */ + +/* Just avoid: ISO C forbids an empty translation unit [-Werror=pedantic] */ +int main (int argc, char ** argv); + +#endif /* WIN32 */ diff --git a/conf/Makefile.am b/conf/Makefile.am index 847c2994c5..fd384eebbc 100644 --- a/conf/Makefile.am +++ b/conf/Makefile.am @@ -19,22 +19,25 @@ nodist_sysconf_DATA = upssched.conf.sample upsmon.conf.sample SPELLCHECK_SRC = $(dist_sysconf_DATA) \ upssched.conf.sample.in upsmon.conf.sample.in -# NOTE: Due to portability, we do not use a GNU percent-wildcard extension: +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): #%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) -# $(MAKE) -s -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC_ONE="$<" SPELLCHECK_DIR="$(srcdir)" $@ +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ # NOTE: Portable suffix rules do not allow prerequisites, so we shim them here # by a wildcard target in case the make implementation can put the two together. *-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) .sample.sample-spellchecked: - $(MAKE) -s -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC_ONE="$<" SPELLCHECK_DIR="$(srcdir)" $@ + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ .in.in-spellchecked: - $(MAKE) -s -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC_ONE="$<" SPELLCHECK_DIR="$(srcdir)" $@ + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ spellcheck spellcheck-interactive spellcheck-sortdict: - $(MAKE) -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_DIR="$(srcdir)" $@ + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/conf/nut.conf.sample b/conf/nut.conf.sample index 523197ce38..4857933e7d 100644 --- a/conf/nut.conf.sample +++ b/conf/nut.conf.sample @@ -1,19 +1,33 @@ # Network UPS Tools: example nut.conf # +# This file tries to standardize the various files being found in the field, +# like /etc/default/nut on Debian based systems, /etc/sysconfig/ups on RedHat +# based systems, ... Distribution's init script or service unit/method script +# should source this file to see which component(s) has to be started. +# Some scripts and units provided by NUT project itself may also look into +# this file for optional configuration about OS integration. +# +# IMPORTANT NOTES: +# This file is intended to be sourced by standard POSIX shell scripts +# (so there is no guaranteed `export VAR=VAL` syntax) and additionally +# by systemd on Linux (no guaranteed expansion of variables). +# You MUST NOT use spaces around the equal sign! +# Practical support for this file and its settings currently varies between +# various OS packages and NUT sample scripts, but should converge over time. +# +# See also: `man nut.conf` (usually in Manual pages Section 5, +# for Configuration files) +# ############################################################################## # General section ############################################################################## # The MODE determines which part of the NUT is to be started, and which # configuration files must be modified. # -# This file try to standardize the various files being found in the field, like -# /etc/default/nut on Debian based systems, /etc/sysconfig/ups on RedHat based -# systems, ... Distribution's init script should source this file to see which -# component(s) has to be started. -# # The values of MODE can be: # - none: NUT is not configured, or use the Integrated Power Management, or use -# some external system to startup NUT components. So nothing is to be started. +# some external system to startup NUT components. So nothing is to be started +# by scripts or services bundled with NUT packages. # - standalone: This mode address a local only configuration, with 1 UPS # protecting the local system. This implies to start the 3 NUT layers (driver, # upsd and upsmon) and the matching configuration files. This mode can also @@ -23,17 +37,52 @@ # specific LISTEN directive in upsd.conf. # Since this MODE is opened to the network, a special care should be applied # to security concerns. -# - netclient: this mode only requires upsmon. -# -# IMPORTANT NOTE: -# This file is intended to be sourced by standard POSIX shell scripts (so -# there is no guaranteed `export VAR=VAL` syntax) and by systemd on Linux. -# You MUST NOT use spaces around the equal sign! +# - netclient: this mode only requires upsmon (and tools it may be using, like +# upssched or custom scripts) to monitor a remote NUT server and possibly +# shut down this system (part of upsmon must run as root then). MODE=none -# Uncomment this to allow starting the service even if ups.conf has no device -# sections at the moment. This environment variable overrides the built-in -# "false" and an optional same-named default flag that can be set in upsd.conf: +# Uncomment this to allow starting the `upsd` data server even if `ups.conf` +# has no device sections configured at the moment. This environment variable +# overrides the built-in "false" flag in `upsd`, and an optional same-named +# default flag that can be set in `upsd.conf`. If you want a data server always +# running, even if it initially has nothing to serve (may be live-reloaded +# later, when devices become configured), this option is for you. ALLOW_NO_DEVICE=true export ALLOW_NO_DEVICE + +# Uncomment this to allow starting the `upsd` data server even if not all +# `LISTEN` directives can be honoured at the moment. This environment variable +# overrides the built-in "false" flag in `upsd`, and an optional same-named +# default flag that can be set in `upsd.conf`. If you want a data server always +# running, even if it would potentially not serve all clients on every uptime, +# this option is for you (note you would have to restart `upsd` to pick up the +# `LISTEN`ed IP address if it appears later). Probably `LISTEN *` is better. +#ALLOW_NOT_ALL_LISTENERS=true +#export ALLOW_NOT_ALL_LISTENERS + +# The optional 'UPSD_OPTIONS' allow to set upsd specific command-line options. +# It is ignored when 'MODE' above indicates that no upsd should be running. +# It may be redundant in comparison to options which can be set in `upsd.conf`. +#UPSD_OPTIONS= + +# The optional 'UPSMON_OPTIONS' allow to set upsmon specific command-line options. +# It is ignored when 'MODE' above indicates that no upsmon should be running. +# It may be redundant in comparison to options which can be set in `upsmon.conf`. +#UPSMON_OPTIONS= + +# If the optional 'POWEROFF_WAIT' is configured (to a value that can be handled +# by `/bin/sleep` on the current system - typically an integer with the number +# of seconds for a delay, but not always limited to that syntax), and the current +# system which manages one or more UPS devices would not only command it to shut +# down, but also try to avoid the "Power race". Caveats emptor, see NUT FAQ and +# other docs for details. +#POWEROFF_WAIT=3600 + +# The optional 'POWEROFF_QUIET' setting controls if the NUT shutdown integration +# scripts or service units would emit messages about their activity (or lack +# thereof). By default they may be verbose, to aid post-mortem troubleshooting +# via logs or console captures. +# Set to `true` to avoid that trove of information, if you consider it noise. +#POWEROFF_QUIET=true diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index 46b43b7816..7b68fc2574 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -32,12 +32,12 @@ # On a system called "doghouse", the line in your upsmon.conf to monitor # and manage it would look something like this: # -# MONITOR snoopy@doghouse 1 upsmonuser mypassword primary +# MONITOR snoopy@doghouse 1 monuser mypassword primary # # It might look like this if monitoring in "secondary" mode (without any # ability to directly manage the UPS) from a different system: # -# MONITOR snoopy@doghouse 1 upsmonuser mypassword secondary +# MONITOR snoopy@doghouse 1 monuser mypassword secondary # # Configuration directives # ------------------------ @@ -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,32 +53,105 @@ # # 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. +# Note that after this time upsdrvctl would just move +# along with its business (whether retrying the same +# driver if `maxretry>1`, or trying another driver if +# starting them all, or just eventually exit); however, +# each such most recently started "stuck" driver process +# may be further initializing in the background, and +# might even succeed eventually. +# They would not be actively killed by upsdrvctl after +# this timeout expires. +# +# 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. # apcsmart, bestups, and sec are some examples. # # port: REQUIRED. The serial port where your UPS is connected. -# /dev/ttyS0 is usually the first port on Linux boxes, for example. +# For example: +# /dev/ttyS0 is usually the first port on Linux boxes. +# "\\\\.\\COM1" is the first port on Windows boxes (note that +# the backslash characters themselves must be escaped, +# for the NUT configuration parser to yield "\\.\COM1"). # -# 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 +162,40 @@ 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. +# +# usb_config_index=hexnum, usb_hid_rep_index=hexnum, +# usb_hid_desc_index=hexnum, usb_hid_ep_in=hexnum, usb_hid_ep_out=hexnum: +# OPTIONAL. Force use of specific interface, endpoint, descriptor +# index etc. numbers, rather than defaulting to 0 (rarely other +# values in certain drivers for some devices known to use non-zero +# numbers). As a rule of thumb for `usb_hid_desc_index` discovery, +# you can see larger `wDescriptorLength` values (roughly 600+ bytes) +# in reports of `lsusb` or similar tools. +# +# 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..dfbdf9e5ba 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -44,6 +44,25 @@ # Boolean values 'false', 'no', 'off' and '0' mean that the server should refuse # to start if zero device sections were found in ups.conf. This is the default. +# ======================================================================= +# ALLOW_NOT_ALL_LISTENERS +# ALLOW_NOT_ALL_LISTENERS true +# +# Normally upsd requires that all `LISTEN` directives can be honoured at the +# moment the daemon starts. If your LAN IP address (or host name) used in one +# of the `LISTEN` directives may be not always accessible, and for some reason +# do not want to just `LISTEN *` on the wildcard interface, but e.g. you still +# want to use `upsmon` on `localhost`, this option can help. Note you would +# have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +# later. +# +# Boolean values 'true', 'yes', 'on' and '1' mean that the server would not +# refuse to start if it can listen on at least one interface. +# +# Boolean values 'false', 'no', 'off' and '0' mean that the server should +# refuse to start if it can not LISTEN on each and every (non-localhost) +# interface found in upsd.conf. This is the default. + # ======================================================================= # STATEPATH # STATEPATH /var/run/nut @@ -58,18 +77,35 @@ # LISTEN myhostname 83493 # LISTEN myhostname.mydomain # -# This defaults to the localhost listening addresses and port 3493. -# In case of IP v4 or v6 disabled kernel, only the available one will be used. +# With no LISTEN statement, the default is localhost and port 3493. +# In case of IP v4 or v6 disabled kernel, only the available one will +# be used. +# Note that it is not true for Windows platforms. You shouldn't use IPv6 in +# your configuration files unless you have IPv6 installed. +# +# As a special case, `LISTEN * ` (with an asterisk) will try +# to listen on "ANY" IP address for both IPv6 (::0) and IPv4 (0.0.0.0), +# subject to `upsd` command-line arguments, or system configuration. +# Note that if the system supports IPv4-mapped IPv6 addressing per RFC-3493, +# and does not allow to disable this mode, then there may be one listening +# socket to handle both address families. # -# You may specify each interface IP address or name that you want upsd to -#Ā listen on for connections, optionally with a port number. +# One or more LISTEN statements give the IP address (or name that +# resolves to such an address) for upsd to listen on, optionally with +# a port number. # -# You may need this if you have multiple interfaces on your machine and -# you don't want upsd to listen to all interfaces (for instance on a -# firewall, you may not want to listen to the external interface). +# As an example, a machine with a LAN and a WAN interface that also +# functions as a router and firewall might be configured to listen +# only on the LAN interface. # # This will only be read at startup of upsd. If you make changes here, -# you'll need to restart upsd, reload will have no effect. +# you'll need to restart upsd, as reload will have no effect. +# +# Please note that older NUT releases could have been using the IPv4-mapped +# IPv6 addressing (sometimes also known as "dual-stack") mode, if provided +# by the system. Current versions (since NUT v2.8.1 release) explicitly try +# to restrict their listening sockets to only support one address family on +# each socket, and so avoid IPv4-mapped mode where possible. # ======================================================================= # MAXCONN @@ -145,3 +181,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/upsd.users.sample b/conf/upsd.users.sample index c6b9f36ed7..60c42892bf 100644 --- a/conf/upsd.users.sample +++ b/conf/upsd.users.sample @@ -59,9 +59,9 @@ # # --- Configuring for upsmon # -# To add a user for your upsmon, use this example: +# To add a NUT user for your upsmon, with a particular role, use this example: # -# [upsmon] +# [monuser] # password = pass # upsmon primary # or @@ -69,7 +69,7 @@ # # The matching MONITOR line in your upsmon.conf would look like this: # -# MONITOR myups@localhost 1 upsmon pass primary (or secondary) +# MONITOR myups@localhost 1 monuser pass primary (or secondary) # # See comments in the upsmon.conf(.sample) file for details about this # keyword and the difference of NUT secondary and primary systems. diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index de38bff66f..bcda24a549 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -27,6 +27,7 @@ # # This user should not have write access to upsmon.conf. # +# (Unprivileged) OS account to run as: # RUN_AS_USER @RUN_AS_USER@ # -------------------------------------------------------------------------- @@ -66,10 +67,10 @@ # changes for a given UPS without shutting down when it goes critical. # # and must match an entry in that system's -# upsd.users. If your username is "upsmon" and your password is +# upsd.users. If your NUT username is "monuser" and your password is # "blah", the upsd.users would look like this: # -# [upsmon] +# [monuser] # password = blah # upsmon primary # (or secondary) # @@ -113,8 +114,8 @@ # Examples: # # MONITOR myups@bigserver 1 upswired blah primary -# MONITOR su700@server.example.com 1 upsmon secretpass secondary -# MONITOR myups@localhost 1 upsmon pass primary # (or secondary) +# MONITOR su700@server.example.com 1 monuser secretpass secondary +# MONITOR myups@localhost 1 monuser pass primary # (or secondary) # -------------------------------------------------------------------------- # MINSUPPLIES @@ -148,9 +149,49 @@ MINSUPPLIES 1 # off, consider setting the SHUTDOWNCMD temporarily to do something benign - # such as posting a message with 'logger' or 'wall' or 'mailx'. Do be careful # to plug the UPS back into the wall in a timely fashion. +# +# For Windows setup use something like: +# SHUTDOWNCMD "C:\\WINDOWS\\system32\\shutdown.exe -s -t 0" +# If you have command line using space character you have to add double quote to them, like this: +# SHUTDOWNCMD "\"C:\\Program Files\\some command.bat\" -first_arg -second_arg" +# Or use the old DOS 8.3 file name, like this: +# SHUTDOWNCMD "C:\\PROGRA~1\\SOMECO~1.bat -first_arg -second_arg" SHUTDOWNCMD "/sbin/shutdown -h +0" +# -------------------------------------------------------------------------- +# SHUTDOWNEXIT +# +# After initiating shutdown, should this upsmon daemon itself exit? +# By doing so NUT secondary systems can tell the NUT primary that +# it can proceed with its own shutdown and eventually tell the UPS +# to cut power for the load. ("Yes" by default) +# +# Some "secondary" systems with workloads that take considerable time +# to stop (e.g. virtual machines or large databases) can benefit from +# reporting (by virtue of logging off the data server) that they are +# ready for the "primary" system to begin its own shutdown and eventually +# to tell the UPS to cut the power - not as soon as they have triggered +# their own shutdown, but at a later point (e.g. when the upsmon service +# is stopped AFTER the heavier workloads). +# +# Note that the actual ability to complete such shutdown depends on the +# remaining battery run-time at the moment when UPS power state becomes +# considered critical and the shutdowns begin. You may also have to tune +# HOSTSYNC on the NUT primary to be long enough for those secondaries to +# stop their services. In practice, it may be worthwhile to investigate +# ways to trigger shutdowns earlier on these systems, e.g. by setting up +# `upssched` integration, or `dummy-ups` driver with overrides for stricter +# `battery.charge` or `battery.runtime` triggers than used by the rest of +# your servers. +# +# This option supports Boolean-style strings (yes/on/true or no/off/false) +# or numbers to define a delay (in seconds) between calling SHUTDOWNCMD +# and exiting the daemon. Zero means immediate exit (default), negative +# values mean never exiting on its own accord. +# +#SHUTDOWNEXIT yes + # -------------------------------------------------------------------------- # NOTIFYCMD # @@ -247,8 +288,13 @@ DEADTIME 15 # Refer to the section: # [[UPS_shutdown]] "Configuring automatic shutdowns for low battery events" # or refer to the online version. - -POWERDOWNFLAG /etc/killpower +# +# For Windows setup use something like: +# POWERDOWNFLAG "C:\\killpower" +# +# For Unix/Linux systems the legacy default is: +# POWERDOWNFLAG /etc/killpower +POWERDOWNFLAG "@POWERDOWNFLAG@" # -------------------------------------------------------------------------- # NOTIFYMSG - change messages sent by upsmon when certain events occur @@ -267,6 +313,12 @@ POWERDOWNFLAG /etc/killpower # NOTIFYMSG REPLBATT "UPS %s battery needs to be replaced" # NOTIFYMSG NOCOMM "UPS %s is unavailable" # NOTIFYMSG NOPARENT "upsmon parent process died - shutdown impossible" +# NOTIFYMSG CAL "UPS %s: calibration in progress" +# NOTIFYMSG NOTCAL "UPS %s: calibration finished" +# NOTIFYMSG OFF "UPS %s: administratively OFF or asleep" +# NOTIFYMSG NOTOFF "UPS %s: no longer administratively OFF or asleep" +# NOTIFYMSG BYPASS "UPS %s: on bypass (powered, not protecting)" +# NOTIFYMSG NOTBYPASS "UPS %s: no longer on bypass" # # Note that %s is replaced with the identifier of the UPS in question. # @@ -287,7 +339,9 @@ POWERDOWNFLAG /etc/killpower # NOTIFYFLAG - change behavior of upsmon when NOTIFY events occur # # By default, upsmon sends walls (global messages to all logged in users) -# and writes to the syslog when things happen. You can change this. +# and writes to the syslog when things happen. +# Except for Windows where upsmon only writes to the syslog by default. +# You can change this. # # NOTIFYFLAG [+][+] ... # @@ -301,6 +355,12 @@ POWERDOWNFLAG /etc/killpower # NOTIFYFLAG REPLBATT SYSLOG+WALL # NOTIFYFLAG NOCOMM SYSLOG+WALL # NOTIFYFLAG NOPARENT SYSLOG+WALL +# NOTIFYFLAG CAL SYSLOG+WALL +# NOTIFYFLAG NOTCAL SYSLOG+WALL +# NOTIFYFLAG OFF SYSLOG+WALL +# NOTIFYFLAG NOTOFF SYSLOG+WALL +# NOTIFYFLAG BYPASS SYSLOG+WALL +# NOTIFYFLAG NOTBYPASS SYSLOG+WALL # # Possible values for the flags: # @@ -311,6 +371,25 @@ POWERDOWNFLAG /etc/killpower # # If you use IGNORE, don't use any other flags on the same line. +# -------------------------------------------------------------------------- +# OFFDURATION - put "OFF" state into effect if it persists for this many seconds +# +# NUT supports an "administrative OFF" for power devices which can be managed to +# turn off their application workload, while the UPS or ePDU remains accessible +# for monitoring and management. This toggle allows to delay propagation of such +# state into a known loss of a feed (possibly triggering FSD on `upsmon` clients +# which `MONITOR` the device and are in fact still alive -- e.g. with multiple +# power sources or because they as the load are not really turned off), because +# when some devices begin battery calibration, they report "OFF" for a few seconds +# and only then they might report "CAL" after switching all the power relays -- +# thus causing false-positives for `upsmon` FSD trigger. +# +# A negative value means to disable decreasing the counter of working power +# supplies in such cases, and a zero makes the effect of detected "OFF" state +# immediate. Built-in default value is 30 (seconds). + +OFFDURATION 30 + # -------------------------------------------------------------------------- # RBWARNTIME - replace battery warning time in seconds # @@ -333,6 +412,23 @@ RBWARNTIME 43200 NOCOMMWARNTIME 300 +# -------------------------------------------------------------------------- +# POLLFAIL_LOG_THROTTLE_MAX - device poll failure logging throttle +# +# upsmon normally reports polling failures for each device that are in place +# for each POLLFREQ loop (e.g. "Data stale" or "Driver not connected") to +# system log as configured. If your devices are expected to be AWOL for an +# extended timeframe, you can use this throttle to reduce the stress on +# syslog traffic and storage, by posting these messages only once in every +# several loop cycles, and when the error condition has changed or cleared. +# A negative value means standard behavior (log on every loop, same as when +# max=1), and a zero value means to never repeat the message (log only on +# start and end/change of the failure state). +# Note that this throttle only applies to one latest-active error state per +# monitored device. +# +#POLLFAIL_LOG_THROTTLE_MAX 100 + # -------------------------------------------------------------------------- # FINALDELAY - last sleep interval before shutting down the system # @@ -430,3 +526,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/conf/upsset.conf.sample b/conf/upsset.conf.sample index 921a20aa52..3bb4850573 100644 --- a/conf/upsset.conf.sample +++ b/conf/upsset.conf.sample @@ -15,7 +15,7 @@ # your web server, then you should *not* run this program. # # For Apache, the .htaccess file can be used in the directory with the -# programs. You'll need something like this: +# programs. You'll need something like this for older versions: # # # deny from all @@ -25,6 +25,20 @@ # You will probably have to set "AllowOverride Limit" for this directory in # your server-level configuration file as well. # +# Modern Apache enjoys a more detailed syntax, like this: +# +# ScriptAlias /upsstats.cgi /usr/share/nut/cgi/upsstats.cgi +# ScriptAlias /upsset.cgi /usr/share/nut/cgi/upsset.cgi +# +# +# Options +Includes +ExecCGI +# AllowOverride Limit +# +# Require local +# Require ip aa.bb.cc.dd/nn +# +# +# # If this doesn't make sense, then stop reading and leave this program alone. # # Assuming you have all this done (and it works), then you may uncomment diff --git a/configure.ac b/configure.ac index b4716db544..5755a27afa 100644 --- a/configure.ac +++ b/configure.ac @@ -5,11 +5,83 @@ 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.1.1],[https://github.com/networkupstools/nut/issues]) +dnl See docs/maintainer-guide.txt about releases - updating the version +dnl above is a small part of the consistent ritual! + +dnl Note: this refers to GITREV at the time of configure script running +dnl primarily for better messaging in the script itself (also to render +dnl the PDF documentation revision history via docs/docinfo.xml.in). +dnl If the NUT codebase in this workspace is being developed and rebuilt +dnl without reconfiguration, detailed version in the binaries will differ +dnl (that one comes from the NUT_VERSION_MACRO from nut_version.h file). +(command -v git >/dev/null 2>/dev/null) \ +&& NUT_SOURCE_GITREV="`(git describe --tags --match 'v[0-9]*.[0-9]*.[0-9]' --exclude '*-signed' --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*' 2>/dev/null || git describe --tags --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*' --exclude '*Windows*' --exclude '*IPM*' 2>/dev/null ) | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,'`" \ +|| NUT_SOURCE_GITREV="" + +dnl Gitrev-based build identifier which can be used for e.g. PyPI uploads: +NUT_SOURCE_GITREV_NUMERIC="`echo "${NUT_SOURCE_GITREV}" | sed -e 's/^v//' -e 's/-g.*$//' -e 's/-/./g'`" + +dnl Note: except for experiments, do not pass this into config.h - use +dnl the NUT_VERSION_MACRO from nut_version.h instead. Developers may +dnl want to disable their "force-nut-version-header" configure option +dnl to reduce rebuild scope and so speed up the iterations with ccache. +dnl New "config.h" contents for every reconfig would defeat that purpose! +dnl ### AS_IF([test -n "${NUT_SOURCE_GITREV-}"], +dnl ### [AC_DEFINE_UNQUOTED([NUT_SOURCE_GITREV], ["${NUT_SOURCE_GITREV}"], [NUT source revision when the build was configured])], +dnl ### [AC_DEFINE([NUT_SOURCE_GITREV], [NULL], [NUT source revision when the build was configured: not detected])]) + +dnl Keep track of command-line options passed to this script: +AC_MSG_CHECKING([for CONFIG_FLAGS]) +CONFIG_FLAGS="" +dnl Keep each token quoted, in case spaces or other special chars crawl in: +_flag_enable_inplace_runtime="" +for F in "$@" ; do + case "$F" in + *=*" "*) NF="$(echo "$F" | sed "s,=,=',")""'" && F="$NF" ;; + esac + + case "$F" in + --disable-inplace-runtime|--enable-inplace-runtime=no) _flag_enable_inplace_runtime="no" ;; + --enable-inplace-runtime*) _flag_enable_inplace_runtime="$F" ;; + *) + test -z "${CONFIG_FLAGS}" \ + && CONFIG_FLAGS="$F" \ + || CONFIG_FLAGS="${CONFIG_FLAGS} $F" + ;; + esac +done +dnl Keep it "known" that we tried to re-use at least some local configuration +dnl so unspecified options are not necessarily at default values for this script +AS_IF([test x"${NUT_VERSION_DEPLOYED-}" = x""], [_flag_enable_inplace_runtime="reenter"]) +F="" +AS_IF([test x"${NUT_VERSION_DEPLOYED-}" != x && test x"${NUT_VERSION_DEPLOYED-}" != x""], [ + dnl Even if explicitly disabled... did we re-enter? Save the value to be seen: + F="--enable-inplace-runtime='${NUT_VERSION_DEPLOYED}'" + ],[ + AS_CASE(["${_flag_enable_inplace_runtime}"], + [no|""|reenter], [], + [F="${_flag_enable_inplace_runtime}"]) + ]) +AS_IF([test x"$F" != x], [ + test -z "${CONFIG_FLAGS}" \ + && CONFIG_FLAGS="$F" \ + || CONFIG_FLAGS="${CONFIG_FLAGS} $F" + ]) +unset _flag_enable_inplace_runtime +unset F +unset NF +AC_MSG_RESULT([${CONFIG_FLAGS}]) + +AC_DEFINE_UNQUOTED([CONFIG_FLAGS],["${CONFIG_FLAGS}"],[Flags passed to configure script]) +AC_SUBST(CONFIG_FLAGS) + AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) -echo "Network UPS Tools version ${PACKAGE_VERSION}" +AS_IF([test x"${NUT_SOURCE_GITREV}" = x], + [echo "Network UPS Tools version ${PACKAGE_VERSION}"], + [echo "Network UPS Tools version ${PACKAGE_VERSION} (${NUT_SOURCE_GITREV})"]) AC_CANONICAL_TARGET NUT_CHECK_OS NUT_STASH_WARNINGS @@ -17,19 +89,100 @@ AC_CONFIG_HEADERS([include/config.h]) AC_PREFIX_DEFAULT(/usr/local/ups) AM_INIT_AUTOMAKE([subdir-objects]) +AS_CASE([${target_os}], + [*mingw*], [AS_CASE([${CFLAGS-}${CXXFLAGS-}], + [*"-std=c"*|*-ansi*], [ + AC_MSG_NOTICE( +[----------------------------------------------------------------------- +WARNING: It seems you are building with MinGW and requested a strict C/C++ +language mode. Per https://stackoverflow.com/a/76780217/4715872 MinGW may +not define WIN32 and other options needed for proper building and linking. +If this happens, please retry with GNU C/C++ language mode options instead. +-----------------------------------------------------------------------]) + sleep 5 + ])] +) + +dnl +------------------------------------------------------------------- +dnl Help avoid "polluting" CFLAGS etc. with auto-settings like "-g -O2" +dnl at least if we intend to tune --with-debuginfo anyway (where we check +dnl if the caller asked for specific optimizations). Following some +dnl experimentation, we do not want to actually mangle the flags provided +dnl by autoconf - for GCC at least, the rightmost mentions of conflicting +dnl flags on command line win. We just want to know when it is okay to add +dnl ours. They may be inherited from "in-place" build setup though. +test -n "${CONFIG_CFLAGS-}" || CONFIG_CFLAGS="${CFLAGS-}" +test -n "${CONFIG_CXXFLAGS-}" || CONFIG_CXXFLAGS="${CXXFLAGS-}" +dnl +------------------------------------------------------------------- + +dnl Default to `configure --enable-silent-rules` or `make V=1` for details? +dnl This feature seems to require automake-1.13 or newer (1.11+ by other info) +dnl On very old systems can comment it away with little loss (then automake-1.10 +dnl is known to suffice): +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], + [AC_MSG_NOTICE([Silent Rules feature not defined in this automake version, skipped])]) + +dnl +------------------------------------------------------------------- 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) +dnl UPDATE: As tested on CentOS 6, its "autoconf-2.63-5.1.el6.noarch" also suffices. +dnl But OpenBSD 6.5 requires autoconf-2.65 and automake-1.13 or newer... +dnl AC_MSG_NOTICE([CFLAGS_BEFORE_ACPROG="${CFLAGS-}"]) +dnl AC_MSG_NOTICE([CXXFLAGS_BEFORE_ACPROG="${CXXFLAGS-}"]) AC_MSG_CHECKING(for autoconf macro to enable system extensions) m4_version_prereq(2.61, [ AC_MSG_RESULT(yes) + dnl Causes calls to ac_prog stuff, so dittoed below if "no" AC_USE_SYSTEM_EXTENSIONS ], [ AC_MSG_RESULT(no) + AC_PROG_CC ]) +AC_PREREQ([2.63]) +dnl #AC_PREREQ([2.64]) + +dnl Macro AC_PROG_CC_C99 is obsolete; use AC_PROG_CC +dnl Note that NUT does not support building with C89 anyway +dnl AC_PROG_CC_C99 +dnl Needed for per-target flags +AM_PROG_CC_C_O +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXX_C_O + +CFLAGS_AFTER_ACPROG="${CFLAGS-}" +CXXFLAGS_AFTER_ACPROG="${CXXFLAGS-}" + +dnl AC_MSG_NOTICE([CFLAGS_AFTER_ACPROG="${CFLAGS-}"]) +dnl AC_MSG_NOTICE([CXXFLAGS_AFTER_ACPROG="${CXXFLAGS-}"]) + +dnl # LEGACY DEFAULT FALLBACK which NUT had since 2005 or before: +dnl # if autoconf does not set any options, use default/moderate optimizations +CFLAGS=${CFLAGS-"-O"} +dnl # Not so far a legacy, added for consistency in 2024 :) +CXXFLAGS=${CXXFLAGS-"-O"} + +dnl +------------------------------------------------------------------- + dnl Use "./configure --enable-maintainer-mode" to keep Makefile.in and Makefile dnl in sync after Git updates. AM_MAINTAINER_MODE +dnl GNU and BSD make are okay with the syntax, but Sun make/dmake are not: +AC_MSG_CHECKING([whether this make implementation supports export VAR=VAL syntax]) +dnl # using printf formatting for some funniner shells out there +nut_am_output="`printf 'export VAR=VAL\ntest:\n\t@echo "VAR=%s%sVAR%s"\n' '\$' '(' ')' | ${MAKE-make} -f - test`" +nut_am_result="$?" +AS_IF([test x"${nut_am_result}" = x0 -a x"${nut_am_output}" = x"VAR=VAL"], [ + NUT_AM_MAKE_CAN_EXPORT="" + AC_MSG_RESULT(yes) +], [ + NUT_AM_MAKE_CAN_EXPORT="#ThisMakeCanNotExport# " + AC_MSG_RESULT(no: got '${nut_am_output}') +]) +AC_SUBST(NUT_AM_MAKE_CAN_EXPORT) + dnl Some systems have older autotools without direct macro support for PKG_CONF* NUT_CHECK_PKGCONFIG @@ -50,7 +203,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]) @@ -58,7 +211,12 @@ dnl Fix this early so we can expand with eval later test "${prefix}" = "NONE" && prefix="${ac_default_prefix}" test "${exec_prefix}" = "NONE" && exec_prefix='${prefix}' -CFLAGS=${CFLAGS-"-O"} +dnl Note: for practical and platform-independent use, see AX_REALPATH macro +AC_CHECK_PROGS([REALPATH], [realpath], []) + +dnl FIXME: remove after dev-testing +dnl UNITTEST_AX_REALPATH +dnl exit dnl NOTE: for definition of NUT_* autoconf macros, see m4/ directory dnl and docs/macros.txt @@ -68,6 +226,30 @@ dnl | default values for things later on (can be overridden) | STATEPATH="/var/state/ups" +dnl Historically this refers to *system location* for PID files, +dnl and more specifically that for `upsmon` (running as `root`). +dnl See also ALTPIDPATH (defaulted to STATEPATH) setting below +dnl for the unprivileged daemons (`upsd`, drivers). +PIDPATH="/var/run" +dnl Honour new LFS recommendations if applied on the build system: +AS_IF([test -d "/run"], [PIDPATH="/run"]) + +dnl Defaults for respective configure options below +dnl Note these defaults may change further below depending on OS and +dnl certain other configure options (e.g. "in-place replacement") +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"] +) + +dnl NOTE: NUT legacy default, keep as is for least surprise +dnl Distributions are however welcome to specify the option to use tmpfs +AS_CASE([${target_os}], + [*mingw*], [POWERDOWNFLAG="C:\\\\killpower"], dnl NOTE: Double backslashes (times escaping) are correct! + [POWERDOWNFLAG="/etc/killpower"] dnl POSIX systems default +) + cgiexecdir='${exec_prefix}/cgi-bin' driverexecdir='${exec_prefix}/bin' htmldir='${prefix}/html' @@ -79,44 +261,382 @@ if test ! -d "${auglensdir}"; then auglensdir='' fi fi -hotplugdir='/etc/hotplug' -if test ! -d "${hotplugdir}"; then - hotplugdir='' -fi -udevdir='/lib/udev' -if test ! -d "${udevdir}"; then - udevdir='/etc/udev' - if test ! -d "${udevdir}"; then - udevdir='' - fi -fi -devddir='/usr/local/etc/devd' -if test ! -d "${devddir}"; then - devddir='/etc/devd' - if test ! -d "${devddir}"; then - devddir='' - fi -fi +dnl ### NUT_CHECK_LIBREGEX ### Detect below as part of libusb etc. +dnl ### LIBREGEX_LIBS='' +dnl Disable Hotplug, DevD and udev support on Windows +dnl (useful when cross-compiling) +case "${target_os}" in + *mingw* ) + dnl TODO: Actually detect it? See also nut_check_libregex.m4 for same. + dnl Here we assumed from practice that it is available... + dnl ### if test x"${LIBREGEX_LIBS}" = x ; then + dnl ### LIBREGEX_LIBS='-lregex' + dnl ### fi + hotplugdir='' + udevdir='' + devddir='' + ;; + * ) + hotplugdir='/etc/hotplug' + if test ! -d "${hotplugdir}"; then + hotplugdir='' + fi + + udevdir='/lib/udev' + if test ! -d "${udevdir}"; then + udevdir='/etc/udev' + if test ! -d "${udevdir}"; then + udevdir='' + fi + fi -RUN_AS_USER="nobody" -RUN_AS_GROUP="nobody" -PIDPATH="/var/run" + devddir='/usr/local/etc/devd' + if test ! -d "${devddir}"; then + devddir='/etc/devd' + if test ! -d "${devddir}"; then + devddir='' + fi + fi + ;; +esac + +dnl Note: this deals with run-time settings so that the newly built +dnl programs can be "just executed" to use same configuration files +dnl and filesystem object permissions as an older deployment of NUT +dnl on this system; this specifically does not deal with trying to +dnl use same prefix and other paths to fully replace an older setup +dnl (a different option might be crafted to do that, but really the +dnl arcane distro-dependent packaging recipes are a better way here). +dnl This feature is intended for users who want to see if a custom +dnl build of NUT supports their hardware or other use-cases better. +NUT_ARG_ENABLE([inplace-runtime], + [Request configure option defaults for runtime user/group/confpath based on currently installed NUT (to extent these can be detected)], + [no]) + +dnl Check for not re-entering in this run - whether NUT_VERSION_DEPLOYED is set +AS_IF([test x"$nut_enable_inplace_runtime" = xyes -a x"${NUT_VERSION_DEPLOYED-}" = x], [ + AC_MSG_NOTICE([checking for location, CONFIG_FLAGS and version of an already deployed NUT build (if it reports those)]) + + dnl If existing NUT installation reports its build options, re-run this + dnl script with those flags (overridden by any options passed currently): + conftemp="${sbindir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + PREFIX_SBINDIR="${conftemp}" + + conftemp="${bindir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + PREFIX_BINDIR="${conftemp}" + + DEPLOYED_SBINDIR="${PREFIX_SBINDIR}" + DEPLOYED_BINDIR="${PREFIX_BINDIR}" + dnl Note use of AC_PATH_PROG for specific pathname (not AC_CHECK_PROGS which picks filename variants)! + dnl Note: "ordinary" users might not have "sbin" in their default PATH, so appending it: + dnl Note: in some systems, the file in PATH is a shell + dnl wrapper for /etc/nut/nut.conf MODE support + AC_PATH_PROG([DEPLOYED_UPSD], [upsd], [], [${PREFIX_SBINDIR}:${PATH}:/sbin:/usr/sbin:/usr/local/sbin]) + dnl ...however "upsc" is a userland tool so should be there: + AC_PATH_PROG([DEPLOYED_UPSC], [upsc], [], [${PREFIX_BINDIR}:${PATH}]) + AS_IF([test -x "${DEPLOYED_UPSD}"], [ + AX_REALPATH([$DEPLOYED_UPSD], [DEPLOYED_UPSD]) + DEPLOYED_SBINDIR="`dirname "${DEPLOYED_UPSD}"`" + ]) + AS_IF([test -x "${DEPLOYED_UPSC}"], [ + AX_REALPATH([$DEPLOYED_UPSC], [DEPLOYED_UPSC]) + DEPLOYED_BINDIR="`dirname "${DEPLOYED_UPSC}"`" + ]) + + DEPLOYED_PREFIX="" + AS_IF([test -d "${DEPLOYED_SBINDIR}"], + [DEPLOYED_PREFIX="`dirname "${DEPLOYED_SBINDIR}"`"], + [AS_IF([test -d "${DEPLOYED_BINDIR}"], + [DEPLOYED_PREFIX="`dirname "${DEPLOYED_BINDIR}"`"])] + ) + + AC_MSG_CHECKING([for CONFIG_FLAGS of an already deployed NUT build (if it reports those)]) + + CONFIG_FLAGS_DEPLOYED="" + NUT_VERSION_DEPLOYED="" + dnl TODO: Similar query can be done for BINDIR tools + dnl and for libupsclient-config if deployed locally + for DEPLOYED_TOOL in \ + "${DEPLOYED_UPSD}" \ + "${DEPLOYED_UPSC}" \ + ; do + AS_IF([test x"${DEPLOYED_TOOL}" = x], [continue]) + AS_IF([test -x "${DEPLOYED_TOOL}"], [], [continue]) + + AC_MSG_CHECKING([for CONFIG_FLAGS from ${DEPLOYED_TOOL}]) + CONFIG_FLAGS_DEPLOYED="`"${DEPLOYED_TOOL}" -DV 2>&1 | grep 'configured with flags:' | head -1 | sed 's,^.*configured with flags: *,,'`" \ + && test x"${CONFIG_FLAGS_DEPLOYED}" != x \ + || CONFIG_FLAGS_DEPLOYED="" + + NUT_VERSION_DEPLOYED="`"${DEPLOYED_TOOL}" -DV 2>&1 | grep 'configured with flags:' | head -1 | sed 's,^.*Network UPS Tools version \(.*\) configured with flags:.*$,\1,'`" \ + && test x"${NUT_VERSION_DEPLOYED}" != x \ + || NUT_VERSION_DEPLOYED="" + + AS_IF([test x"${CONFIG_FLAGS_DEPLOYED}${NUT_VERSION_DEPLOYED}" = x], [], [break]) + done + + AS_IF([test x"${CONFIG_FLAGS_DEPLOYED}" != x], [ + AC_MSG_RESULT([CONFIG_FLAGS_DEPLOYED: ${CONFIG_FLAGS_DEPLOYED}]) + ],[ + AC_MSG_RESULT([CONFIG_FLAGS_DEPLOYED: not reported]) + ]) + + dnl Account for custom paths if known/found: + AS_IF([test x"${DEPLOYED_PREFIX}" != x && test x"${DEPLOYED_PREFIX}" != x"${prefix}"], + AC_MSG_WARN([Deployed NUT installation uses a PREFIX different from one specified (wins) or derived (loses) for this build: '${DEPLOYED_PREFIX}' vs. '${prefix}']) + [AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--prefix=*], [], + [CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --prefix='${DEPLOYED_PREFIX}'"]) + ]) + + AS_IF([test x"${DEPLOYED_SBINDIR}" != x && test x"${DEPLOYED_SBINDIR}" != x"${PREFIX_SBINDIR}"], + AC_MSG_WARN([Deployed NUT installation uses a SBINDIR different from one specified (wins) or derived (loses) for this build: '${DEPLOYED_SBINDIR}' vs. '${sbindir}']) + [AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--sbindir=*], [], + [CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --sbindir='${DEPLOYED_SBINDIR}'"]) + ]) + + AS_IF([test x"${DEPLOYED_BINDIR}" != x && test x"${DEPLOYED_BINDIR}" != x"${PREFIX_BINDIR}"], + AC_MSG_WARN([Deployed NUT installation uses a BINDIR different from one specified (wins) or derived (loses) for this build: '${DEPLOYED_BINDIR}' vs. '${bindir}']) + [AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--bindir=*], [], + [CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --bindir='${DEPLOYED_BINDIR}'"]) + ]) + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--sysconfdir=*], [ + for F in ${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS} ; do + case "$F" in + "--sysconfdir="*) sysconfdir="`echo "$F" | ( IFS='=' read K V ; echo "$V" )`" ;; + esac + done + ], + [ + dnl If there was no custom --sysconfdir=/etc/myNUT passed, + dnl try to default it to something from existing deployment. + dnl NOTE: Single-quotes are correct for autotools default, + dnl expanded at runtime (see conftemp tricks below) + + AC_MSG_CHECKING([for in-place replacement default sysconfdir (better than '${sysconfdir}')]) + DEPLOYED_SYSCONFDIR="" + + dnl TODO: Any more reasonable defaults? Pile them on here :) + for D in \ + /etc/nut /etc/ups \ + /usr/etc/nut /usr/etc/ups \ + /usr/local/etc/nut /usr/local/etc/ups \ + /usr/local/nut/etc /usr/local/ups/etc \ + ; do + if test -e "$D/ups.conf" || test -e "$D/upsmon.conf" \ + || ( test -e "$D/upsd.conf" && test -e "$D/upsd.users" ) \ + ; then + DEPLOYED_SYSCONFDIR="$D" + break + else + if test -d "$D/" && test ! -r "$D/" ; then + dnl Keeping order of preference defined by "for" loop: + AC_MSG_WARN([Picking directory '${D}' which exists but current build user may not read]) + DEPLOYED_SYSCONFDIR="$D" + break + fi + fi + done + + AS_IF([test -n "${DEPLOYED_SYSCONFDIR}"], [ + AC_MSG_RESULT([${DEPLOYED_SYSCONFDIR}]) + dnl May be used in searches below: + sysconfdir="${DEPLOYED_SYSCONFDIR}" + CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --sysconfdir='${DEPLOYED_SYSCONFDIR}'" + ], [ + AC_MSG_RESULT([not detected]) + ]) + ]) + + dnl Prepare paths that may be used in user/group searches below: + conftemp="${sysconfdir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + CONFPATH="${conftemp}" + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--libdir=*], [ + for F in ${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS} ; do + case "$F" in + "--libdir="*) libdir="`echo "$F" | ( IFS='=' read K V ; echo "$V" )`" ;; + esac + done + ] + ) + + conftemp="${libdir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + LIBDIR="${conftemp}" + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--libexecdir=*], [ + for F in ${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS} ; do + case "$F" in + "--libexecdir="*) libexecdir="`echo "$F" | ( IFS='=' read K V ; echo "$V" )`" ;; + esac + done + ] + ) + + conftemp="${libexecdir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + LIBEXECDIR="${conftemp}" + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--with-pkgconfig-dir=*], [ + for F in ${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS} ; do + case "$F" in + "--with-pkgconfig-dir="*) pkgconfigdir="`echo "$F" | ( IFS='=' read K V ; echo "$V" )`" ;; + esac + done + ] + ) + + dnl pkgconfigdir may have more indirections: + conftemp="${pkgconfigdir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + PKGCONFIGDIR="${conftemp}" + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--with-group=*], [], + [ + AC_MSG_CHECKING([for in-place replacement default group (better than '${RUN_AS_GROUP}')]) + nut_inplace_group="" + AS_IF([test -d "${udevdir}/rules.d"], + [for F in "${udevdir}/rules.d"/*-nut-*.rules ; do + if test -s "$F" ; then + nut_inplace_group="`grep GROUP= "$F" | head -1 | sed 's,^.* GROUP="*\(.*\)"*.*$,\1,'`" \ + && test -n "`getent group "${nut_inplace_group}"`" \ + && AC_MSG_RESULT([Got from ${F}]) \ + && break \ + || nut_inplace_group="" + fi + done + ]) + + AS_IF([test -z "${nut_inplace_group}"], + [AS_IF([test -n "`getent group nut`"], [nut_inplace_group="nut"], + [AS_IF([test -n "`getent group ups`"], [nut_inplace_group="ups"])])]) + + AS_IF([test -n "${nut_inplace_group}"], [ + AC_MSG_RESULT([${nut_inplace_group}]) + CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --with-group='${nut_inplace_group}'" + ], [ + AC_MSG_RESULT([not detected]) + ]) + ]) + + AS_CASE(["${CONFIG_FLAGS_DEPLOYED} ${CONFIG_FLAGS}"], + [*--with-user=*], [], + [ + AC_MSG_CHECKING([for in-place replacement default user (better than '${RUN_AS_USER}')]) + nut_inplace_user="" + + dnl Beside currently active setting, check the built-in default sugestion + AS_IF([test -s "${CONFPATH}/upsmon.conf"], + [for nut_inplace_user in \ + `grep -E '^ *RUN_AS_USER' "${CONFPATH}/upsmon.conf" | awk '{print $2}'`\ + `grep -E '^ *##*RUN_AS_USER' "${CONFPATH}/upsmon.conf" | awk '{print $2}'`\ + `grep -E '^ *##* *RUN_AS_USER' "${CONFPATH}/upsmon.conf" | awk '{print $3}'`\ + ; do \ + AS_IF([test -z "${nut_inplace_user}"], [ + test -n "`getent passwd "${nut_inplace_user}"`" \ + && AC_MSG_RESULT([Got from ${CONFPATH}/upsmon.conf]) \ + || nut_inplace_user=""]) + done + ]) + + AS_IF([test -s "${CONFPATH}/upsmon.conf.sample" && test -z "${nut_inplace_user}"], + [for nut_inplace_user in \ + `grep -E '^ *RUN_AS_USER' "${CONFPATH}/upsmon.conf.sample" | awk '{print $2}'`\ + `grep -E '^ *##*RUN_AS_USER' "${CONFPATH}/upsmon.conf.sample" | awk '{print $2}'`\ + `grep -E '^ *##* *RUN_AS_USER' "${CONFPATH}/upsmon.conf.sample" | awk '{print $3}'`\ + ; do \ + AS_IF([test -z "${nut_inplace_user}"], [ + test -n "`getent passwd "${nut_inplace_user}"`" \ + && AC_MSG_RESULT([Got from ${CONFPATH}/upsmon.conf]) \ + || nut_inplace_user=""]) + done + ]) + + dnl Note: with default short inplace and no prefix, this would look under + dnl /usr/local/ups/lib/pkgconfig which may be irrelevant for packaged setups + + dnl TODO: Any more reasonable defaults? Pile them on here :) + for F in \ + "${PKGCONFDIR}/libupsclient.pc" \ + "${PKGCONFDIR}/libnutclient.pc" \ + "${LIBDIR}/pkgconfig/libupsclient.pc" \ + "${LIBDIR}/pkgconfig/libnutclient.pc" \ + ; do + AS_IF([test -z "${nut_inplace_user}" && test -s "$F"], + [nut_inplace_user="`grep -E '^ *nutuser=' "$F" | sed 's,^ *nutuser=,,'`" \ + && test -n "`getent passwd "${nut_inplace_user}"`" \ + && AC_MSG_RESULT([Got from $F]) \ + || nut_inplace_user="" + ]) + done + + AS_IF([test -z "${nut_inplace_user}"], + [AS_IF([test -n "`getent passwd nut`"], [nut_inplace_user="nut"], + [AS_IF([test -n "`getent passwd ups`"], [nut_inplace_user="ups"])])]) + + AS_IF([test -n "${nut_inplace_user}"], [ + AC_MSG_RESULT([${nut_inplace_user}]) + CONFIG_FLAGS_DEPLOYED="${CONFIG_FLAGS_DEPLOYED} --with-user='${nut_inplace_user}'" + ], [ + AC_MSG_RESULT([not detected]) + ]) + ]) + + AS_IF([test x"${CONFIG_FLAGS_DEPLOYED}" != x], [ + AC_MSG_NOTICE([Detected CONFIG_FLAGS of an already deployed NUT installation, using them for --inplace-runtime configuration (restarting script)]) + dnl For multiply-specified flags with conflicting values, last mention wins: + AC_MSG_NOTICE([exec "$0" $CONFIG_FLAGS_DEPLOYED $CONFIG_FLAGS --disable-inplace-runtime]) + AS_IF([test x"${NUT_VERSION_DEPLOYED}" != x], [ + AC_MSG_NOTICE([NUT_VERSION_DEPLOYED: ${NUT_VERSION_DEPLOYED}]) + ],[ + NUT_VERSION_DEPLOYED="" + ]) + export NUT_VERSION_DEPLOYED + dnl # Avoid replacement after re-entering: + test -n "${CONFIG_CFLAGS-}" || CONFIG_CFLAGS=" " + test -n "${CONFIG_CXXFLAGS-}" || CONFIG_CXXFLAGS=" " + export CONFIG_CFLAGS + export CONFIG_CXXFLAGS + eval exec "$0" $CONFIG_FLAGS_DEPLOYED $CONFIG_FLAGS --disable-inplace-runtime + ],[ + AC_MSG_NOTICE([No CONFIG_FLAGS were reported or discovered from existing NUT deployment (if any); restarting script for a clean run]) + NUT_VERSION_DEPLOYED="" + export NUT_VERSION_DEPLOYED + dnl # Avoid replacement after re-entering: + test -n "${CONFIG_CFLAGS-}" || CONFIG_CFLAGS=" " + test -n "${CONFIG_CXXFLAGS-}" || CONFIG_CXXFLAGS=" " + export CONFIG_CFLAGS + export CONFIG_CXXFLAGS + AC_MSG_NOTICE([exec "$0" $CONFIG_FLAGS --disable-inplace-runtime]) + eval exec "$0" $CONFIG_FLAGS --disable-inplace-runtime + ]) +]) dnl Define directory where LIBOBJS replacement functions are AC_CONFIG_LIBOBJ_DIR([common]) dnl +------------------------------------------------------------------- -dnl AC_PROG_CC -dnl Macro AC_PROG_CC_C99 is obsolete; use AC_PROG_CC -dnl Note that NUT does not support building with C89 anyway -dnl AC_PROG_CC_C99 -dnl Needed for per-target flags -AM_PROG_CC_C_O -AC_PROG_CPP -AC_PROG_CXX -AC_PROG_CXX_C_O AC_PROG_INSTALL AC_PROG_MKDIR_P AC_PROG_LN_S @@ -128,8 +648,26 @@ AC_C_BIGENDIAN AC_C_INLINE AC_C_FLEXIBLE_ARRAY_MEMBER AC_C_VARARRAYS + +NUT_ARG_ENABLE([keep_nut_report_feature], + [Request that we keep config.nut_report_feature.log (normally deleted by configure script after displaying)], + [no]) + +AS_IF([test x"${NUT_SOURCE_GITREV}" = x], + [NUT_REPORT([configured version], [${PACKAGE_VERSION}])], + [NUT_REPORT([configured version], [${PACKAGE_VERSION} (${NUT_SOURCE_GITREV})])]) + dnl Note: the compiler/pragma/attr methods below are custom for NUT codebase: NUT_COMPILER_FAMILY + +dnl Note: DO NOT AC_DEFINE the "FULL" items: they are generally multi-line. +dnl NUT_REPORT_TARGET([CC_VERSION_FULL], ["${CC_VERSION_FULL}"], [Version and other details of C compiler]) +dnl NUT_REPORT_TARGET([CXX_VERSION_FULL], ["${CXX_VERSION_FULL}"], [Version and other details of C++ compiler]) +dnl NUT_REPORT_TARGET([CPP_VERSION_FULL], ["${CPP_VERSION_FULL}"], [Version and other details of C preprocessor]) +NUT_REPORT_TARGET([CC_VERSION], ["${CC_VERSION}"], [Compact version of C compiler]) +NUT_REPORT_TARGET([CXX_VERSION], ["${CXX_VERSION}"], [Compact version of C++ compiler]) +NUT_REPORT_TARGET([CPP_VERSION], ["${CPP_VERSION}"], [Compact version of C preprocessor]) + dnl Help find warning/error details in a wall of text (must be processed before NUT_COMPILER_FAMILY_FLAGS): NUT_ARG_ENABLE([Wcolor], [Request that compiler output is colorized (no = leave it up to compiler defaults)], @@ -138,7 +676,19 @@ NUT_COMPILER_FAMILY_FLAGS AX_C_PRAGMAS AX_C___ATTRIBUTE__ AX_C_PRINTF_STRING_NULL -AC_CHECK_FUNCS(flock lockf fcvt fcvtl pow10 round abs_val abs) + +dnl All current systems provide time.h; it need not be checked for. +dnl Not all systems provide sys/time.h, but those that do, all allow +dnl you to include it and time.h simultaneously. +dnl NUT codebase provides the include/timehead.h to wrap these nuances. +AC_CHECK_HEADERS_ONCE([sys/time.h time.h sys/types.h sys/socket.h netdb.h]) +dnl ###obsolete### AC_HEADER_TIME +AS_IF([test "$ac_cv_header_sys_time_h" = yes], + [AC_DEFINE([TIME_WITH_SYS_TIME],[1],[Define to 1 if you can safely include both + and . This macro is deemed obsolete by autotools.]) + ], []) + +AC_CHECK_FUNCS(flock lockf fcvt fcvtl dup dup2 abs_val abs) AC_CHECK_HEADER([float.h], [AC_DEFINE([HAVE_FLOAT_H], [1], @@ -146,18 +696,33 @@ AC_CHECK_HEADER([float.h], AC_CHECK_HEADER([math.h], [AC_DEFINE([HAVE_MATH_H], [1], [Define to 1 if you have .])]) -AC_CHECK_FUNCS(fabs fabsf fabsl, [], [], + +AC_CHECK_DECLS([fabs, fabsf, fabsl, pow10, round], [], [], [#ifdef HAVE_MATH_H # include #endif #ifdef HAVE_FLOAT_H # include -#endif]) +#endif +]) AC_CHECK_HEADER([limits.h], [AC_DEFINE([HAVE_LIMITS_H], [1], [Define to 1 if you have .])]) +AC_CHECK_HEADER([stdlib.h], + [AC_DEFINE([HAVE_STDLIB_H], [1], + [Define to 1 if you have .])]) + +AC_CHECK_DECLS(realpath, [], [], +[#ifdef HAVE_LIMITS_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +]) + AC_CHECK_HEADER([signal.h], [AC_DEFINE([HAVE_SIGNAL_H], [1], [Define to 1 if you have .])]) @@ -188,6 +753,11 @@ getrlimit(RLIMIT_NOFILE, &limit); ] ) +AC_CHECK_HEADER([poll.h], + [AC_DEFINE([HAVE_POLL_H], [1], + [Define to 1 if you have .])]) + +SEMLIBS="" AC_CHECK_HEADER([semaphore.h], [AC_DEFINE([HAVE_SEMAPHORE_H], [1], [Define to 1 if you have .]) @@ -206,16 +776,23 @@ sem_destroy(&semaphore); [AC_DEFINE([HAVE_SEMAPHORE], [1], [Define to 1 if you have with usable sem_t sem_init() and sem_destroy().]) AC_MSG_RESULT([ok]) + + dnl Solaris 8 builds complain about indirect dependency involved: + dnl sem_init nut_scanner-nut-scanner.o + dnl (symbol belongs to implicit dependency /usr/lib/librt.so.1) + + AC_CHECK_LIB(rt, sem_init, SEMLIBS="-lrt") ], [AC_MSG_RESULT([no])] ) AC_LANG_POP([C]) ] ) +AM_CONDITIONAL(HAVE_SEMAPHORE_LIBS, test -n "${SEMLIBS}") AC_CHECK_FUNCS(cfsetispeed tcsendbreak) AC_CHECK_FUNCS(seteuid setsid getpassphrase) -AC_CHECK_FUNCS(on_exit strptime setlogmask) +AC_CHECK_FUNCS(on_exit setlogmask) AC_CHECK_DECLS(LOG_UPTO, [], [], [#include ]) dnl These common routines are not available in strict C standard library @@ -236,9 +813,39 @@ dnl# [], [AC_MSG_WARN([Required C library routine not found; try adding __EXTEN dnl These appear with CFLAGS="-std=c99 -D_POSIX_C_SOURCE=200112L": dnl# Methods below currently do not cause build errors for C99+ modes so are dnl# not tested as thoroughly as those further below: - AC_CHECK_FUNCS(strtok_r fileno localtime_r sigemptyset sigaction, +AC_CHECK_FUNCS(strtok_r fileno sigemptyset sigaction, [], [AC_MSG_WARN([Required C library routine not found; try adding -D_POSIX_C_SOURCE=200112L])]) +dnl For these we have a fallback implementation via the other, +dnl if at least one is available, so initial check is quiet. +dnl This typically pops up in POSIX vs. Windows builds: +AC_CHECK_FUNCS(localtime_r localtime_s gmtime_r gmtime_s timegm _mkgmtime, + [], []) + +AC_MSG_CHECKING([for at least one gmtime implementation]) +AS_IF([test x"${ac_cv_func_gmtime_s}-${ac_cv_func_gmtime_r}" = "xno-no"], [ + AC_MSG_RESULT([no]) + AC_MSG_WARN([Required C library routine gmtime_s nor gmtime_r was not found; try adding -D_POSIX_C_SOURCE=200112L]) + ],[ + AC_MSG_RESULT([yes]) + ]) + +AC_MSG_CHECKING([for at least one localtime implementation]) +AS_IF([test x"${ac_cv_func_localtime_s}-${ac_cv_func_localtime_r}" = "xno-no"], [ + AC_MSG_RESULT([no]) + AC_MSG_WARN([Required C library routine localtime_s nor localtime_r was not found; try adding -D_POSIX_C_SOURCE=200112L]) + ],[ + AC_MSG_RESULT([yes]) + ]) + +AC_MSG_CHECKING([for at least one timegm implementation]) +AS_IF([test x"${ac_cv_func_timegm}-${ac_cv_func__mkgmtime}" = "xno-no"], [ + AC_MSG_RESULT([no]) + AC_MSG_WARN([Required C library routine timegm nor _mkgmtime was not found]) + ],[ + AC_MSG_RESULT([yes]) + ]) + AC_LANG_PUSH([C]) AC_CHECK_HEADER([string.h], @@ -258,7 +865,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 +877,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] @@ -280,10 +887,144 @@ AS_IF([test x"${ac_cv_func_strncasecmp}" = xyes], [AC_MSG_WARN([Required C library routine strncasecmp not found; try adding __EXTENSIONS__])] ) +dnl Note: this changes the original string in argument! +dnl Do not pass it constants (strdup them if needed)! +AC_CACHE_CHECK([for strlwr(s)], + [ac_cv_func_strlwr], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL], + [char s@<:@64@:>@ = {"Some STR1 text"} ; if (strlwr(s) == NULL) return 1])], + [ac_cv_func_strlwr=yes], [ac_cv_func_strlwr=no] + )]) +AS_IF([test x"${ac_cv_func_strlwr}" = xyes], + [AC_DEFINE([HAVE_STRLWR], 1, [defined if standard library has, and C standard allows, the strlwr(s1,s2) method])], + [AC_MSG_WARN([Optional C library routine strlwr not found])] + ) + +AC_CACHE_CHECK([for strsep(s1,s2)], + [ac_cv_func_strsep], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL], + [char arr@<:@64@:>@ = {"Some,tuple,text"} ; char *s = arr; if (strsep(&s, ",") == NULL) return 1])], + [ac_cv_func_strsep=yes], [ac_cv_func_strsep=no] + )]) +AS_IF([test x"${ac_cv_func_strsep}" = xyes], + [AC_DEFINE([HAVE_STRSEP], 1, [defined if standard library has, and C standard allows, the strsep(s1,s2) method])], + [AC_MSG_WARN([Optional C library routine strsep not found])] + ) +AM_CONDITIONAL([HAVE_STRSEP], [test x"${ac_cv_func_strsep}" = "xyes"]) + +AC_CACHE_CHECK([for strstr(s1,s2)], + [ac_cv_func_strstr], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL], + [if (strstr("Some str1 text", "str1") == NULL) return 1])], + [ac_cv_func_strstr=yes], [ac_cv_func_strstr=no] + )]) +AS_IF([test x"${ac_cv_func_strstr}" = xyes], + [AC_DEFINE([HAVE_STRSTR], 1, [defined if standard library has, and C standard allows, the strstr(s1,s2) method])], + [AC_MSG_ERROR([Required C library routine strstr not found])] + ) + +AC_CACHE_CHECK([for strcasestr(s1,s2)], + [ac_cv_func_strcasestr], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL], + [if (strcasestr("Some STR1 text", "str1") == NULL) return 1])], + [ac_cv_func_strcasestr=yes], [ac_cv_func_strcasestr=no] + )]) +AS_IF([test x"${ac_cv_func_strcasestr}" = xyes], + [AC_DEFINE([HAVE_STRCASESTR], 1, [defined if standard library has, and C standard allows, the strcasestr(s1,s2) method])], + [AS_IF([test x"${ac_cv_func_strlwr}" = xyes && test x"${ac_cv_func_strstr}" = xyes], + [AC_MSG_WARN([Optional C library routine strcasestr not found; a simple wrapper will be built in])] + [AC_MSG_WARN([Required C library routine strcasestr not found; try adding _GNU_SOURCE])] + )]) + +AC_CACHE_CHECK([for strptime(s1,s2,tm)], + [ac_cv_func_strptime], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +], + [struct tm tm; +char *date = "12/30/1999"; +char *p = strptime(date, "%m/%d/%Y", &tm); +if (p == NULL || *p != '\0') return 1])], + [ac_cv_func_strptime=yes], [ac_cv_func_strptime=no] + )]) +AS_IF([test x"${ac_cv_func_strptime}" = xyes], + [AC_DEFINE([HAVE_STRPTIME], 1, [defined if standard library has, and C standard allows, the strptime(s1,s2,tm) method])], + [AC_MSG_WARN([Optional C library routine strptime not found; try adding _GNU_SOURCE; a fallback implementation will be built in])] + ) +dnl Note: per Linux headers, this may need __USE_XOPEN (features.h) +dnl which is enabled by _XOPEN_SOURCE via _GNU_SOURCE on the platform. +AM_CONDITIONAL([HAVE_STRPTIME], [test x"${ac_cv_func_strptime}" = "xyes"]) + +AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC,ts)], + [ac_cv_func_clock_gettime], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif +], + [struct timespec monoclock_ts; +int got_monoclock = clock_gettime(CLOCK_MONOTONIC, &monoclock_ts); +if (monoclock_ts.tv_sec < 0 || monoclock_ts.tv_nsec < 0) return 1])], + [ac_cv_func_clock_gettime=yes], [ac_cv_func_clock_gettime=no] + )]) +AS_IF([test x"${ac_cv_func_clock_gettime}" = xyes], + [AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [defined if standard library has, and C standard allows, the clock_gettime(clkid,ts) method]) + AC_DEFINE([HAVE_CLOCK_MONOTONIC], 1, [defined if standard library has, and C standard allows, the CLOCK_MONOTONIC macro or token])], + [AC_MSG_WARN([Optional C library routine clock_gettime not found; will not be used in notifications])] + ) +dnl Currently these two are the same for us; note also fallback support for +dnl older autoconf in SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY_RELOAD way below: +AM_CONDITIONAL([HAVE_CLOCK_GETTIME], [test x"${ac_cv_func_clock_gettime}" = "xyes"]) +AM_CONDITIONAL([HAVE_CLOCK_MONOTONIC], [test x"${ac_cv_func_clock_gettime}" = "xyes"]) + +AC_CACHE_CHECK([for strnlen(s1,s2,tm)], + [ac_cv_func_strnlen], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL +#include +], + [size_t len = strnlen("LongString", 5); +if (len != 5) return 5; + +len = strnlen("LongString", 50); +if (len != 10) return 10 +])], + [ac_cv_func_strnlen=yes], [ac_cv_func_strnlen=no] + )]) +AS_IF([test x"${ac_cv_func_strnlen}" = xyes], + [AC_DEFINE([HAVE_STRNLEN], 1, [defined if standard library has, and C standard allows, the strnlen(s1,s2) method])], + [AC_MSG_WARN([Optional C library routine strnlen not found; a fallback implementation will be built in])] + ) +AM_CONDITIONAL([HAVE_STRNLEN], [test x"${ac_cv_func_strnlen}" = "xyes"]) + AC_CACHE_CHECK([for strdup(s)], [ac_cv_func_strdup], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM([$CODE_STRINGINCL], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([$CODE_STRINGINCL +#include +], [[int res = 0; char *t = "Test"; char *s = strdup(t); @@ -299,13 +1040,59 @@ AS_IF([test x"${ac_cv_func_strdup}" = xyes], [AC_MSG_WARN([Required C library routine strdup not found; try adding -D_POSIX_C_SOURCE=200112L])] ) +AC_CHECK_HEADER([sys/select.h], + [AC_DEFINE([HAVE_SYS_SELECT_H], [1], + [Define to 1 if you have .])]) + +AC_CACHE_CHECK([for suseconds_t], + [ac_cv_type_suseconds_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include +#if HAVE_SYS_SELECT_H +# include +#endif + ]], + [[suseconds_t us = 1000000; + signed long int l = 1000000; + if (l != (signed long int)us) return 1; + + l = -1; us = -1; + if (l != (signed long int)us) return 1 +/* autoconf adds ";return 0;" */ +/* we hope the code above fails if type is not defined or range is not sufficient */ +]])], + [ac_cv_type_suseconds_t=yes], [ac_cv_type_suseconds_t=no] + )]) +AS_IF([test x"${ac_cv_type_suseconds_t}" = xyes], + [AC_DEFINE([HAVE_SUSECONDS_T], 1, [defined if standard library has, and C standard allows, the suseconds_t type])], + [AC_MSG_WARN([Required C library type suseconds_t not found; try adding -D_POSIX_C_SOURCE=200112L])] + ) + +AC_CACHE_CHECK([for useconds_t], + [ac_cv_type_useconds_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include + ]], + [[useconds_t us = 1000000; + unsigned long int l = 1000000; + if (l != (unsigned long int)us) return 1 +/* autoconf adds ";return 0;" */ +/* we hope the code above fails if type is not defined or range is not sufficient */ +]])], + [ac_cv_type_useconds_t=yes], [ac_cv_type_useconds_t=no] + )]) +AS_IF([test x"${ac_cv_type_useconds_t}" = xyes], + [AC_DEFINE([HAVE_USECONDS_T], 1, [defined if standard library has, and C standard allows, the useconds_t type])], + [AC_MSG_WARN([Required C library type useconds_t not found; try adding -D_POSIX_C_SOURCE=200112L])] + ) + AC_CACHE_CHECK([for usleep(us)], [ac_cv_func_usleep], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[useconds_t us = 1000; -if (usleep(us) != 0) return 1 /* per doc, no erros are returned actually*/ +if (usleep(us) != 0) return 1 /* per doc, no errors are returned actually */ /* autoconf adds ";return 0;" */ ]])], [ac_cv_func_usleep=yes], [ac_cv_func_usleep=no] @@ -329,13 +1116,34 @@ AC_MSG_CHECKING([whether ln -sr works]) dnl We need to relative-symlink some files. Or hardlink. Or copy... LN_S_R="cp -pR" if test "$as_ln_s" = "ln -s" ; then - LN_S_R="ln" + _abs_srcdir="`cd "${srcdir}" && pwd`" || _abs_srcdir="" + _abs_builddir="`pwd`" + dnl AC_MSG_NOTICE([srcdir='${srcdir}' _abs_srcdir='${_abs_srcdir}' _abs_builddir='${_abs_builddir}']) + if test x"${_abs_srcdir}" = x"${_abs_builddir}" ; then + LN_S_R="ln" + else + _fs_srcdir="`df "${_abs_srcdir}" | tail -1 | awk '{print $1}'`" || fs_srcdir="XXXs" + _fs_builddir="`df "${_abs_builddir}" | tail -1 | awk '{print $1}'`" || fs_builddir="XXXb" + dnl AC_MSG_NOTICE([_fs_srcdir='${_fs_srcdir}' _fs_builddir='${_fs_builddir}']) + if test x"${_fs_srcdir}" = x"${_fs_builddir}" ; then + LN_S_R="ln" + else + dnl Source and build areas are in different filesystems, + dnl can not hardlink - keep copying approach in place + AC_MSG_NOTICE([Source and build areas are in different filesystems, or we could not detect this for sure - avoiding hardlinks]) + fi + unset _fs_srcdir _fs_builddir + fi + unset _abs_srcdir _abs_builddir + + dnl Explore GNU ln (or compatible) with relative symlink support DIR1="$(mktemp -d "dir1.XXXXXXX")" && \ DIR2="$(mktemp -d "dir2.XXXXXXX")" && \ touch "${DIR1}/a" && \ $as_ln_s -r "${DIR1}/a" "${DIR2}/b" && \ ls -la "${DIR2}/b" | grep '\.\./' > /dev/null && \ LN_S_R="$as_ln_s -r" + rm -rf "${DIR1}" "${DIR2}" fi AC_SUBST([LN_S_R], [${LN_S_R}]) @@ -415,16 +1223,6 @@ dnl Solaris compatibility - check for -lnsl and -lsocket AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(connect, socket) -dnl All current systems provide time.h; it need not be checked for. -dnl Not all systems provide sys/time.h, but those that do, all allow -dnl you to include it and time.h simultaneously. -dnl NUT codebase provides the include/timehead.h to wrap these nuances. -AC_CHECK_HEADERS_ONCE([sys/time.h time.h sys/types.h sys/socket.h netdb.h]) -AS_IF([test "$ac_cv_header_sys_time_h" = yes], - [AC_DEFINE([TIME_WITH_SYS_TIME],[1],[Define to 1 if you can safely include both - and . This macro is deemed obsolete by autotools.]) - ], []) - AC_CHECK_HEADERS(sys/modem.h stdarg.h varargs.h, [], [], [AC_INCLUDES_DEFAULT]) @@ -442,17 +1240,158 @@ AC_SEARCH_LIBS([pthread_create], [pthread], dnl ---------------------------------------------------------------------- dnl Check for types and define possible replacements NUT_TYPE_SOCKLEN_T +NUT_CHECK_SOCKETLIB NUT_FUNC_GETNAMEINFO_ARGTYPES +AC_CACHE_CHECK([for inet_ntop() with IPv4 and IPv6 support], + [ac_cv_func_inet_ntop], + [AC_LANG_PUSH([C]) + dnl e.g. add "-lws2_32" for mingw builds + dnl the NETLIBS are set by NUT_CHECK_SOCKETLIB above + SAVED_LIBS="$LIBS" + LIBS="$LIBS $NETLIBS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#if HAVE_WINDOWS_H +# undef inline +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_WS2TCPIP_H +# include +# endif +#else +# include +#endif +#include +]], + [[/* const char* inet_ntop(int af, const void* src, char* dst, size_t cnt); */ +char buf[128]; +printf("%s", inet_ntop(AF_INET, "1.2.3.4", buf, 10)); +printf("%s", inet_ntop(AF_INET6, "::1", buf, 10)) +/* autoconf adds ";return 0;" */ +]])], + [ac_cv_func_inet_ntop=yes], [ac_cv_func_inet_ntop=no] + ) + AC_LANG_POP([C]) + LIBS="$SAVED_LIBS" +]) +AS_IF([test x"${ac_cv_func_inet_ntop}" = xyes], + [AC_DEFINE([HAVE_INET_NTOP], 1, [defined if system has the inet_ntop() method])], + [AC_MSG_WARN([Required C library routine inet_ntop() not found]) + AS_CASE([${target_os}], + [*mingw*], [AC_MSG_WARN([Windows antivirus might block this test])] + ) + ] + ) + +AC_CACHE_CHECK([for inet_pton() with IPv4 and IPv6 support], + [ac_cv_func_inet_pton], + [AC_LANG_PUSH([C]) + dnl e.g. add "-lws2_32" for mingw builds + dnl the NETLIBS are set by NUT_CHECK_SOCKETLIB above + SAVED_LIBS="$LIBS" + LIBS="$LIBS $NETLIBS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#if HAVE_WINDOWS_H +# undef inline +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_WS2TCPIP_H +# include +# endif +#else +# include +#endif +#include +]], + [[/* int inet_pton(int af, const char *src, char *dst); */ +struct in_addr ipv4; +struct in6_addr ipv6; +printf("%i ", inet_pton(AF_INET, "1.2.3.4", &ipv4)); +printf("%i ", inet_pton(AF_INET6, "::1", &ipv6)) +/* autoconf adds ";return 0;" */ +]])], + [ac_cv_func_inet_pton=yes], [ac_cv_func_inet_pton=no] + ) + AC_LANG_POP([C]) + LIBS="$SAVED_LIBS" +]) +AS_IF([test x"${ac_cv_func_inet_pton}" = xyes], + [AC_DEFINE([HAVE_INET_PTON], 1, [defined if system has the inet_pton() method])], + [AC_MSG_WARN([Required C library routine inet_pton() not found]) + AS_CASE([${target_os}], + [*mingw*], [AC_MSG_WARN([Windows antivirus might block this test])] + ) + ] + ) + +AC_CACHE_CHECK([for struct pollfd], + [ac_cv_struct_pollfd], + [AC_LANG_PUSH([C]) + dnl e.g. add "-lws2_32" for mingw builds + dnl the NETLIBS are set by NUT_CHECK_SOCKETLIB above + SAVED_LIBS="$LIBS" + LIBS="$LIBS $NETLIBS" + AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#if HAVE_WINDOWS_H +# undef inline +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_WS2TCPIP_H +# include +# endif +#else +# include +#endif +#include +]], + [[ +struct pollfd pfd; +pfd.fd = 0; +/* autoconf adds ";return 0;" */ +]])], + [ac_cv_struct_pollfd=yes], [ac_cv_struct_pollfd=no] + ) + AC_LANG_POP([C]) + LIBS="$SAVED_LIBS" +]) +AS_IF([test x"${ac_cv_struct_pollfd}" = xyes], + [AC_DEFINE([HAVE_STRUCT_POLLFD], 1, [defined if system has the struct pollfd type])], + [AC_MSG_WARN([Required C library type struct pollfd not found]) + AS_CASE([${target_os}], + [*mingw*], [AC_MSG_WARN([Windows antivirus might block this test])] + ) + ] + ) + dnl ---------------------------------------------------------------------- dnl Check for python binary program names per language version dnl to embed into scripts and Make rules -NUT_CHECK_PYTHON -NUT_CHECK_PYTHON2 -NUT_CHECK_PYTHON3 +NUT_CHECK_PYTHON_DEFAULT dnl ---------------------------------------------------------------------- dnl check for --with-drivers=all (or --with-drivers=name[,name...]) flag +dnl Note: a few drivers are NUT software constructs (NUTSW_DRIVERLIST) +dnl e.g. dummy-ups, clone and clone-outlet; they do not have a separate +dnl toggle or dependency at the moment. Earlier NUT releases before 2.8.0 +dnl grouped them with serial drivers, which were enabled by default. dnl Autoconf versions before 2.62 do not allow consecutive quadrigraphs dnl (square brackets), so the help string depends on the version used @@ -479,6 +1418,14 @@ AC_ARG_WITH(drivers, if test -z "${with_ipmi}"; then with_ipmi="yes"; fi dnl Platform-dependent snowflakes that are required or auto: + if test -z "${with_gpio}"; then + dnl NOTE: Currently we only support a Linux libgpiod + dnl backend for GPIO; eventually there could be more. + case ${target_os} in + linux*) with_gpio="auto";; dnl # TODO: Detect 2018+ distros? + *) with_gpio="auto";; + esac + fi if test -z "${with_linux_i2c}"; then case ${target_os} in linux*) with_linux_i2c="yes";; @@ -504,6 +1451,7 @@ AC_ARG_WITH(drivers, if test -z "${with_powerman}"; then with_powerman="${withval}"; fi if test -z "${with_modbus}"; then with_modbus="${withval}"; fi if test -z "${with_ipmi}"; then with_ipmi="${withval}"; fi + if test -z "${with_gpio}"; then with_gpio="${withval}"; fi if test -z "${with_linux_i2c}"; then with_linux_i2c="${withval}"; fi if test -z "${with_macosx_ups}"; then with_macosx_ups="${withval}"; fi AC_MSG_RESULT(${DRIVER_BUILD_LIST}) @@ -515,11 +1463,11 @@ AC_ARG_WITH(drivers, AS_IF([test -n "$DRIVER_BUILD_LIST"], [dnl DRVLIST is occasionally synced with drivers/Makefile.am dnl NOTE: Currently "USB_DRIVERLIST" is not used standalone: - DRVLIST_NAMES=" + DRVLIST_NAMES="NUTSW_DRIVERLIST SERIAL_DRIVERLIST USB_LIBUSB_DRIVERLIST SERIAL_USB_DRIVERLIST SNMP_DRIVERLIST NEONXML_DRIVERLIST MACOSX_DRIVERLIST MODBUS_DRIVERLIST LINUX_I2C_DRIVERLIST - POWERMAN_DRIVERLIST IPMI_DRIVERLIST" + POWERMAN_DRIVERLIST IPMI_DRIVERLIST GPIO_DRIVERLIST" get_drvlist() ( dnl Note escaped brackets - "against" m4 parser @@ -530,7 +1478,7 @@ AC_ARG_WITH(drivers, SPACE="`printf "$LB"' \t'"$RB"`" SPACES="${SPACE}*" sed -e "s/${SPACES}""$LB"'+'"$RB"'*='"${SPACES}/=/" \ - -e "s/^${SPACES}//" < drivers/Makefile.am \ + -e "s/^${SPACES}//" < "$srcdir/drivers/Makefile.am" \ | { C=false; V=false while read LINE ; do @@ -571,6 +1519,9 @@ AC_ARG_WITH(drivers, DRV_HITS="$DRV_HITS $DRVLIST_NAME" AS_CASE(["$DRVLIST_NAME"], + [NUTSW_DRIVERLIST], [ + AC_MSG_NOTICE([Building NUT-Software-only driver "$DRV"]) + ], [SERIAL_DRIVERLIST], [ AS_IF([test -z "${with_serial}"], [AC_MSG_NOTICE([Requiring --with-serial=yes for driver "$DRV"]) @@ -628,6 +1579,11 @@ AC_ARG_WITH(drivers, [AC_MSG_NOTICE([Requiring --with-ipmi=yes for driver "$DRV"]) with_ipmi=yes] )], + [GPIO_DRIVERLIST], [ + AS_IF([test -z "${with_gpio}"], + [AC_MSG_NOTICE([Requiring --with-gpio=yes for driver "$DRV"]) + with_gpio=yes] + )], [AC_MSG_WARN([Unhandled DRVLIST_NAME=$DRVLIST_NAME])] ) @@ -635,9 +1591,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 ]) @@ -661,7 +1617,7 @@ dnl check for --with-all (or --without-all, or --with-all=auto) flag AC_MSG_CHECKING(for --with-all) AC_ARG_WITH(all, - AS_HELP_STRING([--with-all], [enable serial, usb, snmp, neon, libxml2, ipmi, powerman, modbus, cgi, dev, avahi, linux_i2c, dmf, snmp_dmf]), + AS_HELP_STRING([--with-all], [enable serial, usb, snmp, neon, libxml2, ipmi, powerman, modbus, gpio (currently on Linux released after ~2018), linux_i2c (on Linux), macosx-ups (on MacOS), cgi, dev, avahi, nut-scanner, nutconf, pynut, dmf, snmp_dmf]), dnl ### Not forced: lua, dmf_lua, snmp_dmf_lua [ if test -n "${withval}"; then @@ -672,18 +1628,55 @@ dnl ### Not forced: lua, dmf_lua, snmp_dmf_lua if test -z "${with_serial}"; then with_serial="${withval}"; fi if test -z "${with_usb}"; then with_usb="${withval}"; fi if test -z "${with_snmp}"; then with_snmp="${withval}"; fi - if test -z "${with_snmp_dmf}"; then with_snmp_dmf="${withval}"; fi + if test -z "${with_snmp_dmf}"; then + if test -z "${with_dmf}"; then + with_snmp_dmf="${withval}" + else + dnl Expecting "yes", "no" or "auto": + case "${with_snmp}${with_dmf}" in + *no*) with_snmp_dmf="no" ;; + *yes*) with_snmp_dmf="yes" ;; + *) with_snmp_dmf="auto" ;; + esac + fi + else + dnl Align DMF functionality to explicit request + dnl for a build of snmp-ups-dmf driver + if test -z "${with_dmf}" -a x"${with_snmp_dmf}" != xno; then + with_dmf="${with_snmp_dmf}" + fi + fi dnl NOTE: The LUA integration is experimental, so not forced as part of --with-all +dnl TODO: Integrate interactions for "with_dmf", "with_snmp", "with_snmp_dmf" +dnl settings, if something was asked explicitly beside e.g. "--with-all=no". dnl if test -z "${with_lua}"; then with_lua="${withval}"; fi dnl if test -z "${with_dmf_lua}"; then with_dmf_lua="${withval}"; fi dnl if test -z "${with_snmp_dmf_lua}"; then with_snmp_dmf_lua="${withval}"; fi if test -z "${with_neon}"; then with_neon="${withval}"; fi - if test -z "${with_libxml2}"; then with_libxml2="${withval}"; fi + if test -z "${with_libxml2}"; then + if test -z "${with_dmf}"; then + with_libxml2="${withval}" + else + dnl Align to explicit request for DMF functionality + dnl (or lack thereof, currently the only consumer) + with_libxml2="${with_dmf}" + fi + fi if test -z "${with_powerman}"; then with_powerman="${withval}"; fi if test -z "${with_modbus}"; then with_modbus="${withval}"; fi if test -z "${with_ipmi}"; then with_ipmi="${withval}"; fi + if test -z "${with_dmf}"; then with_dmf="${withval}"; fi dnl Platform-dependent snowflakes that are required or auto: + if test -z "${with_gpio}"; then + dnl NOTE: Currently we only support a Linux libgpiod + dnl backend for GPIO; eventually there could be more. + with_gpio="${withval}" + case ${target_os} in + linux*) ;; + *) test x"${withval}" = xno || with_gpio="auto" ;; + esac + fi if test -z "${with_linux_i2c}"; then with_linux_i2c="${withval}" case ${target_os} in @@ -702,8 +1695,24 @@ dnl if test -z "${with_snmp_dmf_lua}"; then with_snmp_dmf_lua="${withval}"; fi if test -z "${with_cgi}"; then with_cgi="${withval}"; fi if test -z "${with_dev}"; then with_dev="${withval}"; fi if test -z "${with_avahi}"; then with_avahi="${withval}"; fi + if test -z "${with_nut_scanner}"; then with_nut_scanner="${withval}"; fi + if test -z "${with_nutconf}"; then with_nutconf="${withval}"; fi + + if test -n "${PYTHON}${PYTHON2}${PYTHON3}" -a x"${withval}" = xyes \ + || test x"${withval}" != xyes \ + ; then + dnl We expect Python to be available (augeas etc), + dnl so if it is present - try to provide the PyNUT + dnl binding module: + if test -z "${with_pynut}"; then with_pynut="${withval}"; fi + + dnl However, do not let "--with-all" break builds + dnl on servers that lack some GUI modules in their + dnl Python installations; "auto" is defaulted below. + dnl # if test -z "${with_nut_monitor}"; then with_nut_monitor="${withval}"; fi + fi - AC_MSG_RESULT("${withval}") + AC_MSG_RESULT([${withval}]) else AC_MSG_RESULT(not given) fi @@ -717,14 +1726,22 @@ dnl they are listed near the top by "./configure --help"; however, dnl note that options with further investigation methods are listed dnl a bit below to be grouped with their additional with/enable help. -dnl While the contribution is being absorbed into the NUT codebase, -dnl we don't want the CI farm to build it by default and so block NUT -dnl packaging. This toggle may be removed or switched to "yes" later. -dnl Until then, also this is not activated by --with-all flag. -NUT_ARG_WITH([nutconf], [build and install the nutconf tool (experimental, has compiler/coverage warnings)], [no]) +NUT_ARG_WITH([nutconf], [build and install the nutconf tool (experimental, has compiler/coverage warnings)], [auto]) NUT_ARG_WITH([dev], [build and install the development files], [no]) +dnl Activate WITH_UNMAPPED_DATA_POINTS for troubleshooting and evolution? +dnl Note that production drivers must conform to docs/nut-names.txt +NUT_ARG_WITH([unmapped-data-points], + [represent USB-HID and SNMP data points discovered during subdriver generation but not mapped to nut-names yet], + [no]) +AS_IF([test x"${nut_with_unmapped_data_points}" = xyes], + [AC_DEFINE(WITH_UNMAPPED_DATA_POINTS, 1, + [Define to enable data points discovered during subdriver generation but not mapped to nut-names yet])], + [AC_DEFINE(WITH_UNMAPPED_DATA_POINTS, 0, + [Define to enable data points discovered during subdriver generation but not mapped to nut-names yet])] +) + dnl The NUT legacy option was --with-doc; however to simplify configuration dnl in some common packaging frameworks, we also allow --with-docs as dnl a second-class citizen (if both are set, the old option name wins). @@ -737,10 +1754,13 @@ 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]) +NUT_ARG_WITH([debuginfo], + [enable compiler options for debug-friendly builds of all NUT binaries ("no" by default; "auto" means "yes unless CFLAGS say otherwise")], + [no]) dnl To help find warning/error details in a wall of text, see --enable-Wcolor handled above dnl ---------------------------------------------------------------------- @@ -752,6 +1772,7 @@ dnl These checks are performed unconditionally, even if the corresponding dnl --with-* options are not given. This is because we cannot predict dnl what will be in the --with-drivers argument. +NUT_CHECK_LIBREGEX NUT_ARG_WITH([usb], [build and install USB drivers, optionally require build with specified version of libusb library and API: (auto|libusb-0.1|libusb-1.0)], [auto]) nut_usb_lib="" NUT_CHECK_LIBUSB @@ -768,9 +1789,38 @@ NUT_CHECK_LIBPOWERMAN NUT_ARG_WITH([modbus], [build and install modbus drivers], [auto]) NUT_CHECK_LIBMODBUS +NUT_ARG_WITH([gpio], [build and install GPIO driver], [auto]) +NUT_CHECK_LIBGPIO + NUT_ARG_WITH([avahi], [build and install Avahi support], [auto]) NUT_CHECK_LIBAVAHI +NUT_ARG_WITH([ipmi], [build and install IPMI PSU driver], [auto]) +NUT_ARG_WITH([freeipmi], [enable IPMI support using FreeIPMI], [auto]) +dnl NUT_ARG_WITH([openipmi], [enable IPMI support using OpenIPMI], [auto]) + +dnl Platform-dependent drivers, currently their detection code is directly +dnl spelled out in configure.ac +NUT_ARG_WITH([macosx_ups], [build and install Mac OS X Power Sources meta-driver], [auto]) +NUT_ARG_WITH([linux_i2c], [build and install i2c drivers], [auto]) + +dnl A Python GUI client application for the sysadmin desktop +dnl (not necessarily on the NUT server itself): +NUT_ARG_WITH([nut_monitor], [install the NUT-Monitor GUI files], [auto]) +NUT_ARG_WITH([pynut], [install the PyNUT module files (yes, no, app, auto)], [auto]) +dnl Note: we did NUT_CHECK_PYTHON2 NUT_CHECK_PYTHON3 etc above, +dnl and if at all possible, we generate the files from .in templates +dnl anyway by running this configure script. The question is about +dnl installing these features or not. +dnl Note: more for tests than other reasons, there is also an option +dnl value to "force" the installation. + +dnl The gettext "msgfmt" tool (or equivalent) can be used to maintain +dnl human-language text translations. Currently this is used specifically +dnl in the Python NUT-Monitor app sources (*.po => *.mo conversions). +AC_PATH_PROGS([MSGFMT], [msgfmt], [none]) +AM_CONDITIONAL([HAVE_MSGFMT], [test "x${MSGFMT}" != "xnone"]) + dnl ---------------------------------------------------------------------- dnl checks related to --with-serial @@ -840,6 +1890,13 @@ getspeed(&baudrate); CFLAGS="${CFLAGS_SAVED_SERIAL}" AC_MSG_CHECKING([whether we can build serial drivers]) + AS_CASE(["${target_os}"], + [*mingw*], [ + AS_IF([test "${nut_have_serial_types}" != yes], + [AC_MSG_WARN([not with system includes, but can try with our fallback for the target platform]) + nut_have_serial_types=yes] + ) + ]) AS_IF([test "${nut_have_serial_types}" = yes], [AS_IF([test "${nut_with_serial}" = "auto"],[nut_with_serial="yes"])], [AS_IF([test "${nut_with_serial}" = "auto"],[nut_with_serial="no"]) @@ -931,10 +1988,6 @@ dnl if both are available (since it is the only one supported ATM!!) nut_ipmi_lib="" -NUT_ARG_WITH([ipmi], [build and install IPMI PSU driver], [auto]) -NUT_ARG_WITH([freeipmi], [enable IPMI support using FreeIPMI], [auto]) -dnl NUT_ARG_WITH([openipmi], [enable IPMI support using OpenIPMI], [auto]) - dnl ${nut_with_ipmi}: any value except "yes" or "no" is treated as "auto". if test "${nut_with_ipmi}" != "no"; then dnl check if FreeIPMI (and maybe later OpenIPMI) was explicitly requested @@ -991,6 +2044,20 @@ dnl and AM_CONDITIONALs (see below)... AM_CONDITIONAL(WITH_FREEIPMI, test "${nut_with_freeipmi}" = "yes") dnl AM_CONDITIONAL(WITH_OPENIPMI, test "${nut_with_openipmi}" = "yes") +dnl ---------------------------------------------------------------------- +dnl Check for with-gpio + +if test "${nut_with_gpio}" = "yes" -a "${nut_have_gpio}" != "yes"; then + AC_MSG_ERROR([No supported GPIO library was found, required for GPIO driver]) +fi + +dnl ${nut_with_gpio}: any value except "yes" or "no" is treated as "auto". +if test "${nut_with_gpio}" != "no"; then + nut_with_gpio="${nut_have_gpio}" +fi + +NUT_REPORT_FEATURE([build GPIO driver], [${nut_with_gpio}], [${nut_gpio_lib}], + [WITH_GPIO], [Define to enable GPIO support]) dnl ---------------------------------------------------------------------- dnl The Mac OS X meta-driver looks at IOKit Power Sources keys managed by @@ -998,7 +2065,6 @@ dnl the internal USB UPS driver. dnl dnl FIXME: be slightly more clever here: -NUT_ARG_WITH([macosx_ups], [build and install Mac OS X Power Sources meta-driver], [auto]) if test "${nut_with_macosx_ups}" != no; then if test -d /System/Library/Frameworks/IOKit.framework/ ; then nut_with_macosx_ups="yes" @@ -1017,7 +2083,7 @@ NUT_REPORT_FEATURE([build Mac OS X meta-driver], dnl ---------------------------------------------------------------------- dnl checks related to --with_linux_i2c dnl Check for i2c header on Linux, used for ASEM UPS driver -NUT_ARG_WITH([linux_i2c], [build and install i2c drivers], [auto]) +LIBI2C_LIBS="" if test "${nut_with_linux_i2c}" != no; then case ${target_os} in linux* ) @@ -1050,6 +2116,8 @@ if test "${nut_with_linux_i2c}" != no; then dnl "compromised" by lack of respective binary library, so dnl even if we have the right headers, ultimate link fails. dnl Note: here we keep the verdict from above, or make it worse. + LIBS_SAVED="$LIBS" + LIBS="" AC_SEARCH_LIBS([i2c_smbus_read_byte, i2c_smbus_access, i2c_smbus_read_byte_data, i2c_smbus_write_byte_datai2c_smbus_write_byte_data, i2c_smbus_read_word_data, i2c_smbus_write_word_data, i2c_smbus_read_block_data], [i2c], [nut_with_linux_i2c="yes"], @@ -1057,6 +2125,8 @@ if test "${nut_with_linux_i2c}" != no; then [AC_MSG_ERROR(i2c was required but can not be fulfilled for this build)], [nut_with_linux_i2c="no"]) ]) + LIBI2C_LIBS="$LIBS" + LIBS="$LIBS_SAVED" ;; * ) if test "${nut_with_linux_i2c}" = yes; then @@ -1150,22 +2220,36 @@ NUT_REPORT_FEATURE([enable libwrap (tcp-wrappers) support], [${nut_with_wrap}], dnl ---------------------------------------------------------------------- -dnl Check for --with-libltdl +dnl Check for --with-libltdl and --with-nut-scanner +dnl Note: libltdl is primarily used by nut-scanner now; however some +dnl side projects and forks of NUT have more creative uses for it +dnl and might eventually land in NUT codebase proper. NUT_ARG_WITH([libltdl], [enable libltdl (Libtool dlopen abstraction) support], [auto]) +dnl Note: default could be overridden above by --with-all handling. +dnl While nut-scanner decides at run-time if it would use third-party shared +dnl library files (bundled along or not, if available for the platform), its +dnl binary must be configured now and built against their headers at least. +NUT_ARG_WITH([nut-scanner], [build and install nut-scanner tool (requires libltdl; optionally libusb, libneon, libsnmp)], [auto]) + dnl ${nut_with_libltdl}: any value except "yes" or "no" is treated as "auto". -if test "${nut_with_libltdl}" != "no"; then - dnl check for libltdl compiler flags - NUT_CHECK_LIBLTDL +if test x"${nut_with_libltdl}" != x"no"; then + if test x"${nut_with_nut_scanner}" = x"yes"; then + dnl Require libltdl to be present (or fail the configure script) + nut_with_libltdl="yes" + fi + + dnl check for libltdl compiler flags + NUT_CHECK_LIBLTDL fi -if test "${nut_with_libltdl}" = "yes" -a "${nut_have_libltdl}" != "yes"; then - AC_MSG_ERROR([libltdl not found]) +if test x"${nut_with_libltdl}" = x"yes" -a x"${nut_have_libltdl}" != x"yes"; then + AC_MSG_ERROR([libltdl not found]) fi -if test "${nut_with_libltdl}" != "no"; then - nut_with_libltdl="${nut_have_libltdl}" +if test x"${nut_with_libltdl}" != x"no"; then + nut_with_libltdl="${nut_have_libltdl}" fi NUT_REPORT_FEATURE([enable libltdl (Libtool dlopen abstraction) support], [${nut_with_libltdl}], [], @@ -1173,7 +2257,16 @@ NUT_REPORT_FEATURE([enable libltdl (Libtool dlopen abstraction) support], [${nut dnl Explicitly report if we are building nut-scanner or not dnl since it requires libltdl -NUT_REPORT([build nut-scanner], [${nut_with_libltdl}]) +if test x"${nut_with_libltdl}" = x"no" && test x"${nut_with_nut_scanner}" = x"yes"; then + AC_MSG_ERROR([libltdl support was disabled or not found, but --with-nut-scanner was requested and requires it]) +fi + +if test x"${nut_with_nut_scanner}" = x"auto"; then + nut_with_nut_scanner="${nut_with_libltdl}" +fi + +NUT_REPORT_FEATURE([build nut-scanner], [${nut_with_nut_scanner}], [], + [WITH_NUT_SCANNER], [Define to enable nut-scanner tool support]) dnl ---------------------------------------------------------------------- dnl checks related to --with-cgi @@ -1198,6 +2291,268 @@ NUT_REPORT_FEATURE([build CGI programs], [${nut_with_cgi}], [], [WITH_CGI], [Define to enable CGI (HTTP) support]) +dnl ---------------------------------------------------------------------- +dnl checks related to --with-pynut and --with-nut_monitor + +dnl ${nut_with_nut_monitor}: TODO: arg values to request python 2 gtk2, +dnl python 3 qt5, or both +AC_MSG_CHECKING([if we can and should install NUT-Monitor desktop application]) +nut_with_nut_monitor_py2gtk2="" +nut_with_nut_monitor_py3qt5="" +nut_with_nut_monitor_desktop="" +dnl TODO: Add a way to define this path? will have app/ maybe module/ inside... +nut_with_nut_monitor_dir="${datarootdir}/nut-monitor" +PYTHON_FAILED_TEST_DETAILS="" +if test x"${nut_with_nut_monitor}" != xno ; then + dnl While we might just install for "yes" request, in hopes user would + dnl get their python ecosystem in place later, we need some criteria to + dnl avoid installing it always :) Also, need to substitute the shebang. + if test -z "${PYTHON}${PYTHON2}${PYTHON3}" ; then + case "${nut_with_nut_monitor}" in + "auto") nut_with_nut_monitor="no" + PYTHON_FAILED_TEST_DETAILS="No python 2/3 interpreter was found" + ;; + "yes") AC_MSG_ERROR([No python 2/3 interpreter was found, required for NUT-Monitor desktop application]) + ;; + esac + fi +fi + +if test x"${nut_with_nut_monitor}" != xno ; then + dnl Note: no double-quoting for use, the command string may be multi-token + dnl HACK NOTE: Here we redirect outputs to "&5" which is autoconf stream + dnl for "config.log" details since... forever? Still, hardcoded numbers... + PYTHON2_TEST_MODULES="re,glob,codecs,gtk,gtk.glade,gobject,ConfigParser" + PYTHON3_TEST_MODULES="re,glob,codecs,PyQt5.uic,configparser" + if test -n "${PYTHON2}" \ + && (command -v ${PYTHON2} || which ${PYTHON2}) >/dev/null 2>/dev/null \ + ; then + if ${PYTHON2} -c "import ${PYTHON2_TEST_MODULES}" 1>&5 2>&5 \ + ; then + nut_with_nut_monitor_py2gtk2="yes" + else + PYTHON_FAILED_TEST_DETAILS="Missing some or all of these Python2 modules: '${PYTHON2_TEST_MODULES}'" + fi + fi + + if test -n "${PYTHON3}" \ + && (command -v ${PYTHON3} || which ${PYTHON3}) >/dev/null 2>/dev/null \ + ; then + if ${PYTHON3} -c "import ${PYTHON3_TEST_MODULES}" 1>&5 2>&5 \ + ; then + nut_with_nut_monitor_py3qt5="yes" + else + PYTHON_FAILED_TEST_DETAILS="Missing some or all of these Python3 modules: '${PYTHON3_TEST_MODULES}'" + fi + fi + + dnl Fall back to default interpreter + if test -z "${nut_with_nut_monitor_py2gtk2}${nut_with_nut_monitor_py3qt5}" \ + && test -n "${PYTHON}" \ + && (command -v ${PYTHON} || which ${PYTHON2}) >/dev/null 2>/dev/null \ + ; then + if ${PYTHON} -c "import ${PYTHON3_TEST_MODULES}" 1>&5 2>&5 \ + ; then + nut_with_nut_monitor_py3qt5="yes" + else + PYTHON_FAILED_TEST_DETAILS="Missing some or all of these Python3 modules: '${PYTHON3_TEST_MODULES}'" + fi + + if ${PYTHON} -c "import ${PYTHON2_TEST_MODULES}" 1>&5 2>&5 \ + ; then + nut_with_nut_monitor_py2gtk2="yes" + PYTHON_FAILED_TEST_DETAILS="" + else + if test -n "${PYTHON_FAILED_TEST_DETAILS}" ; then + PYTHON_FAILED_TEST_DETAILS="${PYTHON_FAILED_TEST_DETAILS} and some or all of these Python2 modules: '${PYTHON2_TEST_MODULES}'" + else + PYTHON_FAILED_TEST_DETAILS="Missing some or all of these Python2 modules: '${PYTHON2_TEST_MODULES}'" + fi + fi + fi + + dnl Can we satisfy any NUT-Monitor installation request? + if test -n "${nut_with_nut_monitor_py2gtk2}${nut_with_nut_monitor_py3qt5}" ; then + case "${nut_with_nut_monitor}" in + "auto") nut_with_nut_monitor="yes" ;; + esac + else + case "${nut_with_nut_monitor}" in + "auto") nut_with_nut_monitor="no" ;; + "yes") + AC_MSG_ERROR([No python 2/3 interpreter with needed modules was found, as required for NUT-Monitor desktop application: ${PYTHON_FAILED_TEST_DETAILS}]) + ;; + esac + fi +fi +case "${nut_with_nut_monitor}" in + "no") if test -n "${PYTHON_FAILED_TEST_DETAILS}" ; then + AC_MSG_RESULT([${nut_with_nut_monitor}: ${PYTHON_FAILED_TEST_DETAILS}]) + else + AC_MSG_RESULT([${nut_with_nut_monitor}]) + fi + ;; + *) AC_MSG_RESULT([${nut_with_nut_monitor}]) ;; +esac + +if test x"${nut_with_nut_monitor}" != xno ; then + if (command -v desktop-file-install || which desktop-file-install) >/dev/null 2>/dev/null ; then + case "${nut_with_nut_monitor}" in + "auto"|"yes") nut_with_nut_monitor_desktop="desktop-file-install" ;; + esac + else + case "${nut_with_nut_monitor}" in + "yes") AC_MSG_WARN([Current OS does not seem to provide desktop-file-install]) + nut_with_nut_monitor_desktop="install" + ;; + "auto") nut_with_nut_monitor_desktop="install" ;; + esac + fi +fi + +dnl ${nut_with_pynut}: TODO: arg values to request python 2, 3 or both +AC_MSG_CHECKING([if we can and should install PyNUT module]) +nut_with_pynut_py="" +nut_with_pynut_py2="" +nut_with_pynut_py3="" +if test x"${nut_with_pynut}" != xno \ + -a -n "${PYTHON}${PYTHON2}${PYTHON3}" \ +; then + if test -n "${PYTHON2}" \ + && (command -v ${PYTHON2} || which ${PYTHON2}) >/dev/null 2>/dev/null \ + ; then + if ${PYTHON2} -c "import telnetlib" \ + ; then + nut_with_pynut_py2="yes" + fi + fi + + if test -n "${PYTHON3}" \ + && (command -v ${PYTHON3} || which ${PYTHON3}) >/dev/null 2>/dev/null \ + ; then + if ${PYTHON3} -c "import telnetlib" \ + ; then + nut_with_pynut_py3="yes" + fi + fi + + dnl Test same-ness of pythons with sys.version also? + if test -n "${PYTHON}" \ + && (command -v ${PYTHON} || which ${PYTHON}) >/dev/null 2>/dev/null \ + && test "${PYTHON}" != "${PYTHON2}" -a "${PYTHON}" != "${PYTHON3}" \ + ; then + if ${PYTHON} -c "import telnetlib" \ + ; then + nut_with_pynut_py="yes" + fi + fi +fi + +if test -z "${nut_with_pynut_py}${nut_with_pynut_py2}${nut_with_pynut_py3}" ; then + dnl Not all prereqs are available... + case "${nut_with_pynut}" in + "auto"|"app") + if test "${nut_with_nut_monitor}" = yes ; then + AC_MSG_ERROR([Prerequisites for PyNUT not found, can't install as required for NUT-Monitor desktop application]) + else + nut_with_pynut="no" + fi + ;; + "yes") + AC_MSG_ERROR([Prerequisites for PyNUT not found, can't install as required]) + ;; + esac +fi + +if test x"${nut_with_pynut}" != xno ; then + if test -n "${PYTHON_SITE_PACKAGES}${PYTHON2_SITE_PACKAGES}${PYTHON3_SITE_PACKAGES}" \ + ; then + dnl retain "app" if requested by caller + case "${nut_with_pynut}" in + "auto") + nut_with_pynut="yes" + ;; + esac + else + case "${nut_with_pynut}" in + "auto") + if test "${nut_with_nut_monitor}" = yes -o "${nut_with_nut_monitor}" = force ; then + nut_with_pynut="app" + else + nut_with_pynut="no" + fi + ;; + "yes") dnl Note: this would die for --with-nut_monitor=yes but no site location + if test "${nut_with_nut_monitor}" = yes -o "${nut_with_nut_monitor}" = force ; then + nut_with_pynut="app" + else + AC_MSG_ERROR([python interpreter and/or its site-packages location not found, but required for PyNUT]) + fi + ;; + esac + fi +fi +AC_MSG_RESULT([${nut_with_pynut}]) + +dnl Note: do not move up to before pynut processing +if test "${nut_with_nut_monitor}" = force ; then + AC_MSG_NOTICE([overriding nut_with_nut_monitor=yes because caller forced it]) + nut_with_nut_monitor=yes + nut_with_nut_monitor_py2gtk2=yes + nut_with_nut_monitor_py3qt5=yes +fi + +if test "${nut_with_pynut}" = force ; then + AC_MSG_NOTICE([overriding nut_with_pynut=yes because caller forced it]) + if test "${nut_with_nut_monitor}" = yes ; then + nut_with_pynut=app + else + nut_with_pynut=yes + fi +fi + +NUT_REPORT_FEATURE([install NUT-Monitor desktop application], [${nut_with_nut_monitor}], [], + [WITH_NUT_MONITOR], [Define to enable NUT-Monitor desktop application installation]) + +NUT_REPORT_FEATURE([install PyNUT binding module], [${nut_with_pynut}], [], + [WITH_PYNUT], [Define to enable PyNUT module installation]) + +dnl One or both of these may be installed: +AM_CONDITIONAL(WITH_NUT_MONITOR, test "${nut_with_nut_monitor}" = "yes" && test "${nut_with_nut_monitor_py2gtk2}" = "yes" -o "${nut_with_nut_monitor_py3qt5}" = "yes") +AM_CONDITIONAL(WITH_NUT_MONITOR_PY2GTK2, test "${nut_with_nut_monitor_py2gtk2}" = "yes") +AM_CONDITIONAL(WITH_NUT_MONITOR_PY3QT5, test "${nut_with_nut_monitor_py3qt5}" = "yes") +dnl Install PyNUT as a globally usable module, or just as app internals? +AM_CONDITIONAL(WITH_PYNUT_PY, test "${nut_with_pynut_py}" = "yes" -a "${nut_with_pynut}" = yes) +AM_CONDITIONAL(WITH_PYNUT_PY2, test "${nut_with_pynut_py2}" = "yes" -a "${nut_with_pynut}" = yes) +AM_CONDITIONAL(WITH_PYNUT_PY3, test "${nut_with_pynut_py3}" = "yes" -a "${nut_with_pynut}" = yes) +AM_CONDITIONAL(WITH_PYNUT_APP, test "${nut_with_pynut}" = "app") + +AC_SUBST([nut_with_nut_monitor_dir], [${nut_with_nut_monitor_dir}]) +AC_SUBST([nut_with_nut_monitor_py2gtk2], [${nut_with_nut_monitor_py2gtk2}]) +AC_SUBST([nut_with_nut_monitor_py3qt5], [${nut_with_nut_monitor_py3qt5}]) +AC_SUBST([nut_with_nut_monitor_desktop], [${nut_with_nut_monitor_desktop}]) +AC_SUBST([nut_with_nut_monitor], [${nut_with_nut_monitor}]) +AC_SUBST([nut_with_pynut], [${nut_with_pynut}]) +AC_SUBST([nut_with_pynut_py], [${nut_with_pynut_py}]) +AC_SUBST([nut_with_pynut_py2], [${nut_with_pynut_py2}]) +AC_SUBST([nut_with_pynut_py3], [${nut_with_pynut_py3}]) + +AS_IF([test "${nut_with_nut_monitor}" != no -o "${nut_with_pynut}" != no], [ + NUT_REPORT([use default Python interpreter], [${PYTHON}]) + NUT_REPORT([use specific Python2 interpreter], [${PYTHON2}]) + NUT_REPORT([use specific Python3 interpreter], [${PYTHON3}]) + + NUT_REPORT_PATH_INTEGRATIONS([Default Python interpreter site-packages], + [${PYTHON_SITE_PACKAGES}]) + + NUT_REPORT_PATH_INTEGRATIONS([Specific Python2 interpreter site-packages], + [${PYTHON2_SITE_PACKAGES}]) + + NUT_REPORT_PATH_INTEGRATIONS([Specific Python3 interpreter site-packages], + [${PYTHON3_SITE_PACKAGES}]) + dnl # Python site-packages installation path for specific Python3 interpreter +]) + dnl ---------------------------------------------------------------------- dnl checks related to --enable-cppcheck @@ -1207,6 +2562,7 @@ dnl unless desired). NUT_ARG_ENABLE([cppcheck], [Run a cppcheck on the codebase among checks], [no]) NUT_CHECK_CPPCHECK +AC_MSG_CHECKING(whether to run cppcheck among default make check target) case "${nut_enable_cppcheck}" in yes) if test "${nut_have_cppcheck}" = "no" ; then AC_MSG_ERROR([Requested to --enable-cppcheck but did not find a good one]) @@ -1215,15 +2571,64 @@ case "${nut_enable_cppcheck}" in ;; no) WITH_CPPCHECK=no ;; auto) if test "${nut_have_cppcheck}" = "yes" ; then - WITH_CPPCHECK=no + WITH_CPPCHECK=yes else WITH_CPPCHECK=no fi ;; esac +AC_MSG_RESULT([${WITH_CPPCHECK}]) 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 --enable-spellcheck + +NUT_CHECK_ASPELL +NUT_ARG_ENABLE([spellcheck], [Run spellcheck among default checks], [auto]) + +AC_MSG_CHECKING(whether to run spellcheck among default make check target) +case "${nut_enable_spellcheck}" in + yes) if test "${nut_have_aspell}" = "no" ; then + AC_MSG_ERROR([Requested to --enable-spellcheck but did not find a good one]) + fi + WITH_SPELLCHECK=yes + ;; + no) WITH_SPELLCHECK=no ;; + auto) if test "${nut_have_aspell}" = "yes" ; then + WITH_SPELLCHECK=yes + else + WITH_SPELLCHECK=no + fi + ;; +esac +AC_MSG_RESULT([${WITH_SPELLCHECK}]) + +AM_CONDITIONAL(WITH_SPELLCHECK, test "${WITH_SPELLCHECK}" = "yes") + dnl ---------------------------------------------------------------------- dnl checks related to --with-doc @@ -1309,11 +2714,13 @@ dnl to detect if we can build the wanted documentation format and yet dnl not fail if we have no tools to generate it (so add to SKIP list). html-single*) - AC_MSG_CHECKING([if asciidoc version can build ${nut_doc_build_target_base} (minimum required ${ASCIIDOC_MIN_VERSION})]) + AC_MSG_CHECKING([if asciidoc and a2x versions can build ${nut_doc_build_target_base} (minimum required asciidoc-${ASCIIDOC_MIN_VERSION} a2x-${A2X_MIN_VERSION})]) can_build_doc_html_single=no AX_COMPARE_VERSION([${ASCIIDOC_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ - ( cd "$DOCTESTDIR" && ${A2X} --attribute=xhtml11_format --format=xhtml --xsl-file="${abs_srcdir}"/docs/xhtml.xsl --destination-dir=. "${abs_srcdir}"/docs/asciidoc.txt && test -s asciidoc.html ) && can_build_doc_html_single=yes - rm -f "$DOCTESTDIR"/asciidoc*.htm* + AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${A2X_MIN_VERSION}], [ + ( cd "$DOCTESTDIR" && ${A2X} --attribute=xhtml11_format --format=xhtml --xsl-file="${abs_srcdir}"/docs/xhtml.xsl --destination-dir=. "${abs_srcdir}"/docs/asciidoc.txt && test -s asciidoc.html ) && can_build_doc_html_single=yes + rm -f "$DOCTESTDIR"/asciidoc*.htm* + ], []) ], []) if test "${can_build_doc_html_single}" = yes ; then AC_MSG_RESULT(yes) @@ -1330,11 +2737,13 @@ dnl not fail if we have no tools to generate it (so add to SKIP list). ;; html-chunked*) - AC_MSG_CHECKING([if a2x version can build ${nut_doc_build_target_base} (minimum required ${ASCIIDOC_MIN_VERSION})]) + AC_MSG_CHECKING([if asciidoc and a2x versions can build ${nut_doc_build_target_base} (minimum required asciidoc-${ASCIIDOC_MIN_VERSION} a2x-${A2X_MIN_VERSION})]) can_build_doc_html_chunked=no - AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ - ( cd "$DOCTESTDIR" && ${A2X} --attribute=chunked_format --format=chunked --xsl-file="${abs_srcdir}"/docs/chunked.xsl --destination-dir=. "${abs_srcdir}"/docs/FAQ.txt && test -s FAQ.chunked/index.html ) && can_build_doc_html_chunked=yes - rm -rf "${DOCTESTDIR}"/FAQ*.chunked* + AX_COMPARE_VERSION([${ASCIIDOC_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ + AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${A2X_MIN_VERSION}], [ + ( cd "$DOCTESTDIR" && ${A2X} --attribute=chunked_format --format=chunked --xsl-file="${abs_srcdir}"/docs/chunked.xsl --destination-dir=. "${abs_srcdir}"/docs/FAQ.txt && test -s FAQ.chunked/index.html ) && can_build_doc_html_chunked=yes + rm -rf "${DOCTESTDIR}"/FAQ*.chunked* + ], []) ], []) if test "${can_build_doc_html_chunked}" = yes ; then AC_MSG_RESULT(yes) @@ -1523,6 +2932,7 @@ then *-m64*) CC="$CC -m64" ;; esac fi +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) LT_INIT AC_SUBST([LIBTOOL_DEPS]) GCC="$SAVED_GCC" @@ -1544,18 +2954,37 @@ NUT_REPORT_FEATURE([build and install the development files], [${nut_with_dev}], [WITH_DEV], [Define to enable development files support]) dnl ---------------------------------------------------------------------- +dnl checks related to MS Windows support (MingW) + +AC_CHECK_TOOL([WINDMC], [windmc], [none]) +AC_CHECK_TOOL([WINDRES], [windres], [none]) + +if test "x$WINDMC" != "xnone" -a "x$WINDRES" != "xnone" ; then + nut_have_mingw_resgen="yes" +fi + +AM_CONDITIONAL([HAVE_MINGW_RESGEN], [test "${nut_have_mingw_resgen}" = "yes"]) + +dnl Also define a generic automake condition for general Windows compilation: +dnl do we at least have the header file(s) we require for the platform +dnl (more files may be optional e.g. for different generations of networking) +dnl Could just use ..._COND_IF([HAVE_WINDOWS_H],... but it is not present in +dnl some older versions of autotools. (Note autoconf expands in comments too). +AS_IF([test "x$nut_cv_header_windows_h" = xyes], + [AM_CONDITIONAL([HAVE_WINDOWS], [test "${nut_have_mingw_resgen}" = "yes"])], + [AM_CONDITIONAL([HAVE_WINDOWS], [false])] + ) -AM_CONDITIONAL(WITH_NUTCONF, test "${nut_with_nutconf}" = "yes") -NUT_REPORT_FEATURE([build and install the nutconf tool (experimental, has compiler/coverage warnings)], - [${nut_with_nutconf}], [], - [WITH_NUTCONF], [Define to enable nutconf tool support]) dnl ---------------------------------------------------------------------- +PREFIX="${prefix}" +NUT_REPORT_SETTING_PATH([Default installation prefix path], + PREFIX, "${prefix}", [Default installation prefix path]) -AC_MSG_CHECKING(state path) +AC_MSG_CHECKING(if requested state path) AC_ARG_WITH(statepath, - AS_HELP_STRING([--with-statepath=PATH], [path for ups state files (/var/state/ups)]), + AS_HELP_STRING([--with-statepath=PATH], [path for ups state files (${STATEPATH}, typically /var/state/ups)]), [ case "${withval}" in yes|no) @@ -1563,11 +2992,12 @@ AC_ARG_WITH(statepath, ;; *) STATEPATH="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], []) -AC_DEFINE_UNQUOTED(STATEPATH, "${STATEPATH}", [Path for UPS driver state files]) -AC_MSG_RESULT(${STATEPATH}) +], [AC_MSG_RESULT([default])]) +NUT_REPORT_SETTING_PATH([State file path], + STATEPATH, "${STATEPATH}", [Path for UPS driver state files]) dnl --------------------------------------------------------------------- dnl The 'alt pid path' is used by the drivers (via main.c) and upsd, since @@ -1576,9 +3006,9 @@ dnl /var/run path. This defaults to the STATEPATH since they should be dnl able to write there. dnl -AC_MSG_CHECKING(alt pid path) +AC_MSG_CHECKING(if requested alt pid path) AC_ARG_WITH(altpidpath, - AS_HELP_STRING([--with-altpidpath=PATH], [path for driver/upsd .pid files ()]), + AS_HELP_STRING([--with-altpidpath=PATH], [path for NUT driver/upsd .pid files not running as root ()]), [ case "${withval}" in yes|no) @@ -1586,149 +3016,143 @@ AC_ARG_WITH(altpidpath, ;; *) ALTPIDPATH="${withval}" + AC_MSG_RESULT([specified]) ;; esac ], [ ALTPIDPATH="${STATEPATH}" + AC_MSG_RESULT([default]) ]) -AC_DEFINE_UNQUOTED(ALTPIDPATH, "${ALTPIDPATH}", [Path for pid files of drivers and upsd (usually STATEPATH)]) -AC_MSG_RESULT(${ALTPIDPATH}) +NUT_REPORT_SETTING_PATH([Unprivileged PID file path], + ALTPIDPATH, "${ALTPIDPATH}", [Path for pid files of processes not running as root, such as drivers and upsd (usually STATEPATH)]) -AC_MSG_CHECKING(driver path) -AC_ARG_WITH(drvpath, - AS_HELP_STRING([--with-drvpath=PATH], [where to install UPS drivers (EPREFIX/bin)]), +AC_MSG_CHECKING(if requested pidpath) +AC_ARG_WITH(pidpath, + AS_HELP_STRING([--with-pidpath=PATH], [Path for root-owned .pid files (${PIDPATH}, typically /var/run)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-drvpath - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-pidpath - see docs/configure.txt) ;; *) - driverexecdir="${withval}" + PIDPATH="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], []) -conftemp="${driverexecdir}" -eval conftemp=\"${conftemp}\" -eval conftemp=\"${conftemp}\" -AC_DEFINE_UNQUOTED(DRVPATH, "${conftemp}", [Default path for UPS drivers]) -AC_MSG_RESULT(${driverexecdir}) - -AC_MSG_CHECKING(cgi path) -AC_ARG_WITH(cgipath, - AS_HELP_STRING([--with-cgipath=PATH], [where to install CGI programs (EPREFIX/cgi-bin)]), +], [AC_MSG_RESULT([default])]) +NUT_REPORT_SETTING_PATH([Privileged PID file path], + PIDPATH, "${PIDPATH}", [Path for pid files of processes running as root, such as upsmon]) + +dnl -------------------------------------------------------------------- +dnl Legacy default was /etc/killpower, but modern distros may prefer some +dnl temporary filesystem (no I/O storage device impact) as long as it is +dnl mounted at least read-only late in shutdown routine. The upsmon program +dnl writes it as root, so this may well be in /var/run or in NUT PID/state +dnl path (if that location remains mounted in run-time OS distribution). +dnl Ideally the filesystems with `upsmon` program and libraries it needs +dnl also remain mounted, so `upsmon -K` may be queried late in shutdown - +dnl and we can avoid hardcoding such paths into those shutdown hooks. +dnl Note that upsmon removes this file early in any daemonized start-up. +AC_MSG_CHECKING(if requested default upsmon POWERDOWNFLAG path) +AC_ARG_WITH(powerdownflag, + AS_HELP_STRING([--with-powerdownflag=PATH], [default path for upsmon POWERDOWNFLAG file (${POWERDOWNFLAG}, typically /etc/killpower)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-cgipath - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-powerdownflag - see docs/configure.txt) ;; *) - cgiexecdir="${withval}" + POWERDOWNFLAG="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], []) -conftemp="${cgiexecdir}" -eval conftemp=\"${conftemp}\" -eval conftemp=\"${conftemp}\" -AC_DEFINE_UNQUOTED(CGIPATH, "${conftemp}", [Default path for CGI programs]) -AC_MSG_RESULT(${cgiexecdir}) +], [AC_MSG_RESULT([default])]) +dnl # This should be internal detail for "upsmon -K" implementation, +dnl # so not necessarily reported (reduce noise): +dnl NUT_REPORT_SETTING_PATH([Default upsmon POWERDOWNFLAG path], +dnl POWERDOWNFLAG, "${POWERDOWNFLAG}", [Default path for upsmon POWERDOWNFLAG file]) -AC_MSG_CHECKING(html path) -AC_ARG_WITH(htmlpath, - AS_HELP_STRING([--with-htmlpath=PATH], [where to install HTML files (PREFIX/html)]), +dnl --------------------------------------------------------------------- +AC_MSG_CHECKING(if requested driver path) +AC_ARG_WITH(drvpath, + AS_HELP_STRING([--with-drvpath=PATH], [where to install UPS drivers (EPREFIX/bin)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-htmlpath - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-drvpath - see docs/configure.txt) ;; *) - htmldir="${withval}" + driverexecdir="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], []) -conftemp="${htmldir}" +], [AC_MSG_RESULT([default])]) +conftemp="${driverexecdir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -AC_DEFINE_UNQUOTED(HTMLPATH, "${conftemp}", [Default path for HTML files]) -AC_MSG_RESULT(${htmldir}) - -AC_MSG_CHECKING(pidpath) -AC_ARG_WITH(pidpath, - AS_HELP_STRING([--with-pidpath=PATH], [path for .pid files (/var/run)]), -[ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-pidpath - see docs/configure.txt) - ;; - *) - PIDPATH="${withval}" - ;; - esac -], []) -AC_DEFINE_UNQUOTED(PIDPATH, "${PIDPATH}", [Path where the pid files should go]) -AC_MSG_RESULT(${PIDPATH}) +DRVPATH="${conftemp}" +NUT_REPORT_SETTING_PATH([Driver program path], + DRVPATH, "${conftemp}", [Default path for UPS drivers]) -AC_MSG_CHECKING(network port number) -AC_ARG_WITH(port, - AS_HELP_STRING([--with-port=PORT], [port for network communications (3493)]), +AC_MSG_CHECKING(if requested cgi path) +AC_ARG_WITH(cgipath, + AS_HELP_STRING([--with-cgipath=PATH], [where to install CGI programs (EPREFIX/cgi-bin)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-port - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-cgipath - see docs/configure.txt) ;; *) - PORT="${withval}" + cgiexecdir="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], [ - PORT="3493" -]) -AC_DEFINE_UNQUOTED(PORT, ${PORT}, [Port for network communications]) -AC_MSG_RESULT(${PORT}) +], [AC_MSG_RESULT([default])]) +conftemp="${cgiexecdir}" +eval conftemp=\"${conftemp}\" +eval conftemp=\"${conftemp}\" +CGIPATH="${conftemp}" +NUT_REPORT_SETTING_PATH([CGI program path], + CGIPATH, "${conftemp}", [Default path for CGI programs]) -AC_MSG_CHECKING(user to run as) -AC_ARG_WITH(user, - AS_HELP_STRING([--with-user=username], [user for programs started as root (nobody)]), +AC_MSG_CHECKING(if requested html path) +AC_ARG_WITH(htmlpath, + AS_HELP_STRING([--with-htmlpath=PATH], [where to install HTML files (PREFIX/html)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-user - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-htmlpath - see docs/configure.txt) ;; *) - RUN_AS_USER="${withval}" - nut_user_given=yes + htmldir="${withval}" + AC_MSG_RESULT([specified]) ;; esac -], [ - nut_user_given=no -]) -AC_DEFINE_UNQUOTED(RUN_AS_USER, "${RUN_AS_USER}", [User to switch to if started as root]) -AC_MSG_RESULT(${RUN_AS_USER}) +], [AC_MSG_RESULT([default])]) +conftemp="${htmldir}" +eval conftemp=\"${conftemp}\" +eval conftemp=\"${conftemp}\" +HTMLPATH="${conftemp}" +NUT_REPORT_SETTING_PATH([HTML file path], + HTMLPATH, "${conftemp}", [Default path for HTML files (CGI templates)]) -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)]), +AC_MSG_CHECKING(network port number) +AC_ARG_WITH(port, + AS_HELP_STRING([--with-port=PORT], [port for network communications (3493)]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-group - see docs/configure.txt) + AC_MSG_ERROR(invalid option --with(out)-port - see docs/configure.txt) ;; *) - RUN_AS_GROUP="${withval}" - nut_group_given=yes + PORT="${withval}" ;; esac ], [ - nut_group_given=no + PORT="3493" ]) -AC_DEFINE_UNQUOTED(RUN_AS_GROUP, "${RUN_AS_GROUP}", [Group membership of user to switch to if started as root]) -AC_MSG_RESULT(${RUN_AS_GROUP}) - -dnl check that --with-user is given if --with-group is given. -if test "${nut_user_given}" = "yes" -a "${nut_group_given}" = "no"; then - AC_MSG_ERROR([If you specify --with-user, you also must specify --with-group]) -elif test "${nut_user_given}" = "no" -a "${nut_group_given}" = "yes"; then - AC_MSG_ERROR([If you specify --with-group, you also must specify --with-user]) -fi +AC_DEFINE_UNQUOTED(PORT, ${PORT}, [Port for network communications]) +AC_MSG_RESULT(${PORT}) AC_MSG_CHECKING(facility for syslog) AC_ARG_WITH(logfacility, @@ -1749,6 +3173,7 @@ AC_DEFINE_UNQUOTED(LOG_FACILITY, ${LOGFACILITY}, [Desired syslog facility - see AC_MSG_RESULT(${LOGFACILITY}) AC_MSG_CHECKING(which driver man pages to install) +DRIVER_MAN_LIST_PAGES="" if test "${WITH_MANS}" = "yes"; then if test "${DRIVER_BUILD_LIST}" = "all"; then DRIVER_MAN_LIST=all @@ -1756,8 +3181,10 @@ if test "${WITH_MANS}" = "yes"; then else DRIVER_MAN_LIST="" for i in ${DRIVER_BUILD_LIST}; do - if test -f ${srcdir}/docs/man/$i.8; then + dnl See if source or pre-generated (tarball) doc file exists: + if test -f ${srcdir}/docs/man/$i.txt -o -f ${srcdir}/docs/man/$i.8; then DRIVER_MAN_LIST="${DRIVER_MAN_LIST} $i.8" + DRIVER_MAN_LIST_PAGES="${DRIVER_MAN_LIST_PAGES} $i.txt" fi done AC_MSG_RESULT(${DRIVER_MAN_LIST}) @@ -1767,11 +3194,38 @@ else AC_MSG_RESULT([none (manpages disabled)]) fi +dnl By default as we iterate (and git commit) the codebase during development, +dnl prerequisites for that header file change and cause much of the C code +dnl to be rebuilt and re-linked. For developers fixing one small part of the +dnl project after another (*and* committing fixes as they go on), the version +dnl string reported by their new binaries may be of lesser consequence than +dnl iterating *quickly* and rebuiding just what "really" changed! +dnl Still, this speed-up is not default to avoid surprises for core team. +AC_MSG_CHECKING(whether to force nut_version.h generation for every make run) +dnl Value is "FORCE" or empty, substituted into Makefile.am rule: +FORCE_NUT_VERSION="FORCE" +AC_ARG_ENABLE(force-nut-version-header, + AS_HELP_STRING([--enable-force-nut-version-header], [Force nut_version.h generation for every make run (yes)]), +[ + case "${enableval}" in + no) + AC_MSG_RESULT(no) + FORCE_NUT_VERSION="" + ;; + *) + AC_MSG_RESULT(yes) + ;; + esac +], [ + AC_MSG_RESULT(no) +]) + + 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) ;; @@ -1799,41 +3253,27 @@ AC_ARG_WITH(pkgconfig-dir, ;; esac ], []) + +dnl Note: currently pkgconfigdir='${libdir}/pkgconfig' literally +dnl goes into lib/Makefile.am substitution for pkgconfig_DATA. +dnl By default we get ${libdir}/pkgconfig and below expand it to +dnl => ${exec_prefix}/lib/pkgconfig => ${prefix}/lib/pkgconfig => real path +conftemp="${pkgconfigdir}" +eval conftemp=\"${conftemp}\" +eval conftemp=\"${conftemp}\" +eval conftemp=\"${conftemp}\" +PKGCONFIGDIR="${conftemp}" + if test -n "${pkgconfigdir}"; then - AC_MSG_RESULT(using ${pkgconfigdir}) + AC_MSG_RESULT(using ${pkgconfigdir} => ${conftemp}) + NUT_REPORT_PATH_INTEGRATIONS([pkg-config *.pc directory], [${pkgconfigdir} => ${conftemp}]) else AC_MSG_RESULT(no) fi AM_CONDITIONAL(WITH_PKG_CONFIG, test -n "${pkgconfigdir}") -AC_MSG_CHECKING(whether to install Solaris SMF files) -solarissmf="auto" -AC_ARG_WITH([solaris-smf], - AS_HELP_STRING([--with-solaris-smf=(yes|auto|no)], [Enable installation of NUT scripts and manifests for Solaris Service Management Framework (auto)]), -[ - case "${withval}" in - auto|"") - solarissmf="auto" - ;; - yes|no) - solarissmf="${withval}" - ;; - *) - AC_MSG_ERROR([Unexpected argument for --with-solaris-smf=${withval}]) - ;; - esac -], []) - -if test x"$solarissmf" = xauto ; then - if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then - solarissmf="yes" - else - solarissmf="no" - fi -fi -AC_MSG_RESULT([${solarissmf}]) -AM_CONDITIONAL(WITH_SOLARIS_SMF, test x"$solarissmf" = x"yes") +dnl Options for Solaris/illumos `make install` and `make package` AC_MSG_CHECKING(whether to make Solaris SVR4 packages) solarispkg_svr4="auto" AC_ARG_WITH([solaris-pkg-svr4], @@ -1890,6 +3330,83 @@ fi AC_MSG_RESULT([${solarispkg_ips}]) AM_CONDITIONAL(WITH_SOLARIS_PKG_IPS, test x"$solarispkg_ips" = x"yes") + +dnl NOTE: Be sure to customize e.g. --datarootdir=/usr/share/nut to install +dnl these scripts not into default location as e.g. /usr/share/solaris-smf +AC_MSG_CHECKING(whether to install Solaris SMF files) +solarissmf="auto" +AC_ARG_WITH([solaris-smf], + AS_HELP_STRING([--with-solaris-smf=(yes|auto|no)], [Enable installation of NUT scripts and manifests for Solaris Service Management Framework (auto)]), +[ + case "${withval}" in + auto|"") + solarissmf="auto" + ;; + yes|no) + solarissmf="${withval}" + ;; + *) + AC_MSG_ERROR([Unexpected argument for --with-solaris-smf=${withval}]) + ;; + esac +], []) + +if test x"$solarissmf" = xauto ; then + if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + solarissmf="yes" + else + case "${solarispkg_ips}${solarispkg_svr4}" in + *yes*) solarisinit="yes" ;; dnl Want to install so we can generally package + *) solarissmf="no" ;; dnl Target not solarish + esac + fi +fi +AC_MSG_RESULT([${solarissmf}]) +AM_CONDITIONAL(WITH_SOLARIS_SMF, test x"$solarissmf" = x"yes") +NUT_REPORT([consider basic SMF support], [${solarissmf}]) + + +AC_MSG_CHECKING(whether to install Solaris SVR4 (legacy) init-script files) +solarisinit="auto" +AC_ARG_WITH([solaris-init], + AS_HELP_STRING([--with-solaris-init=(yes|auto|no)], [Enable installation of NUT legacy init-scripts for Solaris/illumos (auto)]), +[ + case "${withval}" in + auto|"") + solarisinit="auto" + ;; + yes|no) + solarisinit="${withval}" + ;; + *) + AC_MSG_ERROR([Unexpected argument for --with-solaris-init=${withval}]) + ;; + esac +], []) + +if test x"$solarisinit" = xauto ; then + dnl Depends on usability of SMF or making for packaging + case "${solarispkg_ips}${solarispkg_svr4}" in + *yes*) solarisinit="yes" ;; dnl Want to install so we can generally package + *) + case ${target_os} in + solaris*|sunos*|SunOS*|illumos*) + if test "$solarissmf" = x"yes" ; then + dnl no need on modern OSes + solarisinit="no" + else + solarisinit="yes" + fi + ;; + *) solarisinit="no" ;; dnl Some other OS + esac + ;; + esac +fi +AC_MSG_RESULT([${solarisinit}]) +AM_CONDITIONAL(WITH_SOLARIS_INIT, test x"$solarisinit" = x"yes") + + dnl Note: Currently there is no reliable automatic detection - dnl users have to ask they want systemd units installed, or dnl risk auto-detection like seen below. @@ -1902,8 +3419,8 @@ case "${systemdsystemunitdir}" in yes|auto|"") AS_IF([test x"$have_PKG_CONFIG" = xyes], [systemdsystemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null`" && test -n "$systemdsystemunitdir" || systemdsystemunitdir="`$PKG_CONFIG --variable=systemdsystemunitdir libsystemd 2>/dev/null`"], - [AS_IF([test "${withval}" = yes], - [AC_MSG_ERROR([--with-systemdsystemunitdir=${withval} was requested, but PKG_CONFIG could not be queried for the system settings])]) + [AS_IF([test "${systemdsystemunitdir}" = yes], + [AC_MSG_ERROR([--with-systemdsystemunitdir=${systemdsystemunitdir} was requested, but PKG_CONFIG could not be queried for the system settings])]) systemdsystemunitdir="" ]) ;; @@ -1911,16 +3428,24 @@ case "${systemdsystemunitdir}" in systemdsystemunitdir="" ;; *) - systemdsystemunitdir="${withval}" + AS_IF([test -d "${systemdsystemunitdir}"], [], + [AC_MSG_WARN([--with-systemdsystemunitdir='${systemdsystemunitdir}' was requested, but that location does not currently exist in build environment - just so you know...])]) ;; esac if test "${systemdsystemunitdir}" = "auto" ; then systemdsystemunitdir=""; fi if test -n "${systemdsystemunitdir}"; then + have_systemd="yes" AC_MSG_RESULT(using ${systemdsystemunitdir}) + NUT_REPORT_PATH_INTEGRATIONS([Service units for systemd], [${systemdsystemunitdir}]) else + have_systemd="no" AC_MSG_RESULT(no) fi -AM_CONDITIONAL(HAVE_SYSTEMD, test "$systemdsystemunitdir" != "") +dnl Note: we may want tighter integration, e.g. systemd-notify support +dnl configured as a further option/flag (see --with-systemd below). +dnl This one is a very basic yes/no toggle for unit file delivery. +NUT_REPORT_FEATURE([consider basic systemd support], [${have_systemd}], [], + [HAVE_SYSTEMD], [Define to consider basic systemd support (provide units and configuration files)]) dnl This option is only provided so that make distcheck can override it, dnl otherwise we ask pkg-config whenever --with-systemdsystemunitdir is dnl given @@ -1945,13 +3470,15 @@ if test -n "${systemdsystemunitdir}"; then systemdshutdowndir="" ;; *) - systemdshutdowndir="${withval}" + AS_IF([test -d "${systemdshutdowndir}"], [], + [AC_MSG_WARN([--with-systemdshutdowndir='${systemdshutdowndir}' was requested, but that location does not currently exist in build environment - just so you know...])]) ;; esac fi if test "${systemdshutdowndir}" = "auto" ; then systemdshutdowndir=""; fi if test -n "${systemdshutdowndir}"; then AC_MSG_RESULT(using ${systemdshutdowndir}) + NUT_REPORT_PATH_INTEGRATIONS([Shutdown hooks for systemd], [${systemdshutdowndir}]) else AC_MSG_RESULT(no) fi @@ -1976,16 +3503,137 @@ case "${systemdtmpfilesdir}" in systemdtmpfilesdir="" ;; *) - systemdtmpfilesdir="${withval}" + AS_IF([test -d "${systemdtmpfilesdir}"], [], + [AC_MSG_WARN([--with-systemdtmpfilesdir='${systemdtmpfilesdir}' was requested, but that location does not currently exist in build environment - just so you know...])]) ;; esac if test "${systemdtmpfilesdir}" = "auto" ; then systemdtmpfilesdir=""; fi if test -n "${systemdtmpfilesdir}"; then AC_MSG_RESULT(using ${systemdtmpfilesdir}) + NUT_REPORT_PATH_INTEGRATIONS([Systemd-tmpfiles configs], [${systemdtmpfilesdir}]) else AC_MSG_RESULT(no) fi +dnl What pathname would we embed into unit files ExecStartPre? +dnl TODO? Any need to make it a --with-... argument? +AC_PATH_PROG([SYSTEMD_TMPFILES_PROGRAM], [systemd-tmpfiles], [/usr/bin/systemd-tmpfiles]) + + +dnl Note: we may want binaries with sd_notify and similar features regardless +dnl of building and delivering unit files (which may be crafted separately). +dnl TODO: although end-user deployments (for custom builds) may be lacking +dnl libsystemd development files, they might have a `systemd-notify` program +dnl intended to help scripted service units. Consider making use of that then. +NUT_ARG_WITH([libsystemd], [build binaries with tighter systemd integration (notifications etc)], [auto]) +NUT_CHECK_LIBSYSTEMD + +AC_MSG_CHECKING(whether requested and can build binaries with tighter systemd integration support) +AS_IF([test x"${nut_with_libsystemd}" = xyes && test x"${nut_have_libsystemd}" != xyes], + [AC_MSG_ERROR([--with-libsystemd was requested, but the library was not found or usable])]) +AS_CASE(["${nut_with_libsystemd}"], + [yes|no], [have_libsystemd="${nut_with_libsystemd}"], + [AS_IF([test x"${nut_have_libsystemd}" = xyes], + [with_libsystemd="yes"], + [with_libsystemd="no"]) + ]) +AC_MSG_RESULT(${with_libsystemd}) + +AC_PATH_PROG([SYSTEMD_ANALYZE_PROGRAM], [systemd-analyze], [/usr/bin/systemd-analyze]) + +dnl Relevant since 2023: https://github.com/systemd/systemd/pull/25916 +SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY=no +AS_IF([test -x "$SYSTEMD_ANALYZE_PROGRAM"], [ + AC_MSG_CHECKING([if your systemd version supports Type=notify]) + myFILE="`mktemp systemd-analyze-XXXXXX.service`" + cat > "$myFILE" << EOF +@<:@Unit@:>@ +Description=temp +@<:@Service@:>@ +ExecStart=/bin/true +Type=notify +EOF + if myOUT="`"$SYSTEMD_ANALYZE_PROGRAM" verify "$myFILE" 2>&1`" \ + && ! (echo "$myOUT" | grep "Failed to parse service type, ignoring") \ + ; then + SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY=yes + fi + rm -f "$myFILE" + AC_MSG_RESULT([${SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY}]) + ]) + +SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY_RELOAD=no +AS_IF([test -x "$SYSTEMD_ANALYZE_PROGRAM"], [ + AC_MSG_CHECKING([if your systemd version supports Type=notify-reload]) + myFILE="`mktemp systemd-analyze-XXXXXX.service`" + cat > "$myFILE" << EOF +@<:@Unit@:>@ +Description=temp +@<:@Service@:>@ +ExecStart=/bin/true +Type=notify-reload +EOF + if myOUT="`"$SYSTEMD_ANALYZE_PROGRAM" verify "$myFILE" 2>&1`" \ + && ! (echo "$myOUT" | grep "Failed to parse service type, ignoring") \ + ; then + SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY_RELOAD=yes + fi + rm -f "$myFILE" + AC_MSG_RESULT([${SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY_RELOAD}]) + ]) + +AS_IF([test x"${with_libsystemd}" = xyes && test x"${SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY}" = xyes], [ + dnl Built with sd_notify support + dnl Note: `upsd -FF` both runs without forking and leaves a PID file, as + dnl needed for `upsd -c reload` in legacy scripts and old habits to work: + SYSTEMD_DAEMON_ARGS_UPSD="-FF" + SYSTEMD_DAEMON_TYPE_UPSD="notify" + SYSTEMD_DAEMON_ARGS_UPSMON="-F" + SYSTEMD_DAEMON_TYPE_UPSMON="notify" + SYSTEMD_DAEMON_ARGS_DRIVER="-FF" + SYSTEMD_DAEMON_TYPE_DRIVER="notify" + AS_IF([test x"${SYSTEMD_SUPPORTS_DAEMON_TYPE_NOTIFY_RELOAD}" = xyes], [ + dnl Macro supported since aclocal-1.11: + m4_ifdef([AM_COND_IF], + [AM_COND_IF([HAVE_CLOCK_GETTIME], [AM_COND_IF([HAVE_CLOCK_MONOTONIC], [SYSTEMD_DAEMON_TYPE_DRIVER="notify-reload"])])], + [AS_IF([test x"${ac_cv_func_clock_gettime}" = "xyes"], [SYSTEMD_DAEMON_TYPE_DRIVER="notify-reload"])]) + ]) + dnl Calling shell, upsdrvctl, driver, and then it forks... ugh! + dnl https://github.com/systemd/systemd/issues/25961 + dnl FIXME: if NotifyAccess=cgroup appears, use it (consult SYSTEMD_VERSION) + SYSTEMD_DAEMON_NOTIFYACCESS_DRIVER="NotifyAccess=all" + dnl Similar for UPSMON with its two processes: + SYSTEMD_DAEMON_NOTIFYACCESS_UPSMON="NotifyAccess=all" + dnl UPSD is started directly by systemd and do not fork: + SYSTEMD_DAEMON_NOTIFYACCESS_UPSD="NotifyAccess=main" + dnl Note: at this time we do not pre-define watchdog settings, + dnl to avoid breaking something by a poorly hardcoded guess. + dnl This is something end-users should do for their deployment, + dnl especially for drivers + SYSTEMD_DAEMON_WATCHDOG_DRIVER="#WatchdogSec=240s" + SYSTEMD_DAEMON_WATCHDOG_UPSD="#WatchdogSec=240s" + SYSTEMD_DAEMON_WATCHDOG_UPSMON="#WatchdogSec=240s" + ], [ + dnl "Usual" daemons that happen to be spawned by systemd + SYSTEMD_DAEMON_ARGS_UPSD="-F" + SYSTEMD_DAEMON_TYPE_UPSD="simple" + SYSTEMD_DAEMON_ARGS_UPSMON="-F" + SYSTEMD_DAEMON_TYPE_UPSMON="simple" + SYSTEMD_DAEMON_ARGS_DRIVER="" + SYSTEMD_DAEMON_TYPE_DRIVER="forking" + SYSTEMD_DAEMON_NOTIFYACCESS_DRIVER="" + SYSTEMD_DAEMON_NOTIFYACCESS_UPSD="" + SYSTEMD_DAEMON_NOTIFYACCESS_UPSMON="" + dnl Watchdog should not be configured for not-notifying units, right? + SYSTEMD_DAEMON_WATCHDOG_DRIVER="#WatchdogSec=240s" + SYSTEMD_DAEMON_WATCHDOG_UPSD="#WatchdogSec=240s" + SYSTEMD_DAEMON_WATCHDOG_UPSMON="#WatchdogSec=240s" + ]) + +NUT_REPORT_FEATURE([build with tighter systemd support], [${with_libsystemd}], [], + [WITH_LIBSYSTEMD], [Define to build with tighter systemd support (sd_notify etc)]) + + dnl dnl Tests for CppUnit availability and usability (will be built if we can, dnl and if valgrind is enabled for this configuration - reported below). @@ -2012,16 +3660,25 @@ CPLUSPLUS_DECL=' #if __cplusplus < 201103L #error This library needs at least a C++11 compliant compiler #endif + +/* Make sure it supports modern syntax */ +#include +#include +' +CPLUSPLUS_MAIN=' +printf("%ld\n", __cplusplus); +/* Make sure it supports modern syntax */ +std::map map; +map.emplace("key", "value"); ' -CPLUSPLUS_MAIN='printf("%ld\n", __cplusplus);' AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], [AC_MSG_RESULT([yes, out of the box]) have_cxx11=yes], [AS_CASE(["${CXXFLAGS}"], - [*"-std="*], [ + [*"-std="*|*"-ansi"*], [ AC_MSG_RESULT([no, not with the standard already set in CXXFLAGS='${CXXFLAGS}']) - have_cxx11=no, + have_cxx11=no ],[ CXXFLAGS="$CXXFLAGS -std=c++11" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], @@ -2034,13 +3691,17 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]]) [AC_MSG_RESULT([no]) CXXFLAGS="$my_CXXFLAGS" have_cxx11=no])])])]) -AM_CONDITIONAL(HAVE_CXX11, test "${have_cxx11}" = "yes") +NUT_REPORT_FEATURE([build C++11 codebase (client library, etc.)], [${have_cxx11}], [], + [HAVE_CXX11], [Define to enable C++11 support]) AC_LANG_POP([C++]) unset CPLUSPLUS_MAIN unset CPLUSPLUS_DECL AC_MSG_CHECKING(for have_cppunit) have_cppunit="no" +dnl CPPUNIT_NUT_CXXFLAGS are set below if suitable, but can be +dnl disabled further below if nut_with_debuginfo gets applied +dnl for all NUT build products: CPPUNIT_NUT_CXXFLAGS="" AS_IF([test x"$have_PKG_CONFIG" = xyes], [AS_IF([test x"${have_cxx11}" = xyes], @@ -2048,9 +3709,8 @@ AS_IF([test x"$have_PKG_CONFIG" = xyes], AS_IF([test "${have_cppunit}" != "yes"], [AC_MSG_WARN([libcppunit not found - those C++ tests will not be built.]) have_cppunit=no], - [AS_IF([test -n "$CXX"], - [AS_IF([$CXX --version 2>&1 | grep 'Free Software Foundation' > /dev/null], - [CPPUNIT_NUT_CXXFLAGS="-g -O0"])]) + [AS_IF([test "x$GXX" = xyes], + [CPPUNIT_NUT_CXXFLAGS="-g -O0"]) ]) ]) ], [AC_MSG_WARN([pkg-config not found, can not look properly for libcppunit - those C++ tests will not be built.]) @@ -2105,29 +3765,58 @@ 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 + AC_MSG_RESULT(${have_cppunit}) ]) -AC_MSG_RESULT(${have_cppunit}) dnl # By default keep the originally detected have_cppunit value AC_MSG_CHECKING(for impact from --enable-cppunit option - should we build cppunit tests?) AC_ARG_ENABLE(cppunit, - [AS_HELP_STRING([--enable-cppunit], [enable CPPUNIT tests for C++ bindings])], + [AS_HELP_STRING([--enable-cppunit], [enable CPPUNIT tests for C++ bindings (yes, no, force, auto)])], [AS_CASE(["${enableval}"], - ["yes"], [AS_IF([test x"${have_cppunit}" = xyes], [], [AC_MSG_ERROR([--with-cppunit=yes can not be satisfied])])], + ["force"], [AS_IF([test x"${have_cppunit}" = xyes], [], [ + AC_MSG_WARN([--enable-cppunit=yes can not be satisfied, but developer asked for it]) + have_cppunit=yes + ])], + ["yes"], [AS_IF([test x"${have_cppunit}" = xyes], [], [AC_MSG_ERROR([--enable-cppunit=yes can not be satisfied])])], ["no"], [have_cppunit=no] + dnl # "auto" and other values keep what was detected (or not) )]) AC_MSG_RESULT(${have_cppunit}) -AM_CONDITIONAL(HAVE_CPPUNIT, test "${have_cppunit}" = "yes") -AC_DEFINE_UNQUOTED(CPPUNIT_NUT_CXXFLAGS, $CPPUNIT_NUT_CXXFLAGS, [Compiler flags for cppunit tests]) +NUT_REPORT_FEATURE([build C++ tests with CPPUNIT], [${have_cppunit}], [], + [HAVE_CPPUNIT], [Define to enable CPPUNIT tests]) + +dnl ---------------------------------------------------------------------- + +AC_MSG_CHECKING(whether we can and want to build nutconf configuration-management tool) + +AS_CASE(["${nut_with_nutconf}"], + ["auto"], [AS_IF([test x"${have_cxx11}" = xyes], [nut_with_nutconf="yes"], [nut_with_nutconf="no"])], + ["yes"], [AS_IF([test x"${have_cxx11}" = xyes], [], [AC_MSG_ERROR([explicit --with-nutconf=yes can not be satisfied: C++11 support not enabled])])], + ["no"], [nut_with_nutconf=no] + ) +AC_MSG_RESULT(${nut_with_nutconf}) + +AM_CONDITIONAL(WITH_NUTCONF, test "${nut_with_nutconf}" = "yes") +NUT_REPORT_FEATURE([build and install the nutconf tool (experimental, may lack support for recent NUT options)], + [${nut_with_nutconf}], [], + [WITH_NUTCONF], [Define to enable nutconf tool support]) + +dnl ---------------------------------------------------------------------- AC_MSG_CHECKING(whether to install Augeas configuration-management lenses) AC_ARG_WITH(augeas-lenses-dir, @@ -2160,6 +3849,7 @@ AC_ARG_WITH(augeas-lenses-dir, ], []) if test -n "${auglensdir}"; then AC_MSG_RESULT(using ${auglensdir}) + NUT_REPORT_PATH_INTEGRATIONS([Augeas lenses directory], [${auglensdir}]) else AC_MSG_RESULT(no) fi @@ -2183,7 +3873,7 @@ fi AC_MSG_CHECKING(whether to install hotplug rules) AC_ARG_WITH(hotplug-dir, - AS_HELP_STRING([--with-hotplug-dir=PATH], [where to install hotplug rules (/etc/hotplug)]), + AS_HELP_STRING([--with-hotplug-dir=PATH], [where to install hotplug rules (${hotplugdir}); typically /etc/hotplug]), [ case "${withval}" in yes) @@ -2204,6 +3894,7 @@ AC_ARG_WITH(hotplug-dir, ], []) if test -n "${hotplugdir}"; then AC_MSG_RESULT(using ${hotplugdir}) + NUT_REPORT_PATH_INTEGRATIONS([Hotplug rules directory], [${hotplugdir}]) else AC_MSG_RESULT(no) fi @@ -2211,10 +3902,10 @@ AM_CONDITIONAL(WITH_HOTPLUG, test -n "${hotplugdir}") AC_MSG_CHECKING(whether to install udev rules) AC_ARG_WITH(udev-dir, - AS_HELP_STRING([--with-udev-dir=PATH], [where to install udev rules (/lib/udev or /etc/udev)]), + AS_HELP_STRING([--with-udev-dir=PATH], [where to install udev rules (${udevdir}; typically /lib/udev or /etc/udev)]), [ case "${withval}" in - yes) + yes) dnl Typically /lib/udev or /etc/udev if test -z "${udevdir}"; then AC_MSG_RESULT(no) AC_MSG_ERROR([udev directory requested but not found]) @@ -2240,6 +3931,7 @@ AC_ARG_WITH(udev-dir, ], []) if test -n "${udevdir}"; then AC_MSG_RESULT(using ${udevdir}) + NUT_REPORT_PATH_INTEGRATIONS([Udev rules directory], [${udevdir}]) else AC_MSG_RESULT(no) fi @@ -2249,10 +3941,10 @@ dnl FreeBSD devd support: AC_MSG_CHECKING(whether to install FreeBSD devd.conf file) AC_ARG_WITH(devd-dir, - AS_HELP_STRING([--with-devd-dir=PATH], [where to install devd.conf file (/usr/local/etc/devd or /etc/devd)]), + AS_HELP_STRING([--with-devd-dir=PATH], [where to install devd.conf file (${devddir}; typically /usr/local/etc/devd or /etc/devd)]), [ case "${withval}" in - yes) + yes) dnl Typically /usr/local/etc/devd or /etc/devd if test -z "${devddir}"; then AC_MSG_RESULT(no) AC_MSG_ERROR([devd directory requested but not found]) @@ -2278,11 +3970,66 @@ AC_ARG_WITH(devd-dir, ], []) if test -n "${devddir}"; then AC_MSG_RESULT(using ${devddir}) + NUT_REPORT_PATH_INTEGRATIONS([FreeBSD devd rules directory], [${devddir}]) else AC_MSG_RESULT(no) fi AM_CONDITIONAL(WITH_DEVD, test -n "${devddir}") +dnl FreeBSD quirks support: + +freebsdquirksdir="" +AC_MSG_CHECKING(whether to install FreeBSD site-local USB quirks file) +AC_ARG_WITH(freebsd-quirks-dir, + AS_HELP_STRING([--with-freebsd-quirks-dir=PATH], [where to install nut-usb.quirks file (${datadir}; typically /usr/local/share/nut)]), +[ + case "${withval}" in + yes) dnl Typically /usr/local/share/nut + freebsdquirksdir="${datadir}" + ;; + auto) + dnl Are we building for FreeBSD with such customizations? + if test -s /boot/loader.conf.local ; then + freebsdquirksdir="${datadir}" + fi + if test "${nut_with_usb}" = yes && ! test -s scripts/devd/nut-usb.quirks ; then + AC_MSG_WARN([freebsd-quirks-dir directory skipped because a non-trivial scripts/devd/nut-usb.quirks was not provided by autogen.sh or dist archive]) + freebsdquirksdir="" + fi + ;; + no) + freebsdquirksdir="" + ;; + *) + freebsdquirksdir="${withval}" + ;; + esac +], []) +if test -n "${freebsdquirksdir}"; then + AC_MSG_RESULT(using ${freebsdquirksdir}) + NUT_REPORT_PATH_INTEGRATIONS([FreeBSD site-local USB quirks directory (add into /boot/loader.conf.local)], [${freebsdquirksdir}]) +else + AC_MSG_RESULT(no) +fi + +if test -n "${freebsdquirksdir}" ; then + dnl Expand datadir or similar macros in this path + conftemp="${freebsdquirksdir}" + eval conftemp=\"${conftemp}\" + eval conftemp=\"${conftemp}\" + freebsdquirksdir="${conftemp}" + + if test -z "${freebsdquirksdir}"; then + AC_MSG_ERROR([freebsd-quirks-dir requested but not found (resolved as empty)]) + fi + + if test "${nut_with_usb}" = yes && ! test -s scripts/devd/nut-usb.quirks ; then + AC_MSG_ERROR([freebsd-quirks-dir directory and USB driver support requested, but a non-trivial scripts/devd/nut-usb.quirks was not provided by autogen.sh or dist archive]) + fi +fi + +AM_CONDITIONAL(WITH_FREEBSD_QUIRKS_DIR, test -n "${freebsdquirksdir}") + dnl dnl AIX system @@ -2379,48 +4126,53 @@ dnl autotools default would be $prefix/etc conftemp="${sysconfdir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -CONFPATH=${conftemp} -AC_DEFINE_UNQUOTED(CONFPATH, "${conftemp}", [Default path for configuration files]) +CONFPATH="${conftemp}" +NUT_REPORT_SETTING_PATH([Config file path], + CONFPATH, "${conftemp}", [Default path for configuration files]) dnl same for datadir conftemp="${datadir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -DATADIR=${conftemp} -NUT_DATADIR=${conftemp} -AC_DEFINE_UNQUOTED(DATADIR, "${conftemp}", [Default path for data files]) +NUT_DATADIR="${conftemp}" +NUT_REPORT_SETTING_PATH([Data file path], + NUT_DATADIR, "${conftemp}", [Default path for data files]) dnl same for bindir conftemp="${bindir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -BINDIR=${conftemp} -AC_DEFINE_UNQUOTED(BINDIR, "${conftemp}", [Default path for user executables]) +BINDIR="${conftemp}" +NUT_REPORT_SETTING_PATH([Tool program path], + BINDIR, "${conftemp}", [Default path for user executables]) dnl same for sbindir conftemp="${sbindir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -SBINDIR=${conftemp} -AC_DEFINE_UNQUOTED(SBINDIR, "${conftemp}", [Default path for system executables]) +SBINDIR="${conftemp}" +NUT_REPORT_SETTING_PATH([System program path], + SBINDIR, "${conftemp}", [Default path for system executables]) dnl same for libdir conftemp="${libdir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -LIBDIR=${conftemp} -AC_DEFINE_UNQUOTED(LIBDIR, "${conftemp}", [Default path for system libraries]) +LIBDIR="${conftemp}" +NUT_REPORT_SETTING_PATH([System library path], + LIBDIR, "${conftemp}", [Default path for system libraries]) dnl same for libexecdir conftemp="${libexecdir}" eval conftemp=\"${conftemp}\" eval conftemp=\"${conftemp}\" -LIBEXECDIR=${conftemp} -AC_DEFINE_UNQUOTED(LIBEXECDIR, "${conftemp}", [Default path for system exec-libraries]) +LIBEXECDIR="${conftemp}" +NUT_REPORT_SETTING_PATH([System exec-library path], + LIBEXECDIR, "${conftemp}", [Default path for system exec-libraries]) dnl ---------------------------------------------------------------------- dnl Check for --with-dmf, --with-lua and/or --with-dmf_lua, --with-snmp_dmf and/or --with-snmp_dmf_lua -dnl This MUST be after DATADIR has been processed! +dnl This MUST be after NUT_DATADIR has been processed! NUT_ARG_WITH([dmf], [enable DMF (data mapping file) generic support], [auto]) NUT_ARG_WITH([lua], [enable LUA-5.1+ scripting generic support], [auto]) NUT_ARG_WITH([dmf_lua], [enable DMF (data mapping file) support including LUA-5.1+ scripting], [auto]) @@ -2499,6 +4251,8 @@ fi if test "${nut_with_snmp}" != "no"; then nut_with_snmp="${nut_have_libnetsnmp}" +else + nut_have_libnetsnmp_static="no" fi dnl (Legacy) Should we limit so to require libneon for using @@ -2576,6 +4330,8 @@ fi NUT_REPORT_FEATURE([build SNMP drivers], [${nut_with_snmp}], [], [WITH_SNMP], [Define to enable SNMP support]) +NUT_REPORT_FEATURE([build SNMP drivers with statically linked lib(net)snmp], [${nut_have_libnetsnmp_static}], [], + [WITH_SNMP_STATIC], [Define to use SNMP support with a statically linked libnetsnmp]) AM_CONDITIONAL([HAVE_SNMP], [test "${nut_have_libsnmp}" = "yes"]) NUT_REPORT_FEATURE([enable LUA-5.1+ scripting generic support], [${nut_with_lua}], [], @@ -2623,15 +4379,15 @@ dmfnutscanresdir="" dmfsnmpdir="" dmfnutscandir="" if test "${nut_with_dmf}" = yes -o "${nut_with_snmp_dmf}" = yes -o "${nut_with_snmp_dmf_lua}" = yes ; then - dmfsnmpresdir="${DATADIR}/dmfsnmp" - dmfnutscanresdir="${DATADIR}/dmfnutscan" - dmfsnmpdir="${DATADIR}/dmfsnmp.d" - dmfnutscandir="${DATADIR}/dmfnutscan.d" + dmfsnmpresdir="${NUT_DATADIR}/dmfsnmp" + dmfnutscanresdir="${NUT_DATADIR}/dmfnutscan" + dmfsnmpdir="${NUT_DATADIR}/dmfsnmp.d" + dmfnutscandir="${NUT_DATADIR}/dmfnutscan.d" fi AC_MSG_CHECKING(whether to install DMF resource collection of mappings for SNMP-UPS driver) AC_ARG_WITH(dmfsnmp-res-dir, - AS_HELP_STRING([--with-dmfsnmp-res-dir=PATH], [where to install DMF resource collection of mappings for SNMP-UPS driver (DATADIR/dmfsnmp if DMF+SNMP are enabled)]), + AS_HELP_STRING([--with-dmfsnmp-res-dir=PATH], [where to install DMF resource collection of mappings for SNMP-UPS driver (NUT_DATADIR/dmfsnmp if DMF+SNMP are enabled)]), [ case "${withval}" in yes) @@ -2659,7 +4415,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_DMFSNMP_RES_DIR, "${dmfsnmpresdir}", [Default path fo AC_MSG_CHECKING(whether to activate DMF collection of mappings for SNMP-UPS driver) AC_ARG_WITH(dmfsnmp-dir, - AS_HELP_STRING([--with-dmfsnmp-dir=PATH], [where to install DMF active collection of mappings for SNMP-UPS driver (DATADIR/dmfsnmp.d if DMF+SNMP are enabled)]), + AS_HELP_STRING([--with-dmfsnmp-dir=PATH], [where to install DMF active collection of mappings for SNMP-UPS driver (NUT_DATADIR/dmfsnmp.d if DMF+SNMP are enabled)]), [ case "${withval}" in yes) @@ -2687,7 +4443,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_DMFSNMP_DIR, "${dmfsnmpdir}", [Default path for DMF a AC_MSG_CHECKING(whether to install DMF resource collection of mappings for nut-scanner SNMP mode) AC_ARG_WITH(dmfnutscan-res-dir, - AS_HELP_STRING([--with-dmfnutscan-res-dir=PATH], [where to install DMF resourcecollection of mappings for nut-scanner SNMP mode (DATADIR/dmfnutscan if DMF+SNMP are enabled)]), + AS_HELP_STRING([--with-dmfnutscan-res-dir=PATH], [where to install DMF resourcecollection of mappings for nut-scanner SNMP mode (NUT_DATADIR/dmfnutscan if DMF+SNMP are enabled)]), [ case "${withval}" in yes) @@ -2715,7 +4471,7 @@ AC_DEFINE_UNQUOTED(DEFAULT_DMFNUTSCAN_RES_DIR, "${dmfnutscanresdir}", [Default p AC_MSG_CHECKING(whether to activate DMF collection of mappings for nut-scanner SNMP mode) AC_ARG_WITH(dmfnutscan-dir, - AS_HELP_STRING([--with-dmfnutscan-dir=PATH], [where to install DMF active collection of mappings for nut-scanner SNMP mode (DATADIR/dmfnutscan.d if DMF+SNMP are enabled)]), + AS_HELP_STRING([--with-dmfnutscan-dir=PATH], [where to install DMF active collection of mappings for nut-scanner SNMP mode (NUT_DATADIR/dmfnutscan.d if DMF+SNMP are enabled)]), [ case "${withval}" in yes) @@ -2991,35 +4747,96 @@ NUT_REPORT_FEATURE([enable validation of DMF collection of mappings for nut-scan AM_CONDITIONAL([WITH_VALIDATE_DMF_NUTSCAN], [test "${with_xmllint_dmf_nutscan}" = "yes"]) if test -n "${host_alias}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_HOST_ALIAS, "${host_alias}", [host env spec we run on]) + NUT_REPORT_TARGET(AUTOTOOLS_HOST_ALIAS, "${host_alias}", [host env spec we run on]) else if test -n "${host}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_HOST_ALIAS, "${host}", [host env spec we run on]) + NUT_REPORT_TARGET(AUTOTOOLS_HOST_ALIAS, "${host}", [host env spec we run on]) fi fi if test -n "${build_alias}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_BUILD_ALIAS, "${build_alias}", [host env spec we built on]) + NUT_REPORT_TARGET(AUTOTOOLS_BUILD_ALIAS, "${build_alias}", [host env spec we built on]) else if test -n "${build}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_BUILD_ALIAS, "${build}", [host env spec we built on]) + NUT_REPORT_TARGET(AUTOTOOLS_BUILD_ALIAS, "${build}", [host env spec we built on]) fi fi if test -n "${target_alias}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_TARGET_ALIAS, "${target_alias}", [host env spec we built for]) + NUT_REPORT_TARGET(AUTOTOOLS_TARGET_ALIAS, "${target_alias}", [host env spec we built for]) else if test -n "${target}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_TARGET_ALIAS, "${target}", [host env spec we built for]) + NUT_REPORT_TARGET(AUTOTOOLS_TARGET_ALIAS, "${target}", [host env spec we built for]) fi fi if test -n "${host_cpu}" -a -n "${host_os}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_HOST_SHORT_ALIAS, "${host_cpu}-${host_os}", [host OS short spec we run on]) + NUT_REPORT_TARGET(AUTOTOOLS_HOST_SHORT_ALIAS, "${host_cpu}-${host_os}", [host OS short spec we run on]) fi if test -n "${build_cpu}" -a -n "${build_os}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_BUILD_SHORT_ALIAS, "${build_cpu}-${build_os}", [host OS short spec we built on]) + NUT_REPORT_TARGET(AUTOTOOLS_BUILD_SHORT_ALIAS, "${build_cpu}-${build_os}", [host OS short spec we built on]) fi if test -n "${target_cpu}" -a -n "${target_os}" ; then - AC_DEFINE_UNQUOTED(AUTOTOOLS_TARGET_SHORT_ALIAS, "${target_cpu}-${target_os}", [host OS short spec we built for]) + NUT_REPORT_TARGET(AUTOTOOLS_TARGET_SHORT_ALIAS, "${target_cpu}-${target_os}", [host OS short spec we built for]) +fi + +AC_MSG_CHECKING([whether compiler suggests a MULTIARCH value]) +dnl Recent GCC generally supports this call, although it often returns empty +dnl Some versions of CLANG also have it, others reject the unknown CLI switch +compiler_multiarch="`${CC} -print-multiarch 2>/dev/null`" || compiler_multiarch="" +if test -n "${compiler_multiarch}" ; then + NUT_REPORT_TARGET(MULTIARCH_TARGET_ALIAS, "${compiler_multiarch}", [host multiarch spec we build for (as suggested by compiler being used)]) +fi + +dnl ---------------------------------------------------------------------- +dnl Check the user and group to run as last, so we can use the paths configured +dnl above as data sources for default values if building an in-place replacement + +AC_MSG_CHECKING(if requested user to run as) +AC_ARG_WITH(user, + AS_HELP_STRING([--with-user=username], [user for programs started as root (${RUN_AS_USER})]), +[ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-user - see docs/configure.txt) + ;; + *) + RUN_AS_USER="${withval}" + nut_user_given=yes + AC_MSG_RESULT([specified]) + ;; + esac +], [ + nut_user_given=no + AC_MSG_RESULT([default]) +]) +NUT_REPORT_SETTING([User to run as], + RUN_AS_USER, "${RUN_AS_USER}", [User to switch to if started as root]) + +AC_MSG_CHECKING(if requested 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 (${RUN_AS_GROUP})]), +[ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-group - see docs/configure.txt) + ;; + *) + RUN_AS_GROUP="${withval}" + nut_group_given=yes + AC_MSG_RESULT([specified]) + ;; + esac +], [ + nut_group_given=no + AC_MSG_RESULT([default]) +]) +NUT_REPORT_SETTING([Group of user to run as], + RUN_AS_GROUP, "${RUN_AS_GROUP}", [Group membership of user to switch to if started as root]) + +dnl check that --with-user is given if --with-group is given. +if test "${nut_user_given}" = "yes" -a "${nut_group_given}" = "no"; then + AC_MSG_ERROR([If you specify --with-user, you also must specify --with-group]) +elif test "${nut_user_given}" = "no" -a "${nut_group_given}" = "yes"; then + AC_MSG_ERROR([If you specify --with-group, you also must specify --with-user]) fi dnl ---------------------------------------------------------------------- @@ -3031,6 +4848,9 @@ AC_SUBST(now) AC_SUBST(OS_NAME) AC_SUBST(TREE_VERSION) AC_SUBST(NUT_NETVERSION) +AC_SUBST(FORCE_NUT_VERSION) +AC_SUBST(NUT_SOURCE_GITREV) +AC_SUBST(NUT_SOURCE_GITREV_NUMERIC) AC_SUBST(LIBSSL_CFLAGS) AC_SUBST(LIBSSL_LIBS) AC_SUBST(LIBSSL_REQUIRES) @@ -3038,6 +4858,7 @@ AC_SUBST(LIBGD_CFLAGS) AC_SUBST(LIBGD_LDFLAGS) AC_SUBST(LIBNETSNMP_CFLAGS) AC_SUBST(LIBNETSNMP_LIBS) +AC_SUBST(LIBREGEX_LIBS) AC_SUBST(LIBUSB_CFLAGS) AC_SUBST(LIBUSB_LIBS) AC_SUBST(LIBNEON_CFLAGS) @@ -3052,23 +4873,48 @@ AC_SUBST(LIBMODBUS_CFLAGS) AC_SUBST(LIBMODBUS_LIBS) AC_SUBST(LIBIPMI_CFLAGS) AC_SUBST(LIBIPMI_LIBS) +AC_SUBST(LIBGPIO_CFLAGS) +AC_SUBST(LIBGPIO_LIBS) +AC_SUBST(LIBI2C_LIBS) AC_SUBST(DOC_BUILD_LIST) AC_SUBST(DOC_CHECK_LIST) AC_SUBST(LIBWRAP_CFLAGS) AC_SUBST(LIBWRAP_LIBS) AC_SUBST(LIBLTDL_CFLAGS) AC_SUBST(LIBLTDL_LIBS) +AC_SUBST(LIBSYSTEMD_CFLAGS) +AC_SUBST(LIBSYSTEMD_LIBS) +AC_SUBST(SYSTEMD_DAEMON_ARGS_UPSD) +AC_SUBST(SYSTEMD_DAEMON_TYPE_UPSD) +AC_SUBST(SYSTEMD_DAEMON_ARGS_UPSMON) +AC_SUBST(SYSTEMD_DAEMON_TYPE_UPSMON) +AC_SUBST(SYSTEMD_DAEMON_ARGS_DRIVER) +AC_SUBST(SYSTEMD_DAEMON_TYPE_DRIVER) +AC_SUBST(SYSTEMD_DAEMON_NOTIFYACCESS_DRIVER) +AC_SUBST(SYSTEMD_DAEMON_NOTIFYACCESS_UPSD) +AC_SUBST(SYSTEMD_DAEMON_NOTIFYACCESS_UPSMON) +AC_SUBST(SYSTEMD_DAEMON_WATCHDOG_UPSD) +AC_SUBST(SYSTEMD_DAEMON_WATCHDOG_UPSMON) +AC_SUBST(SYSTEMD_DAEMON_WATCHDOG_DRIVER) +AC_SUBST(SYSTEMD_TMPFILES_PROGRAM) AC_SUBST(DRIVER_BUILD_LIST) AC_SUBST(DRIVER_MAN_LIST) +AC_SUBST(DRIVER_MAN_LIST_PAGES) AC_SUBST(DRIVER_INSTALL_TARGET) AC_SUBST(NETLIBS) AC_SUBST(SERLIBS) +AC_SUBST(SEMLIBS) +AC_SUBST(PREFIX) AC_SUBST(PIDPATH) AC_SUBST(STATEPATH) +AC_SUBST(ALTPIDPATH) +dnl #Not in main codebase yet# AC_SUBST(ALTSTATEPATH) AC_SUBST(CONFPATH) +AC_SUBST(POWERDOWNFLAG) AC_SUBST(BINDIR) AC_SUBST(LIBDIR) -AC_SUBST(NUT_DATADIR, [`eval echo "${DATADIR}"`]) +AC_SUBST(PKGCONFIGDIR) +AC_SUBST(NUT_DATADIR, [`eval echo "${NUT_DATADIR}"`]) AC_SUBST(NUT_LIBEXECDIR, [`eval echo "${LIBEXECDIR}"`]) AC_SUBST(DRVPATH) AC_SUBST(SBINDIR) @@ -3080,6 +4926,7 @@ AC_SUBST(WORDS_BIGENDIAN) AC_SUBST(cgiexecdir) AC_SUBST(devddir) AC_SUBST(driverexecdir) +AC_SUBST(freebsdquirksdir) AC_SUBST(htmldir) AC_SUBST(pkgconfigdir) AC_SUBST(systemdsystemunitdir) @@ -3113,7 +4960,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 +4974,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"], + [*" "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"]) ]) ], @@ -3151,7 +5006,7 @@ AS_CASE(["${nut_enable_warnings}"], nut_enable_warnings="no" ] ) -AC_MSG_RESULT(["${nut_enable_warnings}"]) +AC_MSG_RESULT([${nut_enable_warnings}]) dnl # Nothing special for gcc - we tend to survive it with GNU standard >= 99 dnl # and fail with strict C standard. Suggestions welcome for "gcc-hard" to @@ -3180,7 +5035,26 @@ dnl # -Wno-padded -- NSPR and NSS headers get to issue lots of that dnl # -Wno-c++98-compat-pedantic -Wno-c++98-compat -- our C++ code uses nullptr dnl # as requested by newer linters, and C++98 does not. We require C++11 dnl # or newer anyway, and skip building C++ library and test otherwise. +dnl # -Wno-exit-time-destructors -- "(static) const something" items would be +dnl # de-allocated en-masse when the program exits, not GC'ed at run-time. +dnl # Oh well... +dnl # -Wno-fuse-ld-path -- not much in our control what recipes the autotools +dnl # on the build host generate... this tries to avoid failures due to: +dnl # clang-13: error: '-fuse-ld=' taking a path is deprecated. +dnl # Use '--ld-path=' instead [-Werror,-Wfuse-ld-path] +dnl # -Wno-unsafe-buffer-usage -- clang-16 introduced a check too smart for +dnl # its own good. It detects use of pointer aritmetics as arrays are +dnl # walked, which is indeed potentially dangerous. And also is nearly +dnl # unavoidable in C (at least not without major rewrites of the world). +dnl # -Wno-documentation-unknown-command -fcomment-block-commands=retval -- +dnl # some clang versions sanity-check Doxygen style comments, but do not +dnl # recognize "\retval" key word. These options try to both ignore the +dnl # problematic area, and to add it as recognized (TODO: scripted check +dnl # for abilities of the current build's compiler instead) dnl ### Special exclusion picks for clang-medium (same as hard, plus...): +dnl # -Wno-global-constructors -- using "const something" out of method context +dnl # potentially impacts start-up time and may be prone to race conditions +dnl # (for non-trivial interconnected objects), better be re-architected. dnl # -Wno-float-conversion -Wno-double-promotion -Wno-implicit-float-conversion dnl # -- reduce noise due to floating-point literals like "3.14" being a C dnl # double type (a "3.14f" literal is a C float) cast implicitly into @@ -3204,22 +5078,29 @@ dnl # versions care less about this situation). dnl # -Wno-disabled-macro-expansion -- some system definitions of strncmp() dnl # and other routines are in fact recursive macros. The -Weverything dnl # mode of clang(-3.4) disables their handling, unless told otherwise. +dnl # -Wno-incompatible-function-pointer-types-strict -- some system definitions +dnl # of methods for signals are not compatible with even those systems' +dnl # definitions of default signals, e.g. void vs. int arguments: +dnl # #define SIG_IGN (void (*)())1 +dnl # extern void (*signal(int, void (*)(int)))(int); +dnl # (NUT signal handler methods do have the int signal number) +dnl # This is currently quiesced for clang-17; better solutions are welcome. AS_CASE(["${nut_enable_warnings}"], [all], [ CFLAGS="${CFLAGS} -Wall" CXXFLAGS="${CXXFLAGS} -Wall" ], [clang-hard], [ - CFLAGS="${CFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -Wno-cast-qual -pedantic" - CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-c++98-compat" + CFLAGS="${CFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -fcomment-block-commands=retval -Wno-documentation-unknown-command -Wno-cast-qual -pedantic -Wno-fuse-ld-path -Wno-unsafe-buffer-usage" + CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -fcomment-block-commands=retval -Wno-documentation-unknown-command -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-exit-time-destructors -Wno-fuse-ld-path -Wno-unsafe-buffer-usage" ], [clang-medium], [ - CFLAGS="${CFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -Wno-cast-qual -pedantic -Wno-float-conversion -Wno-double-promotion -Wno-implicit-float-conversion -Wno-conversion -Wno-incompatible-pointer-types-discards-qualifiers" - CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-c++98-compat" + CFLAGS="${CFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -fcomment-block-commands=retval -Wno-documentation-unknown-command -Wno-cast-qual -pedantic -Wno-fuse-ld-path -Wno-unsafe-buffer-usage -Wno-float-conversion -Wno-double-promotion -Wno-implicit-float-conversion -Wno-conversion -Wno-incompatible-pointer-types-discards-qualifiers -Wno-incompatible-function-pointer-types-strict" + CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wno-system-headers -Wall -Wextra -Weverything -Wno-disabled-macro-expansion -Wno-unused-macros -Wno-reserved-id-macro -Wno-padded -Wno-documentation -fcomment-block-commands=retval -Wno-documentation-unknown-command -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-exit-time-destructors -Wno-global-constructors -Wno-fuse-ld-path -Wno-unsafe-buffer-usage" ], [clang-minimal], [ - CFLAGS="${CFLAGS} -ferror-limit=0 -Wall -Wextra -Wno-documentation" - CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wall -Wextra -Wno-documentation" + CFLAGS="${CFLAGS} -ferror-limit=0 -Wall -Wextra -Wno-documentation -Wno-documentation-unknown-command -fcomment-block-commands=retval" + CXXFLAGS="${CXXFLAGS} -ferror-limit=0 -Wall -Wextra -Wno-documentation -Wno-documentation-unknown-command -fcomment-block-commands=retval" ], [gcc-legacy], [CFLAGS="${CFLAGS} -Wall -Wsign-compare"], [gcc-minimal], [ @@ -3244,6 +5125,23 @@ AS_CASE(["${nut_enable_warnings}"], ] ) +AS_IF([test x"${nut_enable_warnings}" != xno || test x"${nut_enable_Werror}" != xno], + [AS_IF([test "x${CLANGCC}" = "xyes" || test "x${GCC}" = "xyes"], + [AS_CASE(["${target_os}"], + [*mingw*], [ + AC_MSG_NOTICE( +[GCC on WIN32 builds warns about '%lld' etc. via PRId64 etc. +even though modern libraries support long-long printing +in practice (older ones did not - hence the warning). +Forcing "-Wno-format" into warnings options. +]) + CFLAGS="${CFLAGS} -Wno-format" + CXXFLAGS="${CXXFLAGS} -Wno-format" + ] + ) + ]) +]) + AC_MSG_CHECKING([whether to make warnings fatal]) AS_CASE(["${nut_enable_Werror}"], [yes|auto], [ @@ -3255,7 +5153,7 @@ AS_CASE(["${nut_enable_Werror}"], CXXFLAGS="${CXXFLAGS} -Wno-error" ] ) -AC_MSG_RESULT(["${nut_enable_Werror}"]) +AC_MSG_RESULT([${nut_enable_Werror}]) dnl Some compilers (e.g. older clang-3.4) have issues with built-in methods dnl that are implemented as macros in system headers -- but only for some @@ -3387,38 +5285,196 @@ AS_IF([test -n "${ac_abs_top_builddir}" && test -d "${ac_abs_top_builddir}"], TOP_BUILDDIR="`cd "$TOP_BUILDDIR" && pwd`" || AC_MSG_ERROR([Can not detect TOP_BUILDDIR])] )] ) -AC_MSG_RESULT(["${TOP_BUILDDIR}"]) +dnl Quoted in case someone copy-pastes this path and it has whitespaces: +AC_MSG_RESULT(['${TOP_BUILDDIR}']) ABS_TOP_BUILDDIR="`cd "${TOP_BUILDDIR}" && pwd`" || AC_MSG_ERROR([Can not detect ABS_TOP_BUILDDIR]) ABS_TOP_SRCDIR="`cd "${abs_srcdir}" && pwd`" || AC_MSG_ERROR([Can not detect ABS_TOP_SRCDIR]) AM_CONDITIONAL([BUILDING_IN_TREE], [test "${ABS_TOP_BUILDDIR}" = "${ABS_TOP_SRCDIR}"]) -AC_MSG_CHECKING([whether to customize ${TOP_BUILDDIR}/scripts/systemd/nut-common.tmpfiles.in for this system]) +dnl When building ON Windows (mingw/MSYS2, cygwin, etc.) fudge these +dnl path strings back to what native OS methods would recognize. +AS_CASE([${target_os}], + [*mingw*], [ + dnl Cygwin path resolver + AC_CHECK_TOOL([CYGPATH], [cygpath], [none]) + AS_IF([test "x${CYGPATH}" != "xnone"], [ + tmp="`${CYGPATH} -m "${ABS_TOP_BUILDDIR}" | sed -e 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" + tmp="`${CYGPATH} -m "${ABS_TOP_SRCDIR}" | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" + ],[ + dnl MSYS pwd with -W option to resolve + AC_CHECK_TOOL([PWD], [pwd], [none]) + AS_IF([test "x${PWD}" != "xnone"], [ + tmp="`(cd "${ABS_TOP_BUILDDIR}" && ${PWD} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_BUILDDIR="$tmp" + tmp="`(cd "${ABS_TOP_SRCDIR}" && ${PWD} -W) | sed 's,/,\\\\\\\\,g'`" && test -n "$tmp" && test -d "$tmp" && ABS_TOP_SRCDIR="$tmp" + ]) + ]) +]) + +dnl Use these at best for tests (e.g. nutconf), not production code: +AC_DEFINE_UNQUOTED([ABS_TOP_SRCDIR], ["${ABS_TOP_SRCDIR}"], [NUT source directory when the build was configured]) +AC_DEFINE_UNQUOTED([ABS_TOP_BUILDDIR], ["${ABS_TOP_BUILDDIR}"], [NUT build directory when the build was configured]) + +AC_MSG_CHECKING([whether to customize ${TOP_BUILDDIR}/scripts/systemd/nut-common-tmpfiles.conf.in for this system]) +dnl TOTHINK: Some distributions make the directories below owned +dnl by "root:${RUN_AS_GROUP}" with 77x permissions. Is it safer?.. AS_IF([test -n "$systemdtmpfilesdir"], [mkdir -p "${TOP_BUILDDIR}"/scripts/systemd - cat > "${TOP_BUILDDIR}"/scripts/systemd/nut-common.tmpfiles.in << EOF -# State file (e.g. upsd to driver) and pidfile location for NUT: -d @STATEPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - -X @STATEPATH@/nut + cat > "${TOP_BUILDDIR}"/scripts/systemd/nut-common-tmpfiles.conf.in << EOF +# Network UPS Tools (NUT) systemd integration +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + +# See also: https://github.com/networkupstools/nut/wiki/Technicalities:-Work-with-PID-and-state-file-paths#pidpath-altpidpath-statepath +# State file (e.g. upsd to driver pipes) and PID file location for NUT: +d @STATEPATH@ 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +# Default PIPEFN and LOCKFN locations per upssched.conf: +d @STATEPATH@/upssched 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @STATEPATH@ EOF AS_IF([test "$STATEPATH" != "$PIDPATH"], - [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common.tmpfiles.in << EOF -d @PIDPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - -X @PIDPATH@/nut -EOF]) + [AS_CASE(["${PIDPATH}"], + [*/run|*/tmp|*/shm], [], dnl Do not intrude into system paths; TODO: add more if appropriate for some Linux distro + [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common-tmpfiles.conf.in << EOF +# Primarily used by upsmon and upslog, possibly running as root: +d @PIDPATH@ 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @PIDPATH@ +EOF +])]) AS_IF([test -n "$ALTPIDPATH" && test "$STATEPATH" != "$ALTPIDPATH" && test "$PIDPATH" != "$ALTPIDPATH"], - [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common.tmpfiles.in << EOF -d @ALTPIDPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - -X @ALTPIDPATH@/nut + [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common-tmpfiles.conf.in << EOF +# Should be used as upsd and driver PID file location for NUT +# (if ALTPIDPATH differs from STATEPATH): +d @ALTPIDPATH@ 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @ALTPIDPATH@ EOF]) + dnl Generally added to support some forks AS_IF([test -n "$ALTSTATEPATH" && test "$STATEPATH" != "$ALTSTATEPATH" && test "$ALTSTATEPATH" != "$ALTPIDPATH" && test "$PIDPATH" != "$ALTSTATEPATH"], - [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common.tmpfiles.in << EOF -d @ALTSTATEPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - -X @ALTSTATEPATH@/nut + [cat >> "${TOP_BUILDDIR}"/scripts/systemd/nut-common-tmpfiles.conf.in << EOF +# Some NUT variants also maintain an ALTSTATEPATH: +d @ALTSTATEPATH@ 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @ALTSTATEPATH@ EOF]) ]) AC_MSG_RESULT([done]) +dnl # ccache versions 4.5 and newer support namespacing of the objects +dnl # to facilitate more targeted eviction with --evict-namespace and +dnl # perhaps --evict-older-than options. Here we bolt a namespace with +dnl # NUT as the project and CPU architecture for resulting binaries; +dnl # maybe we might use distro as well but some overlaps may be possible +dnl # that result in same objects for different-looking build roots. +dnl # Note this is enabled by default (explicit --without-... disables it). +dnl # Has practical effect if NUT_AM_MAKE_CAN_EXPORT test is successful. +AS_IF([test x"${CCACHE_NAMESPACE}" = x], [ + CCACHE_NAMESPACE="nut" + dnl # Variables in this list are defined earlier in the script + for T in "${compiler_multiarch}" "${target_alias}" "${target}" "${target_cpu}-${target_os}" ; do + if test x"$T" != x -a x"$T" != x- ; then + CCACHE_NAMESPACE="${CCACHE_NAMESPACE}:${T}" + break + fi + done + unset T +]) +AC_ARG_WITH(CCACHE_NAMESPACE, + AS_HELP_STRING([--with-CCACHE_NAMESPACE=namespace], [which ccache namespace to use for built binaries; typically nut:${autotools_target})]), +[ + case "${withval}" in + no) + CCACHE_NAMESPACE="" + ;; + yes) # Use user envvar or calculation above + ;; + *) + CCACHE_NAMESPACE="${withval}" + ;; + esac +], []) +NUT_REPORT_TARGET(CCACHE_NAMESPACE, "${CCACHE_NAMESPACE}", [ccache namespace tag (if ccache is used and new enough)]) +AS_IF([test x"$CCACHE_NAMESPACE" != x -a x"$NUT_AM_MAKE_CAN_EXPORT" != x], [ + AC_MSG_WARN([CCACHE_NAMESPACE setting may have no effect: this make implementation seems to not support "export VAR=VAL" syntax]) +]) + +dnl # Mark it as a "precious variable", for more details see +dnl # https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Setting-Output-Variables.html +AC_ARG_VAR(CCACHE_NAMESPACE) + +dnl # Also list some other ccache options which ci_build.sh can fiddle with. +dnl # While that script "exports" them so it can call both configuration and +dnl # the build, their values may be unknown when a developer re-runs "make". +dnl # Normally most of the options would persist under $CCACHE_DIR/ccache.conf +dnl # and we would not pass them via envvars. +AC_ARG_VAR(CCACHE_BASEDIR) +AC_ARG_VAR(CCACHE_DIR) +AC_ARG_VAR(CCACHE_PATH) + +PATH_DURING_CONFIGURE="$PATH" +AC_SUBST(PATH_DURING_CONFIGURE) + +dnl Some binaries, like CPPUNIT tests, have similar flags already added +dnl We might wipe their specific options below if consistently applying +dnl debug-friendly options to everything +AC_MSG_NOTICE([CONFIG_CFLAGS='${CONFIG_CFLAGS}']) +AC_MSG_NOTICE([CONFIG_CXXFLAGS='${CONFIG_CXXFLAGS}']) +AC_MSG_CHECKING([whether to enable debug info in all NUT binaries]) +nut_with_debuginfo_C="${nut_with_debuginfo}" +nut_with_debuginfo_CXX="${nut_with_debuginfo}" +AS_CASE(["${CONFIG_CFLAGS}"], + [*-O*|*-g*], [ + AS_IF([test x"${nut_with_debuginfo_C}" = xauto], [ + nut_with_debuginfo_C="Related settings already specified by caller CFLAGS, not changing anything" + ]) + ], + [ dnl No competing options are provided + AS_IF([test x"${nut_with_debuginfo_C}" = xauto], [nut_with_debuginfo_C="yes"]) + ]) + +AS_CASE(["${CONFIG_CXXFLAGS}"], + [*-O*|*-g*], [ + AS_IF([test x"${nut_with_debuginfo_CXX}" = xauto], [ + nut_with_debuginfo_CXX="Related settings already specified by caller CXXFLAGS, not changing anything" + ]) + ], + [ dnl No competing options are provided + AS_IF([test x"${nut_with_debuginfo_CXX}" = xauto], [nut_with_debuginfo_CXX="yes"]) + ]) + +AS_CASE(["${nut_with_debuginfo_C}"], + [yes], [ + AS_IF([test x"${CLANGCC}" = x"yes" -o x"${GCC}" = x"yes"], [ + CFLAGS="${CFLAGS} -O0 -g3 -gdwarf-2" + ],[nut_with_debuginfo_C="Unknown C compiler, not adding options"] + )], + dnl # [no]: By default we do not add debug info + [legacy], [ + dnl # Apply legacy defaults if no flags were specified by caller or detected by autoconf + AS_IF([test x"${CFLAGS_AFTER_ACPROG}" = x], [CFLAGS="-O ${CFLAGS}"]) + ] +) + +AS_CASE(["${nut_with_debuginfo_CXX}"], + [yes], [ + AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], [ + dnl Where we can enable debug, minimize the optimizations + CXXFLAGS="${CXXFLAGS} -O0 -g3 -gdwarf-2" + dnl Use same settings for CPPUNIT tests (they bump their own by default) + CPPUNIT_NUT_CXXFLAGS="" + ],[nut_with_debuginfo_CXX="Unknown C++ compiler, not adding options"] + )], + dnl # [no]: By default we do not add debug info + [legacy], [ + dnl # Apply legacy defaults if no flags were specified by caller or detected by autoconf + AS_IF([test x"${CXXFLAGS_AFTER_ACPROG}" = x], [CXXFLAGS="-O ${CXXFLAGS}"]) + ] +) + +AC_MSG_RESULT([C: ${nut_with_debuginfo_C}; C++: ${nut_with_debuginfo_CXX}]) + +dnl Only in the end, do you understand... +AC_SUBST(CPPUNIT_NUT_CXXFLAGS) + AC_MSG_NOTICE([Generating "data" files from templates, see below for executable scripts]) AC_CONFIG_FILES([ clients/Makefile @@ -3436,13 +5492,13 @@ AC_CONFIG_FILES([ docs/man/Makefile drivers/Makefile include/Makefile - lib/libupsclient-config lib/libupsclient.pc lib/libnutclient.pc lib/libnutclientstub.pc lib/libnutscan.pc lib/Makefile scripts/Aix/nut-aix.spec + scripts/RedHat/ups scripts/augeas/Makefile scripts/augeas/nutnutconf.aug scripts/augeas/nutupsdconf.aug @@ -3456,11 +5512,14 @@ AC_CONFIG_FILES([ scripts/hotplug/Makefile scripts/hotplug/libhidups scripts/HP-UX/nut.psf + scripts/installer/Makefile scripts/python/Makefile + scripts/python/module/Makefile scripts/python/module/PyNUT.py + scripts/python/module/setup.py scripts/upsdrvsvcctl/Makefile scripts/systemd/Makefile - scripts/systemd/nut-common.tmpfiles + scripts/systemd/nut-common-tmpfiles.conf scripts/systemd/nut-driver@.service scripts/systemd/nut-monitor.service scripts/systemd/nut-server.service @@ -3482,22 +5541,29 @@ AC_CONFIG_FILES([ scripts/udev/nut-ipmipsu.rules scripts/ufw/Makefile scripts/ufw/nut.ufw.profile + scripts/Windows/Makefile + scripts/Windows/Installer/NUT-Installer.xml scripts/Makefile server/Makefile tools/Makefile tools/nut-scanner/Makefile tools/nutconf/Makefile tests/Makefile + tests/NIT/Makefile Makefile ]) AC_MSG_NOTICE([Generating templated script files that should be marked executable]) m4_foreach_w([SCRIPTFILE], [ + lib/libupsclient-config scripts/Aix/nut.init scripts/DMF/jsonify-mib.py scripts/DMF/xmlify-mib.py scripts/HP-UX/postinstall - scripts/python/app/NUT-Monitor + scripts/RedHat/upsd + scripts/RedHat/upsmon + 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 @@ -3535,6 +5601,27 @@ m4_foreach_w([SCRIPTFILE], [ ) ]) +dnl Define this before AC_OUTPUT(), so not inside the report routine below: +AM_CONDITIONAL(KEEP_NUT_REPORT, test x"${nut_enable_keep_nut_report_feature-}" = xyes) + +dnl Prints a long list of files generated from templates... AC_OUTPUT +dnl Normally the latest action, for the summary to be visible: +NUT_REPORT_COMPILERS NUT_PRINT_FEATURE_REPORT + +dnl Stopping short of patching the unknown script, we can warn about the issue +dnl (visibly as it impacts next activities of the caller): +AS_IF([test -s "${ABS_TOP_SRCDIR}/install-sh" && grep -w MKDIRPROG "${ABS_TOP_SRCDIR}/install-sh" >/dev/null], + [AS_IF([grep -v '#' "${ABS_TOP_SRCDIR}/install-sh" | grep -E '\$mkdirprog.*-p' >/dev/null], + [], + [AC_MSG_WARN([=====================================================]) + AC_MSG_WARN([Your system provided (or NUT tarball included) an]) + AC_MSG_WARN(['install-sh' implementation which is not safe for]) + AC_MSG_WARN([parallel installs; export MKDIRPROG='mkdir -p']) + AC_MSG_WARN([may help, otherwise run 'make install' sequentially.]) + AC_MSG_WARN([This should not impact parallel builds.]) + AC_MSG_WARN([=====================================================]) + ]) +]) diff --git a/data/Makefile.am b/data/Makefile.am index 99098184cb..29c440bac2 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -6,15 +6,18 @@ dist_data_DATA = cmdvartab nodist_data_DATA = driver.list EXTRA_DIST = evolution500.seq epdu-managed.dev -# NOTE: Due to portability, we do not use a GNU percent-wildcard extension: +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): #%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) -# $(MAKE) -s -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC_ONE="$<" SPELLCHECK_DIR="$(srcdir)" $@ +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ cmdvartab-spellchecked: cmdvartab Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) - $(MAKE) -s -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC_ONE="$<" SPELLCHECK_DIR="$(srcdir)" $@ + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ spellcheck spellcheck-interactive spellcheck-sortdict: - $(MAKE) -f $(top_builddir)/docs/Makefile SPELLCHECK_SRC="cmdvartab" SPELLCHECK_DIR="$(srcdir)" $@ + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="cmdvartab" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ MAINTAINERCLEANFILES = Makefile.in .dirstamp CLEANFILES = *.pdf *.html *-spellchecked diff --git a/data/cmdvartab b/data/cmdvartab index 5f322c6037..9a1d9ab3a9 100644 --- a/data/cmdvartab +++ b/data/cmdvartab @@ -175,20 +175,29 @@ VARDESC outlet.2.autoswitch.charge.low "Remaining battery level to power off thi VARDESC outlet.2.delay.shutdown "Interval to wait before shutting down this outlet (seconds)" VARDESC outlet.2.delay.start "Interval to wait before restarting this outlet (seconds)" +VARDESC device.part "Device Part Number" + +VARDESC server.info "Server information" +VARDESC server.version "Server version" + VARDESC driver.name "Driver name" +VARDESC driver.debug "Current debug verbosity level of the driver program" +# Note: normally a `drivername -k` call is used during shutdowns, +# and at that time the daemon instance of the driver must be stopped: +VARDESC driver.flag.allow_killpower "Safety flip-switch to allow the driver daemon to send UPS shutdown command (accessible via driver.killpower)" VARDESC driver.version "Driver version - NUT release" VARDESC driver.version.internal "Internal driver version" VARDESC driver.version.usb "USB library version" -VARDESC device.part "Device Part Number" - # FIXME: driver.parameter and driver.flag can have many possible members # # VARDESC driver.parameter.[[:alpha:]]+ "Driver parameter: " # VARDESC driver.flag.[[:alpha:]]+ "Driver flag: " -VARDESC server.info "Server information" -VARDESC server.version "Server version" +CMDDESC driver.killpower "Tell the driver daemon to initiate UPS shutdown; should be unlocked with driver.flag.allow_killpower option or variable setting" +CMDDESC driver.reload "Reload running driver configuration from the file system (only works for changes in some options)" +CMDDESC driver.reload-or-error "Reload running driver configuration from the file system (only works for changes in some options); return an error if something changed and could not be applied live (so the caller can restart it with new options)" +CMDDESC driver.reload-or-exit "Reload running driver configuration from the file system (only works for changes in some options); exit the running driver if something changed and could not be applied live (so service management framework can restart it with new options)" CMDDESC load.off "Turn off the load immediately" CMDDESC load.on "Turn on the load immediately" diff --git a/data/driver.list.in b/data/driver.list.in index b9cd0a3908..32b795cb2e 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,39 +46,57 @@ "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" "AEG Power Solutions" "ups" "2" "PROTECT HOME" "" "blazer_ser or blazer_usb" -"AEG Power Solutions" "ups" "3" "PROTECT NAS" "" "usbhid-ups" -"AEG Power Solutions" "ups" "3" "PROTECT B" "" "usbhid-ups" +"AEG Power Solutions" "ups" "3" "PROTECT NAS" "USB" "usbhid-ups" +"AEG Power Solutions" "ups" "3" "PROTECT B" "USB" "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" "APC" "ups" "1" "Matrix-UPS" "" "apcsmart" "APC" "ups" "1" "Smart-UPS" "" "apcsmart" "APC" "ups" "1" "Smart-UPS SMT/SMX/SURTD" "Microlink models with RJ45 socket - they *require* AP9620 SmartSlot expansion card and smart cable" "apcsmart" -"APC" "ups" "2" "Back-UPS Pro USB" "USB" "usbhid-ups" -"APC" "ups" "2" "Back-UPS (USB)" "USB" "usbhid-ups" -"APC" "ups" "2" "Back-UPS CS USB" "USB" "usbhid-ups" -"APC" "ups" "2" "Back-UPS RS USB" "USB" "usbhid-ups" -"APC" "ups" "2" "Back-UPS LS USB" "USB" "usbhid-ups" -"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" "Smart-UPS (USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS Pro USB" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS (USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS CS USB" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS RS USB" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS LS USB" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS ES/CyberFort 350" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS BF500" "USB" "usbhid-ups" +"APC" "ups" "3" "BACK-UPS XS LCD" "USB" "usbhid-ups" +"APC" "ups" "3" "Back-UPS XS 1000M (Back-UPS Pro 1000, Model BX1000M)" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/139 +"APC" "ups" "3" "SMC2200BI-BR" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/557 +"APC" "ups" "3" "Smart-UPS (USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Smart-UPS 750 (SMT750I, USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Smart-UPS 1500 (SMT1500I, USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Smart-UPS X 750 (SMX750I, USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "Smart-UPS X 1500 (SMX1500I, USB)" "USB" "usbhid-ups" +"APC" "ups" "3" "SMC2200BI-BR" "USB" "apc_modbus" # https://github.com/networkupstools/nut/issues/557 +"APC" "ups" "3" "Smart-UPS (USB)" "USB" "apc_modbus" +"APC" "ups" "3" "Smart-UPS 750 (SMT750I, USB)" "USB" "apc_modbus" +"APC" "ups" "3" "Smart-UPS 1500 (SMT1500I, USB)" "USB" "apc_modbus" +"APC" "ups" "3" "Smart-UPS X 750 (SMX750I, USB)" "USB" "apc_modbus" +"APC" "ups" "3" "Smart-UPS X 1500 (SMX1500I, USB)" "USB" "apc_modbus" +"APC" "ups" "3" "CS500" "USB" "usbhid-ups (limited data available)" # https://github.com/networkupstools/nut/issues/1776#issuecomment-1377784584 "APC" "ups" "1" "Back-UPS" "940-0095A/C cables" "genericups upstype=1" "APC" "ups" "1" "Back-UPS" "940-0020B/C cables" "genericups upstype=2" "APC" "ups" "1" "Back-UPS" "940-0023A cable" "genericups upstype=9" "APC" "ups" "1" "Back-UPS Office" "940-0119A cable" "genericups upstype=12" "APC" "ups" "1" "Back-UPS RS 500" "custom non-USB cable" "genericups upstype=20" +"APC" "ups" "3" "Smart-UPS SUA 1000" "" "snmp-ups" +"APC" "ups" "3" "Smart-UPS 3000" "" "snmp-ups" "APC" "ups" "3" "Smart-UPS RT XL" "AP9618 SNMP monitoring card" "snmp-ups" "APC" "ups" "3" "(various)" "AP9618 SNMP monitoring card" "snmp-ups" "APC" "ups" "3" "(various)" "AP9630 SNMP monitoring card" "snmp-ups privProtocol=AES" @@ -92,10 +111,12 @@ "Apollo" "ups" "1" "1000A" "" "genericups upstype=4" "Apollo" "ups" "1" "1000F" "" "genericups upstype=4" -"Apollo" "ups" "2" "850VA" "" "blazer_usb" +"Apollo" "ups" "2" "850VA" "USB" "blazer_usb" "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" @@ -106,7 +127,7 @@ "ARTronic" "ups" "2" "ARTon Platinium Combo 3.1 10/15/20 kVA" "USB" "blazer_usb" "ARTronic" "ups" "2" "ARTon Platinium RT 1/2/3/6/10 kVA" "USB" "blazer_usb" -"Armac" "ups" "2" "R/2000I/PSW" "(USB ID 0925:1234)" "nutdrv_qx" +"Armac" "ups" "2" "R/2000I/PSW and PF1 series" "(USB ID 0925:1234)" "nutdrv_qx" "ASEM SPA" "ups" "5" "PB1300 UPS" "i2c" "asem" @@ -128,6 +149,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)" @@ -138,23 +160,23 @@ "Belkin" "ups" "1" "Home Office F6H350-SER" "" "genericups upstype=7" "Belkin" "ups" "1" "Home Office F6H500-SER" "" "genericups upstype=7" "Belkin" "ups" "1" "Home Office F6H650-SER" "" "genericups upstype=7" -"Belkin" "ups" "2" "F6H375-USB" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" +"Belkin" "ups" "3" "F6H375-USB" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "F6H375-USB" "USB (2007 models, vendor id: 0665)" "blazer_usb" -"Belkin" "ups" "2" "Office Series F6C550-AVR" "USB" "usbhid-ups" +"Belkin" "ups" "3" "Office Series F6C550-AVR" "USB" "usbhid-ups" "Belkin" "ups" "3" "Regulator PRO-USB" "USB (~2000, product id: 0f51)" "usbhid-ups" "Belkin" "ups" "2" "Regulator Pro" "F6C525-SER, F6C625-SER" "belkin" "Belkin" "ups" "1" "Resource" "" "genericups upstype=4" "Belkin" "ups" "2" "Small Enterprise F6C1500-TW-RK" "serial port" "belkin" -"Belkin" "ups" "2" "Small Enterprise F6C1500-TW-RK" "USB" "usbhid-ups" -"Belkin" "ups" "2" "Universal UPS F6C100-UNV" "USB" "usbhid-ups" +"Belkin" "ups" "3" "Small Enterprise F6C1500-TW-RK" "USB" "usbhid-ups" +"Belkin" "ups" "3" "Universal UPS F6C100-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C120-UNV" "serial port" "belkinunv" -"Belkin" "ups" "2" "Universal UPS F6C120-UNV" "USB" "usbhid-ups" +"Belkin" "ups" "3" "Universal UPS F6C120-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C800-UNV" "serial port" "belkinunv" -"Belkin" "ups" "2" "Universal UPS F6C800-UNV" "USB" "usbhid-ups" +"Belkin" "ups" "3" "Universal UPS F6C800-UNV" "USB" "usbhid-ups" "Belkin" "ups" "1" "Universal UPS F6C1100-UNV" "serial port (<= 2005 models)" "belkinunv" -"Belkin" "ups" "2" "Universal UPS F6C1100-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" +"Belkin" "ups" "3" "Universal UPS F6C1100-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "Universal UPS F6C1100-UNV" "USB (2007 models, vendor id: 0665)" "blazer_usb" -"Belkin" "ups" "2" "Universal UPS F6C1200-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" +"Belkin" "ups" "3" "Universal UPS F6C1200-UNV" "USB (<= 2005 models, vendor id: 050d)" "usbhid-ups" "Belkin" "ups" "2" "Universal UPS F6C1200-UNV" "USB (2007 models, vendor id: 0665)" "blazer_usb" "Belkin" "ups" "2" "Universal UPS F6H350deUNV" "serial port" "blazer_ser" "Belkin" "ups" "2" "Universal UPS F6H350ukUNV" "serial port" "blazer_ser" @@ -172,13 +194,13 @@ "Best Power" "ups" "1" "Micro-Ferrups" "" "bestuferrups" "Best Power" "ups" "1" "Fortress/Ferrups" "f-command support" "bestfcom" -"Borri" "ups" "2" "B400-010-B/B400-020-B/B400-030-B/B400-010-C/B400-020-C/B400-030-C" "" "blazer_usb" -"Borri" "ups" "2" "B400-R010-B/B400-R020-B/B400-R030-B/B400-R010-C/B400-R020-C/B400-R030-C" "" "blazer_usb" -"Borri" "ups" "2" "B500-060-B/B500-100-B/B500-060-C/B500-100-C" "" "blazer_usb" -"Borri" "ups" "2" "B500-R060-B/B500-R100-B" "" "blazer_usb" -"Borri" "ups" "2" "B500EVO-100-B/B500EVO-200-B" "" "blazer_usb" +"Borri" "ups" "2" "B400-010-B/B400-020-B/B400-030-B/B400-010-C/B400-020-C/B400-030-C" "USB" "blazer_usb" +"Borri" "ups" "2" "B400-R010-B/B400-R020-B/B400-R030-B/B400-R010-C/B400-R020-C/B400-R030-C" "USB" "blazer_usb" +"Borri" "ups" "2" "B500-060-B/B500-100-B/B500-060-C/B500-100-C" "USB" "blazer_usb" +"Borri" "ups" "2" "B500-R060-B/B500-R100-B" "USB" "blazer_usb" +"Borri" "ups" "2" "B500EVO-100-B/B500EVO-200-B" "USB" "blazer_usb" -"CABAC" "ups" "2" "UPS-1700DV2" "" "blazer_usb" +"CABAC" "ups" "2" "UPS-1700DV2" "USB" "blazer_usb" "Chloride" "ups" "2" "Desk Power 650" "serial port" "blazer_ser" @@ -191,9 +213,15 @@ "Compaq" "ups" "4" "R3000 XR" "" "bcmxcp" "Compaq" "ups" "4" "R5500 XR" "" "bcmxcp" -"COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K" "" "blazer_usb" -"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" +"COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K" "USB" "blazer_usb" +"COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K EC" "USB" "blazer_usb" +"COVER ENERGY SA" "ups" "2" "COVER PRM 6K/10K PR" "USB" "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 Energy" "ups" "3" "Models with USB ID 0483:A430" "USB" "usbhid-ups" # https://alioth-lists.debian.net/pipermail/nut-upsdev/2024-February/007966.html "Cyber Power Systems" "ups" "1" "550SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "725SL" "" "genericups upstype=7" @@ -212,28 +240,39 @@ "Cyber Power Systems" "ups" "1" "CPS900AVR" "" "powerpanel" # http://www.cyberpowersystems.com/products/ups-systems/other-ups/CPS900AVR.html "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" "2" "CP1350AVRLCD" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" -"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" +"Cyber Power Systems" "ups" "3" "AE550" "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" "3" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 +"Cyber Power Systems" "ups" "3" "CP1350AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "CP1500AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "CP850PFCLCD" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/605 +"Cyber Power Systems" "ups" "3" "CP1350PFCLCD" "USB" "usbhid-ups" # https://alioth-lists.debian.net/pipermail/nut-upsuser/2023-October/013441.html +"Cyber Power Systems" "ups" "3" "CP1500PFCLCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/cp1500pfclcd/ https://github.com/networkupstools/nut/issues/520 +"Cyber Power Systems" "ups" "3" "CPJ500" "USB" "usbhid-ups" # https://www.cyberpower.com/jp/ja/product/sku/cpj500#downloads https://github.com/networkupstools/nut/issues/1403 +"Cyber Power Systems" "ups" "3" "CP900AVR" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "CPS685AVR" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "CPS800AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "OL3000RMXL2U" "" "powerpanel" "Cyber Power Systems" "ups" "2" "PR3000E" "" "powerpanel" -"Cyber Power Systems" "ups" "2" "Value 1500ELCD-RU" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "Value 400E" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "Value 600E" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "Value 800E" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "Value 1500ELCD-RU" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "Value 400E" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "Value 600E" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "Value 800E" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "VP1200ELCD (ValueII_1200E)" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "CP 1500C" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "CP1000PFCLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "CP1500EPFCLCD" "USB" "usbhid-ups" # http://www.cyberpower-eu.com/products/ups_systems/pfc-sinewave/cp1500epfclcd.htm "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" "USB" "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,19 +313,28 @@ "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" "Dynamix" "ups" "2" "650VA/1000VA" "USB" "blazer_usb langid_fix=0x0409" "Dynex" "ups" "1" "975AVR" "" "genericups upstype=7" -"Dynex" "ups" "2" "DX-800U" "USB" "usbhid-ups" +"Dynex" "ups" "3" "DX-800U" "USB" "usbhid-ups" -"Eaton" "ups" "5" "3S" "" "usbhid-ups" +"Eaton" "ups" "5" "3S" "USB" "usbhid-ups" "Eaton" "ups" "5" "Protection Station 500/650/800 VA" "USB" "usbhid-ups" "Eaton" "ups" "5" "Ellipse ECO 650/800/1200/1600 VA" "USB" "usbhid-ups" "Eaton" "ups" "5" "Ellipse ASR USBS 600/750/1000/1500 VA" "USB cable" "usbhid-ups" @@ -324,12 +372,13 @@ "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" "Eaton" "ups" "5" "EX RT (XML/HTTP)" "NMC Transverse card (ref 66074)" "netxml-ups (experimental)" "Eaton" "ups" "5" "EX RT (SNMP)" "NMC Transverse card (ref 66074)" "snmp-ups (experimental)" -"Eaton" "ups" "5" "E Series NV UPS 400-2000 VA" "" "blazer_usb" +"Eaton" "ups" "5" "E Series NV UPS 400-2000 VA" "USB" "blazer_usb" "Eaton" "ups" "5" "E Series DX UPS 1-20 kVA" "" "blazer_ser" # http://www.eaton.com/Eaton/ESeriesUPS/DXUPS/ "Eaton" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" "Eaton" "ups" "5" "BladeUPS (SNMP)" "ConnectUPS Web/SNMP Card" "snmp-ups (experimental)" @@ -338,15 +387,17 @@ "Eaton" "ups" "5" "various models (SNMP mode)" "NMC Minislot (ref 66102)" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (XML/HTTP mode)" "SNMP/Web Minislot card (ref 66244)" "netxml-ups (experimental)" "Eaton" "ups" "5" "various models (SNMP mode)" "SNMP/Web Minislot card (ref 66244)" "snmp-ups (experimental)" +"Eaton" "ups" "5" "various models (SNMP mode)" "Eaton Gigabit Network Card (Network-M2)" "snmp-ups (experimental)" "Eaton" "ups" "5" "various models (serial mode)" "Management Card Contact (ref 66104)" "mge-shut or mge-utalk" "Eaton" "pdu" "5" "ePDU Managed" "" "snmp-ups" "Eaton" "pdu" "5" "ePDU Switched" "" "snmp-ups" "Eaton" "pdu" "5" "ePDU Monitored" "" "snmp-ups or netxml-ups" +"Eaton" "ups" "5" "ePDU EMSV0001" "nLogic rebranded" "snmp-ups" "Eaton" "ups" "5" "Powerware 3105" "USB" "bcmxcp_usb" # http://powerquality.eaton.com/Products-services/Backup-Power-UPS/3105-eol.aspx "Eaton" "ups" "5" "Powerware 9125" "USB card" "bcmxcp_usb" "Eaton" "ups" "5" "Powerware 9130" "" "bcmxcp or usbhid-ups" "Eaton" "ups" "5" "Powerware 9140" "" "bcmxcp or usbhid-ups" -"Eaton" "ups" "5" "Powerware 5130" "" "usbhid-ups" +"Eaton" "ups" "5" "Powerware 5130" "USB" "usbhid-ups" "Eaton" "ups" "5" "9395" "Serial port" "bcmxcp" "Eaton" "ups" "5" "Best Ferrups" "older ConnectUPS" "snmp-ups" "Eaton" "ups" "5" "ConnectUPS X / BD / E Slot" "Serial Pass-through mode" "bcmxcp" @@ -360,10 +411,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 +434,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" @@ -413,6 +469,10 @@ "Forza Power Technologies" "ups" "2" "FX-1500LCD" "USB" "blazer_usb" "FSP" "ups" "2" "EP650" "USB" "blazer_usb" +"FSP" "ups" "2" "Fortron UPS Champ 1000 VA Tower" "USB" "blazer_usb" +"FSP" "ups" "2" "Fortron UPS Champ 1000 VA Tower" "USB" "nutdrv_qx" +"FSP" "ups" "2" "Fortron UPS Champ 2000 VA Tower" "USB" "blazer_usb" +"FSP" "ups" "2" "Fortron UPS Champ 3000 VA Tower" "USB" "blazer_usb" "Gamatronic" "ups" "5" "All models with alarm interface" "" "genericups upstype=22" "Gamatronic" "ups" "2" "G-SmartCompact 2000" "" "blazer_ser" @@ -421,22 +481,27 @@ "Gamatronic" "ups" "5" "MS" "" "gamatronic" "Gamatronic" "ups" "5" "ĀµPS3/1" "" "gamatronic" -"GE Digital Energy" "ups" "2" "EP Series" "" "blazer_usb" +"GE Digital Energy" "ups" "2" "EP Series" "USB" "blazer_usb" "GE Digital Energy" "ups" "2" "GT Series 1000/1500/2000/3000 VA Rack/Tower" "UL-version" "blazer_ser" -"Geek Squad" "ups" "2" "GS1285U" "USB" "usbhid-ups" +"Geek Squad" "ups" "3" "GS1285U" "USB" "usbhid-ups" "Gemini" "ups" "1" "UPS625/UPS1000" "" "safenet" -"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 +"Grafenthal" "ups" "3" "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 -"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" +"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" "USB" "blazer_usb" +"Gtec" "ups" "2" "ZP120N-6K / ZP120N-6KS / ZP120N-10K-11 / ZP120N-10KS-11" "USB" "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" "Gtec" "ups" "2" "AP160N-1K / AP160LCD-1K-KS / AP160N-2K / AP160LCD-2K-KS / AP160N-3K / AP160LCD-3K-KS / AP160N-6K-PDU / AP160N-10K-PDU" "USB port" "blazer_usb" "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" @@ -447,22 +512,26 @@ "HP" "ups" "3" "T500 / T750" "older models, USB port" "bcmxcp_usb" "HP" "ups" "3" "R/T3000" "Serial port" "mge-shut" "HP" "ups" "3" "R5000 / R7000" "Serial port" "mge-shut" -"HP" "ups" "3" "T750 INTL" "" "usbhid-ups" -"HP" "ups" "3" "T1000 INTL" "" "usbhid-ups" -"HP" "ups" "3" "T1500 INTL" "" "usbhid-ups" +"HP" "ups" "3" "T750 INTL" "USB" "usbhid-ups" +"HP" "ups" "3" "T1000 INTL" "USB" "usbhid-ups" +"HP" "ups" "3" "T1500 INTL" "USB" "usbhid-ups" "HP" "ups" "3" "T750 G2" "USB port" "usbhid-ups" "HP" "ups" "3" "T1000 G3" "USB port" "usbhid-ups" "HP" "ups" "3" "T1500 G3" "USB port" "usbhid-ups" "HP" "ups" "3" "R1500 G2 INTL" "USB port" "usbhid-ups" -"HP" "ups" "3" "R/T 2200 G2" "" "usbhid-ups" +"HP" "ups" "3" "R/T 2200 G2" "USB" "usbhid-ups" "HP" "ups" "3" "R/T3000" "USB port" "usbhid-ups" "HP" "ups" "3" "R5000 / R7000" "USB port" "usbhid-ups" "HP" "ups" "4" "Various (SNMP mode)" "HP UPS Management Module" "snmp-ups" "HP" "pdu" "1" "HP3488 Switch/Control Unit" "" "powerman-pdu (experimental)" "HPE" "pdu" "5" "Various (SNMP mode)" "" "snmp-ups" +"HPE" "pdu" "5" "G2 Metered & Switched PDU" "" "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" @@ -470,7 +539,7 @@ "ICS" "pdu" "1" "8064 Ethernet Relay Interface" "16 outlets" "powerman-pdu (experimental)" -"iDowell" "ups" "2" "iBox UPS" "" "usbhid-ups" +"iDowell" "ups" "3" "iBox UPS" "USB" "usbhid-ups" "INELT" "ups" "2" "Monolith 1000LT" "" "blazer_ser" "INELT" "ups" "2" "Monolith 3000RT" "" "blazer_ser" @@ -489,10 +558,11 @@ "Infosec" "ups" "2" "XP 500" "USB" "blazer_usb" "Infosec" "ups" "2" "XP 1000" "" "blazer_ser" -"IPAR" "ups" "2" "Mini Energy ME 800" "" "blazer_usb" +"IPAR" "ups" "2" "Mini Energy ME 800" "USB" "blazer_usb" "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" @@ -500,11 +570,13 @@ "Ippon" "ups" "2" "Back Comfo Pro II 650/850/1050" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Smart Power Pro 1000/1400/2000" "" "blazer_ser" "Ippon" "ups" "2" "Smart Power Pro 1000/1400/2000" "USB" "blazer_usb (experimental)" +"Ippon" "ups" "2" "Smart Power Pro II 1200/1600/2200" "USB" "usbhid-ups" +"Ippon" "ups" "2" "Smart Power Pro II Euro 1200/1600/2200" "USB" "usbhid-ups" "Ippon" "ups" "2" "Smart Winner 750/1000/1500/2000/3000" "" "blazer_ser" "Ippon" "ups" "2" "Smart Winner 750/1000/1500/2000/3000" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "(various)" "" "blazer_ser" "Ippon" "ups" "2" "(various)" "USB" "blazer_usb" -"Ippon" "ups" "2" "INNOVA RT 1K/1.5K/2K/3K" "" "blazer_usb" +"Ippon" "ups" "2" "INNOVA RT 1K/1.5K/2K/3K" "USB" "blazer_usb" "IVT" "scd" "1" "SCD series" "" "ivtscd" @@ -515,18 +587,20 @@ "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" -"KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K RACK" "" "blazer_usb" -"KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K XL RACK" "" "blazer_usb" +"KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K TOWER" "USB" "blazer_usb" +"KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K XL TOWER" "USB" "blazer_usb" +"KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K RACK" "USB" "blazer_usb" +"KOLFF" "ups" "2" "BLACK NOVA 1K/1.5K/2K/3K/6K/10K XL RACK" "USB" "blazer_usb" "Krauler" "ups" "2" "UP-D1200VA" "USB" "blazer_usb" "Krauler" "ups" "2" "UP-M500VA" "USB" "blazer_usb" "Lacerda" "ups" "2" "New Orion 800VA" "USB" "blazer_usb" -"LDLC" "ups" "2" "UPS-1200D" "" "blazer_usb langid_fix=0x4095" +"LDLC" "ups" "2" "UPS-1200D" "USB" "blazer_usb langid_fix=0x4095" "Legrand" "ups" "2" "Daker DK" "Serial" "nutdrv_qx" "Legrand" "ups" "2" "Daker DK" "USB" "nutdrv_qx" @@ -535,11 +609,11 @@ "Legrand" "ups" "2" "Keor Line RT" "Serial" "nutdrv_qx" "Legrand" "ups" "2" "Keor Line RT" "USB" "nutdrv_qx" "Legrand" "ups" "2" "Keor LP" "Serial" "nutdrv_qx" -"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" # NOTE: As of 2023 reports, the "NEW Keor Multiplug" is not supported (also by older vendor SW) "Legrand" "ups" "2" "Keor S" "Serial" "nutdrv_qx" "Legrand" "ups" "2" "Keor S" "USB" "nutdrv_qx" -"Legrand" "ups" "2" "Keor PDU" "USB" "usbhid-ups" -"Legrand" "ups" "2" "Keor SP" "USB" "usbhid-ups" +"Legrand" "ups" "3" "Keor PDU" "USB" "usbhid-ups" +"Legrand" "ups" "3" "Keor SP" "USB" "usbhid-ups" "Legrand" "ups" "2" "Keor SPX" "USB" "nutdrv_qx" "Legrand" "ups" "4" "Megaline 1250" "Serial" "metasys" "Legrand" "ups" "4" "Megaline 2500" "Serial" "metasys" @@ -573,14 +647,16 @@ "Liebert" "ups" "2" "ITON 600VA" "" "blazer_ser" "Liebert" "ups" "5" "UPStation GXT2" "contact-closure cable" "liebert" "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 PSI 1440" "USB" "usbhid-ups" # http://www.emersonnetworkpower.com/en-US/Products/ACPower/Pages/LiebertPowerSurePSILineInteractiveUPS10003000VA.aspx +"Liebert" "ups" "3" "PowerSure Personal XT" "USB" "usbhid-ups" +"Liebert" "ups" "3" "PowerSure PSA" "USB" "usbhid-ups" +"Liebert" "ups" "3" "PowerSure PSA 500" "USB" "usbhid-ups" +"Liebert" "ups" "3" "PowerSure PSA500MT3-230U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/601 +"Liebert" "ups" "3" "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)" "Lyonn" "ups" "2" "CTB-800V" "" "nutdrv_qx" -"Lyonn" "ups" "2" "CTB-1200" "" "blazer_usb" +"Lyonn" "ups" "2" "CTB-1200" "USB" "blazer_usb" "Masterguard" "ups" "1" "(various)" "" "masterguard" @@ -777,10 +853,10 @@ "MicroDowell" "ups" "5" "Enterprise N60" "" "microdowell" "MicroDowell" "ups" "5" "Enterprise HiBox ST" "" "microdowell" -"Microline" "ups" "2" "C-Lion Innova RT 2K/3K" "" "blazer_usb" -"Microline" "ups" "2" "C-Lion Innova RT 6K/10K (Parallel)" "" "blazer_usb" -"Microline" "ups" "2" "C-Lion Innova Tower 6K/10K" "" "blazer_usb" -"Microline" "ups" "2" "C-Lion Innova Combo 10K/20K (3/1)" "" "blazer_usb" +"Microline" "ups" "2" "C-Lion Innova RT 2K/3K" "USB" "blazer_usb" +"Microline" "ups" "2" "C-Lion Innova RT 6K/10K (Parallel)" "USB" "blazer_usb" +"Microline" "ups" "2" "C-Lion Innova Tower 6K/10K" "USB" "blazer_usb" +"Microline" "ups" "2" "C-Lion Innova Combo 10K/20K (3/1)" "USB" "blazer_usb" "Micropower" "ups" "2" "LCD 1000" "USB" "blazer_usb" @@ -822,11 +898,18 @@ "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" "Numeric" "ups" "2" "Digital 800 plus" "USB" "nutdrv_qx or blazer_usb" +"NUT" "ups" "5" "all supported NUT devices, through remote upsd" "" "dummy-ups" +"NUT" "ups" "5" "Simulated devices" "" "dummy-ups" + "Oneac" "ups" "1" "ON400" "advanced interface" "oneac" "Oneac" "ups" "1" "ON600" "advanced interface" "oneac" "Oneac" "ups" "1" "ON900" "advanced interface" "oneac" @@ -846,20 +929,23 @@ "Oneac" "ups" "1" "ON2000XIU" "advanced interface" "oneac" "Online" "ups" "1" "P-Series" "" "genericups upstype=14" -"Online" "ups" "2" "Zinto A" "" "blazer_usb" +"Online" "ups" "2" "Zinto A" "USB" "blazer_usb" "Online" "ups" "1" "Zinto D" "" "optiups" -"Online" "ups" "2" "Yunto YQ450" "" "blazer_usb" +"Online" "ups" "2" "Yunto YQ450" "USB" "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" "Opti-UPS" "ups" "1" "PowerES" "420E" "optiups" "Opti-UPS" "ups" "1" "VS 575C" "type=OPTI" "powercom" "Opti-UPS" "ups" "1" "Power Series" "PS1500E" "blazer_usb" +"Opti-UPS" "ups" "1" "Power Series" "PS1440RM" "optiups" "Orvaldi Power Protection" "ups" "2" "various" "not 400 or 600" "blazer_ser" -"Orvaldi Power Protection" "ups" "2" "750 / 900SP" "" "blazer_usb" +"Orvaldi Power Protection" "ups" "2" "750 / 900SP" "USB" "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 @@ -888,11 +974,18 @@ "Powercom" "ups" "4" "(various)" "USB (<= 2009 models, product id: 0002)" "powercom (requires 'usbserial' kernel module)" "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" "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" "1200VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox subdriver=hunnox langid_fix=0x0409 novendor norating noscanlangid" # https://github.com/networkupstools/nut/pull/1539 +"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" +"POWEREX" "ups" "2" "VI 1000 LED" "USB" "blazer_usb" "PowerGuard" "ups" "2" "PG-600" "" "blazer_ser" @@ -903,7 +996,7 @@ "PowerMan" "ups" "2" "RealSmart 1000" "" "blazer_ser" "PowerMan" "ups" "1" "BackPro" "" "genericups upstype=4" -"PowerShield" "ups" "2" "Defender 1200VA" "" "blazer_usb" +"PowerShield" "ups" "2" "Defender 1200VA" "USB" "blazer_usb" "PowerTech" "ups" "1" "Comp1000" "DTR cable power" "genericups upstype=3" "PowerTech" "ups" "2" "SMK-800" "" "blazer_ser" @@ -911,22 +1004,33 @@ "PowerWalker" "ups" "2" "Line-Interactive VI 1000" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 400/800" "" "blazer_ser" "PowerWalker" "ups" "2" "Line-Interactive VI 600" "" "blazer_ser" -"PowerWalker" "ups" "2" "Line-Interactive VI 600 SE" "" "blazer_usb" -"PowerWalker" "ups" "2" "Line-Interactive VI 800 SE" "" "blazer_usb" -"PowerWalker" "ups" "2" "Line-Interactive VI 1400" "" "blazer_usb" -"PowerWalker" "ups" "2" "Line-Interactive VI 2000" "" "blazer_usb" -"PowerWalker" "ups" "2" "Line-Interactive VI 850 LCD" "" "blazer_usb" -"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" "Line-Interactive VI 600 SE" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Line-Interactive VI 800 SE" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Line-Interactive VI 1400" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Line-Interactive VI 2000" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Line-Interactive VI 850 LCD" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Online VFI 1000RT/1500RT/2000RT/3000RT/6000RT/10000RT LCD" "USB" "blazer_usb" +"PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "USB" "blazer_usb" "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en +"PowerWalker" "ups" "3" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 +"PowerWalker" "ups" "3" "VI 650 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 +"PowerWalker" "ups" "3" "VI 650/850 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "3" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 +"PowerWalker" "ups" "3" "VI 2200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/756 +"PowerWalker" "ups" "3" "VI 1200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "3" "VI 2200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "3" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 +"PowerWalker" "ups" "3" "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" "3" "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" "Powerware" "ups" "4" "5119, 5125" "" "genericups upstype=15" "Powerware" "ups" "4" "5119 RM" "" "genericups upstype=20" "Powerware" "ups" "5" "5119 RM" "" "upscode2" -"Powerware" "ups" "5" "PW3105" "" "bcmxcp_usb" -"Powerware" "ups" "5" "PW5110" "" "bcmxcp_usb" +"Powerware" "ups" "5" "PW3105" "USB" "bcmxcp_usb" +"Powerware" "ups" "5" "PW5110" "USB" "bcmxcp_usb" "Powerware" "ups" "5" "PW5115" "Serial port" "bcmxcp" "Powerware" "ups" "5" "PW5115" "USB port" "bcmxcp_usb" "Powerware" "ups" "5" "PW5125" "" "bcmxcp" @@ -956,58 +1060,82 @@ "Repotec" "ups" "1" "RPT-800A" "" "genericups upstype=13" "Repotec" "ups" "1" "RPT-162A" "" "genericups upstype=13" -"Riello" "ups" "3" "Riello Sentinel SDL 6000-7" "Netman Plus 102 SNMP Card" "snmp-ups" -"Riello" "ups" "3" "Riello Sentinel Dual SDH 1000-7" "Netman Plus 102 SNMP Card" "snmp-ups" -"Riello" "ups" "4" "IDG 400/600/800/1200/1600" "" "riello_usb" -"Riello" "ups" "4" "IPG 600/800" "" "riello_usb" -"Riello" "ups" "5" "WPG 400/600/800" "" "riello_usb" -"Riello" "ups" "5" "NPW 600/800/1000/1500/2000" "" "riello_usb" -"Riello" "ups" "5" "NDG 800/1000/1500/2000" "" "riello_usb" -"Riello" "ups" "5" "DVT 500/800/1100/1500/2000" "" "riello_usb" -"Riello" "ups" "5" "DVR 500/800/1100" "" "riello_usb" -"Riello" "ups" "5" "DVD 1500/2200/3000" "" "riello_usb" -"Riello" "ups" "5" "VST 800/1100/1500/2000" "" "riello_usb" -"Riello" "ups" "5" "VSD 1100/1500/2200/3000" "" "riello_usb" -"Riello" "ups" "5" "SEP 700/1000/1500/2200/3000" "" "riello_usb" -"Riello" "ups" "5" "SDH 1000/1500/2200/3000" "" "riello_usb" -"Riello" "ups" "5" "SDL 3300/4000/5000/6000/6500/8000/10000" "" "riello_usb" -"Riello" "ups" "5" "SPW" "" "riello_usb" -"Riello" "ups" "5" "SPT" "" "riello_usb" +"Riello" "ups" "3" "Sentinel SDL 6000-7" "Netman Plus 102 SNMP Card" "snmp-ups" +"Riello" "ups" "3" "Sentinel Dual SDH 1000-7" "Netman Plus 102 SNMP Card" "snmp-ups" +"Riello" "ups" "4" "IDG 400/600/800/1200/1600" "USB" "riello_usb" +"Riello" "ups" "4" "IPG 600/800" "USB" "riello_usb" +"Riello" "ups" "5" "WPG 400/600/800" "USB" "riello_usb" +"Riello" "ups" "5" "NPW 600/800/1000/1500/2000" "USB" "riello_usb" +"Riello" "ups" "5" "NDG 800/1000/1500/2000" "USB" "riello_usb" +"Riello" "ups" "5" "DVT 500/800/1100/1500/2000" "USB" "riello_usb" +"Riello" "ups" "5" "DVR 500/800/1100" "USB" "riello_usb" +"Riello" "ups" "5" "DVD 1500/2200/3000" "USB" "riello_usb" +"Riello" "ups" "5" "VST 800/1100/1500/2000" "USB" "riello_usb" +"Riello" "ups" "5" "VSR 800/1100" "USB" "riello_usb" +"Riello" "ups" "5" "VSD 1100/1500/2200/3000" "USB" "riello_usb" +"Riello" "ups" "5" "SEP 700/1000/1500/2200/3000" "USB" "riello_usb" +"Riello" "ups" "5" "SDH 1000/1500/2200/3000" "USB" "riello_usb" +"Riello" "ups" "5" "SDL 3300/4000/5000/6000/6500/8000/10000" "USB" "riello_usb" +"Riello" "ups" "5" "SDU 4000/5000/6000/8000/10000" "USB" "riello_usb" +"Riello" "ups" "5" "SPW" "USB" "riello_usb" +"Riello" "ups" "5" "SPT" "USB" "riello_usb" +"Riello" "ups" "5" "STW 5000/6000/8000/10000" "USB" "riello_usb" +"Riello" "ups" "5" "S3M" "USB" "riello_usb" +"Riello" "ups" "5" "S3T" "USB" "riello_usb" + "Riello" "ups" "5" "NDG 800/1000/1500/2000" "" "riello_ser" "Riello" "ups" "5" "DVT 500/800/1100/1500/2000" "" "riello_ser" "Riello" "ups" "5" "DVR 500/800/1100" "" "riello_ser" "Riello" "ups" "5" "DVD 1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "VST 800/1100/1500/2000" "" "riello_ser" +"Riello" "ups" "5" "VSR 800/1100" "" "riello_ser" "Riello" "ups" "5" "VSD 1100/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SEP 700/1000/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SDH 1000/1500/2200/3000" "" "riello_ser" "Riello" "ups" "5" "SDL 3300/4000/5000/6000/6500/8000/10000" "" "riello_ser" +"Riello" "ups" "5" "SDU 4000/5000/6000/8000/10000" "" "riello_ser" "Riello" "ups" "5" "SPW" "" "riello_ser" "Riello" "ups" "5" "SPT" "" "riello_ser" +"Riello" "ups" "5" "STW 5000/6000/8000/10000" "" "riello_ser" +"Riello" "ups" "5" "S3M" "" "riello_ser" +"Riello" "ups" "5" "S3T" "" "riello_ser" "Riello" "ups" "5" "MCT" "" "riello_ser" "Riello" "ups" "5" "MST" "" "riello_ser" "Riello" "ups" "5" "MCM" "" "riello_ser" "Riello" "ups" "5" "MCT" "" "riello_ser" +"Riello" "ups" "5" "MHE" "" "riello_ser" "Riello" "ups" "5" "MHT" "" "riello_ser" "Riello" "ups" "5" "MPT" "" "riello_ser" "Riello" "ups" "5" "MPM" "" "riello_ser" +"Riello" "ups" "5" "NXE" "" "riello_ser" "Riello" "ups" "3" "(various)" "Netman Plus 101 SNMP Box" "snmp-ups" "Riello" "ups" "3" "(various)" "Netman Plus 102 SNMP Card" "snmp-ups" "Riello" "ups" "3" "(various)" "Netman Plus 202 SNMP Card" "snmp-ups" +"Riello" "ups" "3" "(various)" "Netman 204 SNMP Card" "snmp-ups" # Note: in https://github.com/networkupstools/nut/issues/1878 submitted not as "Netman Plus", clarification requested +"Riello" "ups" "3" "(various)" "Netman 208 SNMP Card" "snmp-ups" # Note: in https://github.com/networkupstools/nut/issues/1878 submitted not as "Netman Plus", clarification requested -"Rocketfish" "ups" "2" "RF-1000VA / RF-1025VA" "" "usbhid-ups" +"Rocketfish" "ups" "3" "RF-1000VA / RF-1025VA" "USB" "usbhid-ups" "Rucelf" "ups" "2" "Rucelf UPOII-3000-96-EL" "" "blazer_ser" # http://www.rucelf.ua/en/catalog/upoii-3000-96-el/ -"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)" +"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" "3" "SPS 650/850 HOME" "usb" "usbhid-ups (experimental)" +"Salicru" "ups" "3" "SLC TWIN PRO2" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/450 +"Salicru" "ups" "3" "SLC-1500-TWIN PRO3" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/1142 +"Salicru" "ups" "3" "SLC TWINPRO3" "usb" "usbhid-ups (experimental)" +"Salicru" "ups" "3" "SLC TWIN RT3" "usb" "usbhid-ups (experimental)" +"Salicru" "ups" "3" "SPS 850 ADV T" "usb" "usbhid-ups (experimental)" # https://www.salicru.com/sps-850-adv-t.html https://github.com/networkupstools/nut/issues/1416 +"Salicru" "ups" "3" "SPS 3000 ADV RT2" "usb" "usbhid-ups (experimental)" # https://www.salicru.com/sps-3000-adv-rt2.html + +"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)" +"SKE" "ups" "2" "SK600" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 bus=003" + "SmartLabs" "pdu" "1" "2412S Power Line Modem" "for X10/Insteon" "powerman-pdu (experimental)" "SMS (Brazil)" "ups" "2" "Manager III" "" "blazer_ser" @@ -1065,14 +1193,16 @@ "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" "3" "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" +"Tripp Lite" "ups" "3" "1500 LCD" "USB" "usbhid-ups" "Tripp Lite" "ups" "3" "AVR550U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3090 "Tripp Lite" "ups" "3" "AVR700U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=4785 "Tripp Lite" "ups" "3" "AVR750U" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=3141 @@ -1104,7 +1234,7 @@ "Tripp Lite" "ups" "3" "INTERNETOFFICE700" "USB (protocol 2012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=930&txtModelID=14 "Tripp Lite" "ups" "3" "OMNI650LCD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3195 "Tripp Lite" "ups" "3" "OMNI900LCD" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=3082 -"Tripp Lite" "ups" "2" "OMNI1000LCD" "USB" "usbhid-ups" +"Tripp Lite" "ups" "3" "OMNI1000LCD" "USB" "usbhid-ups" "Tripp Lite" "ups" "3" "OMNISMART300PNP" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=19 "Tripp Lite" "ups" "1" "OMNISMART500" "USB (older; product ID: 0001)" "tripplite_usb" "Tripp Lite" "ups" "3" "OMNISMART500" "USB (protocol 2010)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=932&txtModelID=21 @@ -1161,6 +1291,7 @@ "Tripp Lite" "ups" "3" "SMART3000SLT" "USB (protocol 3013)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=3490 "Tripp Lite" "ups" "2" "SMART500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" "Tripp Lite" "ups" "3" "SMART500RT1U" "USB (newer; protocol/product ID 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2853 +"Tripp Lite" "ups" "3" "SMC15002URM" "USB (protocol 3015)" "usbhid-ups" # https://www.tripplite.com/smartpro-120v-1-5kva-1kw-line-interactive-sine-wave-ups-2u-rack-tower-lcd-usb-db9-8-outlets~SMC15002URM "Tripp Lite" "ups" "3" "SMX1000LCD" "USB (protocol 2005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3200 "Tripp Lite" "ups" "3" "SMX1000RT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2798 "Tripp Lite" "ups" "3" "SMX1050SLT" "USB (protocol 3012)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3249 @@ -1171,7 +1302,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 @@ -1206,6 +1338,8 @@ "TS Shara" "ups" "4" "(various)" "" "nutdrv_qx" +"UltraMax" "ups" "2" "1000SC" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1634 + "UNITEK" "ups" "2" "ALPHA 500 IC" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 1000is" "" "blazer_ser" "UNITEK" "ups" "2" "Alpha 500" "" "blazer_ser" @@ -1221,6 +1355,10 @@ "UPSonic" "ups" "1" "Power Guardian" "" "genericups upstype=7" "UPSonic" "ups" "2" "PrOffice 650" "USB" "blazer_usb" "UPSonic" "ups" "2" "DS-800" "USB" "blazer_usb" +"UPSonic" "ups" "2" "IRT-3K 2U" "USB" "megatec_usb" # Worked in NUT 2.4.3 +"UPSonic" "ups" "2" "IRT-3K 2U" "USB" "nutdrv_qx" # Should work since 2.8.0; protocol broken vs. strict checks in 2.7.4; https://github.com/networkupstools/nut/issues/441#issuecomment-1288238345 + +"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" @@ -1252,6 +1390,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" @@ -1264,4 +1404,3 @@ "WTI" "pdu" "1" "RPS-10" "10 outlets" "powerman-pdu (experimental)" "WTI" "pdu" "1" "NPS" "8 outlets" "powerman-pdu (experimental)" - diff --git a/data/html/Makefile.am b/data/html/Makefile.am index 4d0620ac68..1327c724b9 100644 --- a/data/html/Makefile.am +++ b/data/html/Makefile.am @@ -1,10 +1,35 @@ # Network UPS Tools: data/html + # install these only if configured --with-cgi if WITH_CGI dist_html_DATA = index.html bottom.html nut-banner.png nodist_html_DATA = header.html endif -EXTRA_DIST = README +EXTRA_DIST = README.adoc + +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked MAINTAINERCLEANFILES = Makefile.in .dirstamp # Generated by configure script: diff --git a/data/html/README b/data/html/README deleted file mode 100644 index ccaf070fc8..0000000000 --- a/data/html/README +++ /dev/null @@ -1,97 +0,0 @@ -Desc: NUT HTML complementary information -File: README -Date: 27 Jul 2005 -Auth: Arnaud Quette - Dave Breiland - -This file provides some complementary information -about the use and integration of NUT HTML pages. - -1) Introduction ---------------- - -NUT HTML pages have been created as a central point -that ease the access to the various CGI scripts -providing the NUT web interface. - -It consists of three .html files: -- index.html: defines the two container frames, -topFrame and mainFrame -- header.html: contain the header including links -to NUT website, and upsstat.cgi/upsset.cgi -- bottom.html: empty frame that will be replaced -with the content of upsstat.cgi or upsset.cgi. - -2) Integration --------------- - -You first need to install NUT CGI (ie using ./configure --with-cgi). -Refer to the README file for more information - -There are two ways to integrate NUT HTML with your -webserver, with the same results: - -a) take advantage of the existing tree -====================================== - -- the cgi are for example installed in /usr/lib/cgi-bin, -which is already configured in your webserver as the -default CGI path - -- in the same spirit, we will use the existing DocumentRoot -and create a "nut" subdirectory, and copy the three .html -files (index, header and bottom) - -Note that the links to cgi scripts in header.html are -pre configured to work in this situation, which ease -the packagers work. - -b) configure manually -===================== - -- copy the data/html directory to somepath (ie /usr/local/nut -for a standard installation from source) - --Now edit your webserver configuration file, adding for -example (for Apache): - -#Begin Section -ScriptAlias /nut/cgi-bin/ /usr/local/nut/cgi-bin/ - - AllowOverride AuthConfig - Options ExecCGI - Order allow,deny - Allow from all - - -Alias /nut/ /usr/local/nut/html/ - - Options None - AllowOverride AuthConfig - Order allow,deny - Allow from all - -#End Section - --Make sure to change the links path in header.html according to your -configuration and installation. - -3) Conclusion -============= - -- Make sure to restart your webserver. - --Configure the CGI scripts. Manpages can be found from: ---prompt> man -M /usr/local/nut/man/ upsstats.cgi ---prompt> man -M /usr/local/nut/man/ upsset.cgi ---prompt> man -M /usr/local/nut/man/ upsimage.cgi ---prompt> man -M /usr/local/nut/man/ hosts.conf - --It is recommended that you use .htaccess files in the cgi-bin folder and the -html folder. Please reference: -http://httpd.apache.org/docs/howto/htaccess.html - -- You will then be able to access the NUT HTML page at: -http://localhost/nut - - diff --git a/data/html/README.adoc b/data/html/README.adoc new file mode 100644 index 0000000000..fdd6659885 --- /dev/null +++ b/data/html/README.adoc @@ -0,0 +1,103 @@ +NUT HTML complementary information +================================== +Arnaud Quette , Dave Breiland +v1.0, 27 Jul 2005 (start date) + +This file provides some complementary information +about the use and integration of NUT HTML pages. + +1) Introduction +--------------- + +NUT HTML pages have been created as a central point +that ease the access to the various CGI scripts +providing the NUT web interface. + +It consists of three HTML files: + +* `index.html`: defines the two container frames, + `topFrame` and `mainFrame` +* `header.html`: contains the header including links + to NUT website, and `upsstats.cgi`/`upsset.cgi` +* `bottom.html`: empty frame that will be replaced + with the content of `upsstats.cgi` or `upsset.cgi`. + +2) Integration +-------------- + +You first need to install NUT CGI, i.e. using +---- +:; ./configure --with-cgi +---- + +Refer to the NUT documentation for more information. + +There are two ways to integrate NUT HTML with your +webserver, with the same results: + +a) take advantage of the existing tree +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- the cgi programs are (for example) installed in `/usr/lib/cgi-bin`, + which is already configured in your webserver as the default CGI path + +- in the same spirit, we will use the existing `DocumentRoot` (in terms + of Apache webserver) and create a `nut` subdirectory, and copy the + three `.html` files (`index`, `header` and `bottom`) + +Note that the links to cgi programs in `header.html` are pre-configured +to work in this situation, which eases the packagers' work. + +b) configure manually +~~~~~~~~~~~~~~~~~~~~~ + +- copy the `data/html` directory to some path (i.e. `/usr/local/nut` + for a standard installation from source) + +- Now edit your webserver configuration file, adding for + example (for Apache): +---- +#Begin Section +ScriptAlias /nut/cgi-bin/ /usr/local/nut/cgi-bin/ + + AllowOverride AuthConfig + Options ExecCGI + Order allow,deny + Allow from all + + +Alias /nut/ /usr/local/nut/html/ + + Options None + AllowOverride AuthConfig + Order allow,deny + Allow from all + +#End Section +---- + +- Make sure to change the links path in `header.html` according to your + configuration and installation. + +3) Conclusion +------------- + +- Make sure to restart your webserver. + +- Configure the CGI scripts. Manpages can be found from: +---- +:; man -M /usr/local/nut/man/ upsstats.cgi +:; man -M /usr/local/nut/man/ upsset.cgi +:; man -M /usr/local/nut/man/ upsimage.cgi +:; man -M /usr/local/nut/man/ hosts.conf +---- + +- It is recommended that you use `.htaccess` files in the `cgi-bin` folder + and the `html` folder. Please reference: + http://httpd.apache.org/docs/howto/htaccess.html + +- You will then be able to access the NUT HTML page at: + http://localhost/nut + +- Further protection with HTTPS is recommended, especially for installations + on networks accessible by someone other than yourself. diff --git a/data/html/header.html.in b/data/html/header.html.in index 6e222a5b1e..53e4778226 100644 --- a/data/html/header.html.in +++ b/data/html/header.html.in @@ -13,7 +13,7 @@ Network UPS Tools - +
diff --git a/data/html/index.html b/data/html/index.html index 953cc376ce..15cff115d2 100644 --- a/data/html/index.html +++ b/data/html/index.html @@ -2,7 +2,7 @@ -Network UPS Tools -- http://www.networkupstools.org +Network UPS Tools -- https://www.networkupstools.org diff --git a/docs/.editorconfig b/docs/.editorconfig new file mode 100644 index 0000000000..66d4744a76 --- /dev/null +++ b/docs/.editorconfig @@ -0,0 +1,7 @@ +# Nested configuration file + +# The following documents with code samples have +# intentional trailing whitespace and should be +# only reformatted manually and attentively: +[{ci-farm-lxc-setup,solaris-usb}.txt] +trim_trailing_whitespace = false diff --git a/docs/.gitignore b/docs/.gitignore index 00076fcd45..6d00f93f51 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,15 +1,28 @@ +# 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-* /tmp/ +docinfo.xml.in.tmp +/asciidoc-vars.conf.lastrev.tmp +.prep-src-docs* diff --git a/docs/ChangeLog.txt b/docs/ChangeLog.txt new file mode 100644 index 0000000000..bd19aeb159 --- /dev/null +++ b/docs/ChangeLog.txt @@ -0,0 +1,21 @@ +:titles.underlines: "__","==","--","~~","^^" + +Network UPS Tools Change Log +____________________________ +:Author: NUT_project_community_contributors + +Introduction +============ + +The primary goal of the Network UPS Tools (NUT) project is to provide support +for Power Devices, such as Uninterruptible Power Supplies, Power Distribution +Units and Solar Controllers. + +[[Change_Log]] +Very detailed Change Log +======================== + +This document intends to detail the change log for relatively recent work +(roughly since the source code was tracked in Git). + +include::{builddir}../ChangeLog.adoc-parsed[] diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 59f0ba6105..a04d1d29f2 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -141,7 +141,7 @@ wandered off, one can be built rather easily with some connectors and cable -- there's no fancy wiring or resistors. See this URL for a handy diagram: -http://www.networkupstools.org/cables/940-0024C.jpg +https://www.networkupstools.org/cables/940-0024C.jpg There is also a text version of that diagram in the docs/cables directory of the NUT source distribution. Either one should allow @@ -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* @@ -262,6 +262,8 @@ warnings; try a value like 25 or 30. == Why do the client programs say 'Driver not connected' when I try to run them? +*Answer 1* + This means that upsd can't connect to the driver for some reason. Your ups.conf entry might be wrong, or the driver might not be running. Maybe your state path is not configured properly. @@ -279,6 +281,15 @@ with 'upsdrvsvcctl resync' and then manage those with commands like 'upsdrvsvcctl stop' and 'upsdrvsvcctl start' (note that on other systems this tool may be not pre-installed via packaging). +*Answer 2* + +Some USB UPS devices have unreliable USB to serial interfaces. +In that case, it's advised to unplug / plug the device and try again. +If that resolves the issue, you should consider resetting the USB hub the device +is attached to before starting the nut driver, using usb_resetter script from +https://github.com/netinvent/usb_resetter +See scripts/usb_resetter for more information. + == Why don't the pathnames in your documentation match the package I installed? Each distribution has conventions for where specific file types should be @@ -305,9 +316,12 @@ 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) + if (test -f /etc/killpower || /sbin/upsmon -K) then /sbin/upsdrvctl shutdown @@ -318,6 +332,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 +364,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 +458,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 +468,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 +481,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 +510,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? @@ -530,13 +546,25 @@ Or a variation like... == How do you make upsd reload the config file? Either find the pid of the background process and send it a SIGHUP, -or just start it again with '-c reload'. +or just start it again with '-c reload' (requires that the previously +started daemon saves a PID file). If you send the signals yourself instead of using -c, be sure you hit the right process. There are usually two upsmon processes, and you should only send signals to one of them. To be safe, read the pid file. +If your daemons are managed as systemd units, it is more idiomatic to +use the framework commands, e.g. `systemctl reload nut-server` (upsd) +or `systemctl reload nut-monitor` (upsmon). Note that the implementation +of `nut-server.service` by default starts `upsd -F` and does not save a +PID file; if your workflow requires to use plain `upsd -c reload`, you +should customize the unit (with a drop-in file) to start `upsd -FF`. + +NUT releases after 2.8.0 define aliases for these units, so if your Linux +distribution uses NUT-provided unit definitions, `systemctl reload upsd` +may also work. + == I just bought a new WhizBang UPS that has a USB connector. How do I monitor it? There are several driver to support USB models. @@ -544,12 +572,36 @@ 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 +https://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! @@ -604,6 +656,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... @@ -611,7 +736,7 @@ Or a variation like... == I can't run this because there's no package for it. Why isn't this in a package yet? Sorry, can't help you there. All official releases are source code -and are posted on http://www.networkupstools.org/ along with PGP +and are posted on https://www.networkupstools.org/ along with PGP signatures for verification. This means all packages have been built by a third party. If you @@ -902,9 +1027,11 @@ Or a variation like... You can rig up a little hack to handle this issue in software. Essentially, you need to test for the POWERDOWNFLAG in your *startup* scripts -while the filesystems are still read-only. If it's there, you know your last -shutdown was caused by a power failure and the UPS battery is probably still -quite weak. +while the filesystems are still read-only (before `upsmon` daemon has started +and removed it). You can also query `upsmon -K` for presence of the file, to +avoid hard-coding the path or parsing it from your `upsmon.conf` file. If the +flag is there, you know your last shutdown was caused by a power failure and +the UPS battery is probably still quite weak. In this situation, your best bet is to sleep it off. Pausing in your startup script to let the batteries recharge with the filesystems in a safe state is @@ -940,7 +1067,11 @@ 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: - if (test -f /etc/killpower) +------ + # `upsmon -K` if available on still mounted filesystems + # at this point is more portable than the `test` below + + if (test -f /etc/killpower || /sbin/upsmon -K) then /sbin/upsdrvctl shutdown @@ -949,3 +1080,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..b109989021 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,9 +1,13 @@ +# Network UPS Tools: main docs + MAINTAINERCLEANFILES = Makefile.in .dirstamp EXTRA_DIST = +all: doc + # Is "egrep == grep -E" always valid? (maybe all a job for configure.ac) -EGREP = egrep -#EGREP = grep -E +#EGREP = egrep +EGREP = grep -E IMAGE_FILES = images/asciidoc.png \ images/hostedby.png \ @@ -18,6 +22,41 @@ IMAGE_FILES = images/asciidoc.png \ images/bizarre.png \ images/old-cgi.png +# Logos which pop up in README.adoc acknowledgements and maybe other places: +IMAGE_LOGO_FILES = \ + images/ci/AppVeyor_logo-2x.png \ + images/ci/AppVeyor_logo-ar21.png \ + images/ci/CircleCI_vertical_black_logo.png \ + images/ci/DO_Powered_by_Badge_blue.png \ + images/ci/DO_Powered_by_Badge_blue_140pxW.png \ + images/ci/fosshost_org_Host_Dark_56px.png \ + images/ci/fosshost_org_Host_Light_309px.png \ + images/ci/fosshost_org_Host_Light_38px.png \ + images/ci/gandi-ar21.png \ + images/ci/gandi-ar21.svg \ + images/ci/GitHub-Mark-140pxW.png \ + images/ci/GitHub-Mark-ea2971cee799.png \ + images/ci/OC_logotype.png \ + images/ci/OC_logo-watercolor-256.png \ + images/ci/OC_logo_merged_171x32.png \ + images/ci/OC_logo_merged_140x26.png + +IMAGE_LOGO_FILES_JENKINS_NUT = \ + images/ci/jenkins-nut-large-256px.png \ + images/ci/jenkins-nut-large-squared.png \ + images/ci/jenkins-nut-large.pdn \ + images/ci/jenkins-nut-large.png \ + images/ci/jenkins-nut-small-256px.png \ + images/ci/jenkins-nut-small.pdn \ + images/ci/jenkins-nut-small.png \ + images/ci/jenkins-nut-squared.png \ + images/ci/jenkins-nut-transparent-bg-140pxW.png \ + images/ci/jenkins-nut-transparent-bg-40px.png \ + images/ci/jenkins-nut-transparent-bg.png \ + images/ci/jenkins-nut.css \ + images/ci/jenkins-nut.png \ + images/ci/jenkins-nut.txt + # Only track here the local deps SHARED_DEPS = nut-names.txt daisychain.txt asciidoc.conf asciidoc.txt @@ -46,16 +85,19 @@ CABLES_IMAGES = images/cables/73-0724.png images/cables/940-0024C.jpg \ ALL_TXT_SRC = nut-names.txt daisychain.txt \ $(USER_MANUAL_DEPS) $(DEVELOPER_GUIDE_DEPS) \ $(CABLES_DEPS) FAQ.txt nut-qa.txt packager-guide.txt snmp.txt \ - solaris-usb.txt + release-notes.txt ChangeLog.txt solaris-usb.txt +ASPELL_FILTER_PATH = @ASPELL_FILTER_PATH@ NUT_SPELL_DICT = nut.dict EXTRA_DIST += $(ALL_TXT_SRC) $(SHARED_DEPS) $(IMAGE_FILES) \ - $(CABLES_IMAGES) $(NUT_SPELL_DICT) \ - common.xsl xhtml.xsl chunked.xsl asciidoc.txt + $(IMAGE_LOGO_FILES) $(IMAGE_LOGO_FILES_JENKINS_NUT) $(CABLES_IMAGES) $(NUT_SPELL_DICT) \ + docinfo.xml common.xsl xhtml.xsl chunked.xsl asciidoc.txt asciidoc-vars.conf ASCIIDOC_HTML_SINGLE = user-manual.html \ developer-guide.html \ packager-guide.html \ + release-notes.html \ + ChangeLog.html \ solaris-usb.html \ cables.html \ FAQ.html @@ -63,6 +105,8 @@ ASCIIDOC_HTML_SINGLE = user-manual.html \ ASCIIDOC_HTML_CHUNKED = user-manual.chunked \ developer-guide.chunked \ packager-guide.chunked \ + release-notes.chunked \ + ChangeLog.chunked \ solaris-usb.chunked \ cables.chunked \ FAQ.chunked @@ -70,24 +114,31 @@ ASCIIDOC_HTML_CHUNKED = user-manual.chunked \ ASCIIDOC_PDF = user-manual.pdf \ developer-guide.pdf \ packager-guide.pdf \ + release-notes.pdf \ + ChangeLog.pdf \ solaris-usb.pdf \ cables.pdf \ FAQ.pdf SUBDIRS = man cables -SUFFIXES = .txt .html .pdf -spellchecked - -all: doc +SUFFIXES = .txt .html .pdf -spellchecked .txt-prepped # This list is defined by configure script choices and options: -check-local: @DOC_CHECK_LIST@ +CHECK_LOCAL_TARGETS = @DOC_CHECK_LIST@ +if WITH_SPELLCHECK +CHECK_LOCAL_TARGETS += spellcheck +endif WITH_SPELLCHECK +check-local: $(CHECK_LOCAL_TARGETS) + +# Make sure sources are there for out-of-tree builds: +@DOC_BUILD_LIST@ $(ASCIIDOC_PDF) $(ASCIIDOC_HTML_SINGLE) $(ASCIIDOC_HTML_CHUNKED): $(abs_top_builddir)/docs/.prep-src-docs # This list is defined by configure script choices and options: -doc: @DOC_BUILD_LIST@ +doc: $(abs_top_builddir)/docs/.prep-src-docs @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,44 +181,123 @@ 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: - cd $(top_builddir)/docs/man/ && $(MAKE) -f Makefile $@ +# 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) $(AM_MAKEFLAGS) -f Makefile $@ man: - cd $(top_builddir)/docs/man/ && $(MAKE) -f Makefile all - -CLEANFILES = *.xml *.html *.pdf *-spellchecked docbook-xsl.css + +cd $(top_builddir)/docs/man/ && $(MAKE) $(AM_MAKEFLAGS) -f Makefile all + +CLEANFILES = *.xml *.html *.pdf *-spellchecked docbook-xsl.css docinfo.xml.in.tmp +CLEANFILES += $(top_builddir)/INSTALL.nut $(top_builddir)/UPGRADING $(top_builddir)/NEWS $(top_builddir)/ChangeLog.adoc $(top_builddir)/README +CLEANFILES += $(top_builddir)/*.adoc-parsed *.adoc-parsed + +### TODO: general automatic dependency generation + +# Prepare text files (currently a manually tracked list) +# with known presence of GitHub links to convert them from +# short notation into asciidoc link markup +# before rendering into HTML/PDF. +# Work around some documents that have originally included +# the asciidoc markup (use double-hash to avoid conversion). + +DOCBUILD_FILTER_GITHUB_LINKS = { \ + $(SED) \ + -e 's%\(link:https*://github.com/networkupstools/[a-zA-Z0-9./-]*/[1-9][0-9]*/*\[[^]]*\)\#\([1-9][0-9]*\)%\1\#\#\2%g' \ + -e 's%\(issue\) *\#\([1-9][0-9]*\)\([^0-9]\|$$\)%link:https://github.com/networkupstools/nut/issues/\2[\1 \#\#\2]\3%g' \ + -e 's%\(PR\|pull request\) *\#\([1-9][0-9]*\)\([^0-9]\|$$\)%link:https://github.com/networkupstools/nut/pull/\2[\1 \#\#\2]\3%g' \ + -e 's%\([[ ,]\)\#\([1-9][0-9]*\)\([^0-9]\|$$\)%\1link:https://github.com/networkupstools/nut/issues/\2[\#\#\2]\3%g' \ + -e 's%\(issue\) networkupstools/\([^ ][^ ]*\)\#\([1-9][0-9]*\)\([^0-9]\|$$\)%link:https://github.com/networkupstools/\2/issues/\3[\1 \2\#\#\3]\4%g' \ + -e 's%\(PR\|pull request\) *networkupstools/\([^ ][^ ]*\)\#\([1-9][0-9]*\)\([^0-9]\|$$\)%link:https://github.com/networkupstools/\2/pull/\3[\1 \2\#\#\3]\4%g' \ + -e 's%\([[ ,]\)networkupstools/\([^ ][^ ]*\)\#\([1-9][0-9]*\)\([^0-9]\|$$\)%\1link:https://github.com/networkupstools/\2/issues/\3[\2\#\#\3]\4%g' \ + -e 's%\#\(\#[1-9][0-9]*\)%\1%g' \ +; } + +# The $< is okay here, it is used in a suffix rule below +DOCBUILD_CONVERT_GITHUB_LINKS = { \ + echo " DOC-ASCIIDOC-GITHUB-LINKS Parsing GitHub link patterns $< => $@"; \ + cat "$<" | $(DOCBUILD_FILTER_GITHUB_LINKS) > "$@.tmp" \ + && mv -f "$@.tmp" "$@" \ +; } + +.adoc.adoc-parsed: + @$(DOCBUILD_CONVERT_GITHUB_LINKS) + +$(top_builddir)/ChangeLog: + +@echo " DOC-CHANGELOG-GENERATE $@" \ + && cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +# BSD Make dislikes the path resolution here and does not always populate "$<" +# (and claims why: "Using $< in a non-suffix rule context is a GNUmake idiom"), +# but it has a "$?" for "list of dependencies that are newer than the target". +# For more details see https://man.freebsd.org/cgi/man.cgi +$(top_builddir)/ChangeLog.adoc: $(top_builddir)/ChangeLog + @INPUT="$?"; \ + test -n "$${INPUT}" || INPUT="$$(top_builddir)/ChangeLog" ; \ + test -n "$${INPUT}" && test -n "$@" && test -s "$${INPUT}" \ + || { \ + MSG="FAILED to resolve input or output filename with this make implementation, or input was not generated!"; \ + echo " DOC-CHANGELOG-ASCIIDOC SKIP: $${MSG}" >&2; \ + test -n "$@" && { printf '=== Failed to generate the ChangeLog\n\n%s\n\nNOTE: See https://github.com/networkupstools/nut/commits/master for change history.\n\n' "$${MSG}" > "$@" ; } ; \ + exit ; \ + } ; \ + echo " DOC-CHANGELOG-ASCIIDOC $${INPUT} => $@" \ + && printf "ifdef::txt[]\n== Very detailed Change Log\nendif::txt[]\n\n" > "$@.tmp" \ + && TABCHAR="`printf '\t'`" \ + && $(SED) \ + -e 's,^\([0-9a-zA-Z]\),=== \1,' \ + -e 's,^=== \(NOTE: \),\1,' \ + -e 's,/[+],/\\\\\+,g' \ + -e 's,[+][+],\\\+\\\+,g' \ + -e 's,^\([ '"$${TABCHAR}"'][^+]*\)\([^+/\]\)[+],\1\2\\\+,g' \ + -e 's,^\([ '"$${TABCHAR}"'].*\)\([~|^]\),\1\\\2,g' \ + -e 's,\[\[\([^]]*\)\]\],[\1],g' \ + -e 's,^\(\s\s*\)\([0-9]\),\1{empty}\2,g' \ + < "$${INPUT}" >> "$@.tmp" \ + && mv -f "$@.tmp" "$@" -# Dirs to clean -clean-local: - rm -rf *.chunked *.bak tmp - -### TODO: automatic dependency generation # Add other directory deps (not for local EXTRA_DIST) and generated contents -FULL_USER_MANUAL_DEPS = $(USER_MANUAL_DEPS) $(SHARED_DEPS) ../README \ - ../INSTALL.nut ../UPGRADING ../TODO ../scripts/ufw/README +FULL_USER_MANUAL_DEPS = $(USER_MANUAL_DEPS) $(SHARED_DEPS) \ + $(top_builddir)/README.adoc-parsed \ + $(top_builddir)/INSTALL.nut.adoc-parsed \ + $(top_builddir)/UPGRADING.adoc-parsed \ + ../TODO.adoc ../scripts/ufw/README.adoc FULL_DEVELOPER_GUIDE_DEPS = $(DEVELOPER_GUIDE_DEPS) $(SHARED_DEPS) \ - ../scripts/augeas/README ../TODO ../lib/README \ - ../tools/nut-scanner/README + ../scripts/augeas/README.adoc ../TODO.adoc \ + ../lib/README.adoc \ + ../tools/nut-scanner/README.adoc user-manual.html user-manual.chunked user-manual.pdf: $(FULL_USER_MANUAL_DEPS) developer-guide.html developer-guide.chunked developer-guide.pdf: $(FULL_DEVELOPER_GUIDE_DEPS) packager-guide.html packager-guide.chunked packager-guide.pdf: packager-guide.txt asciidoc.conf +release-notes.html release-notes.chunked release-notes.pdf: release-notes.txt $(top_builddir)/NEWS.adoc-parsed $(top_builddir)/UPGRADING.adoc-parsed asciidoc.conf +ChangeLog.html ChangeLog.chunked ChangeLog.pdf: ChangeLog.txt $(top_builddir)/ChangeLog.adoc-parsed asciidoc.conf solaris-usb.html solaris-usb.chunked solaris-usb.pdf: solaris-usb.txt asciidoc.conf # Note: without the "-v", asciidoc (circa 8.6.2) sometimes hangs when # 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 \ +# Note: `(top_)srcdir` and `(top_)builddir` must end with a path +# separator, or be empty -- so in all cases letting the resulting +# string resolve meaningfully in the filesystem during docs build. +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=docinfodir="$(builddir)" \ + --attribute=iconsdir="$(srcdir)/images" \ --attribute=badges \ --attribute=external_title \ - --attribute tree_version=@TREE_VERSION@ \ + --attribute=tree_version="@TREE_VERSION@" \ + --attribute=srcdir="$(abs_srcdir)/" \ + --attribute=builddir="$(abs_builddir)/" \ + --attribute=top_srcdir="$(abs_top_srcdir)/" \ + --attribute=top_builddir="$(abs_top_builddir)/" \ -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,13 +317,20 @@ 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 = { \ if test -n "$${A2X_OUTDIR}" && test "$${A2X_OUTDIR}" != '.' ; then \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ - mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + $(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" \ @@ -216,6 +353,12 @@ DOCBUILD_END = { \ fi ; \ } +### Call the prep step consistently to create symlinks (out-of-tree) +### or just touch-files for peace of mind (in-tree builds). Then we +### use these path names (truncated "-prepped") now surely located +### in the builddir as the sources for rendered docs. +*.txt-prepped: $(abs_top_builddir)/docs/.prep-src-docs + # PORTABILITY NOTE: POSIX Make forbids the suffix rule definitions with # prerequisites like done below, and GNU Make of some versions complains; # https://www.gnu.org/software/make/manual/html_node/Error-Messages.html @@ -227,31 +370,43 @@ DOCBUILD_END = { \ # as done below may be pointless in the end (with regard to a portable way # to trigger builds by a changed dependency), but at least predictable and # not toxic. +###.txt.txt-prepped: $(abs_top_builddir)/docs/.prep-src-docs + *.html: common.xsl xhtml.xsl -.txt.html: +.txt-prepped.html: @A2X_OUTDIR="tmp/html-single.$(@F).$$$$" ; \ echo " DOC-HTML Generating $@"; \ $(DOCBUILD_BEGIN) ; RES=0; \ - $(A2X) $(A2X_COMMON_OPTS) --attribute=xhtml11_format --format=xhtml --xsl-file=$(srcdir)/xhtml.xsl $< || RES=$$? ; \ + $(A2X) $(A2X_COMMON_OPTS) --attribute=xhtml11_format --format=xhtml --xsl-file=$(srcdir)/xhtml.xsl "$(&2 ; exit 0; fi; \ + case "$@" in *-spellchecked) ;; *) echo "SKIP: Bogus spellcheck call for non '*-spellchecked' target filename (with make target $@ from `pwd`)" >&2 ; exit 0;; esac; \ rm -f "$@" || true ; \ - echo " ASPELL Spell checking on $(SPELLCHECK_DIR)/$(SPELLCHECK_SRC_ONE)"; \ - OUT="`(sed 's,^\(.*\)$$, \1,' | $(ASPELL) -a -t $(ASPELL_NUT_COMMON_ARGS) 2>&1) < "$(SPELLCHECK_DIR)/$(SPELLCHECK_SRC_ONE)"`" \ + $(MKDIR_P) "$(@D)" || exit ; \ + REPORT_SRCDIR="$(SPELLCHECK_SRCDIR)"; \ + REPORT_SRC_ONE="$(SPELLCHECK_SRC_ONE)"; \ + REPORT_PREFIX="" ; \ + case "$(SPELLCHECK_SRC_ONE)" in \ + /*) ;; \ + */*) if [ x"$${REPORT_SRCDIR}" = x ] ; then \ + echo EMPTY >$(SPELLCHECK_RECIPE_DEBUG_STREAM) ; \ + REPORT_SRCDIR="`dirname "$(SPELLCHECK_SRC_ONE)"`"; \ + else \ + echo "APPEND: SPELLCHECK_SRCDIR='$(SPELLCHECK_SRCDIR)' SPELLCHECK_SRC_ONE='$(SPELLCHECK_SRC_ONE)' dirname='`dirname "$(SPELLCHECK_SRC_ONE)"`'" >$(SPELLCHECK_RECIPE_DEBUG_STREAM) ; \ + REPORT_SRCDIR="$${REPORT_SRCDIR}/`dirname "$(SPELLCHECK_SRC_ONE)"`"; \ + fi ; \ + REPORT_SRC_ONE="`basename "$(SPELLCHECK_SRC_ONE)"`"; \ + ;; \ + *) ;; \ + esac; \ + if [ x"$${REPORT_SRCDIR}" != x ] ; then \ + tmpREPORT_PREFIX="NUT source root :: $${REPORT_SRCDIR} :: " ; \ + REPORT_SRCDIR="`cd "$${REPORT_SRCDIR}" && { pwd >$(SPELLCHECK_RECIPE_DEBUG_STREAM) ; pwd | sed 's|^'"$(abs_top_srcdir)"'/*||' ; }`" \ + || { REPORT_SRCDIR="$(SPELLCHECK_SRCDIR)" ; REPORT_SRC_ONE="$(SPELLCHECK_SRC_ONE)" ; REPORT_PREFIX="" ; } ; \ + fi ; \ + echo "=== Got REPORT_SRCDIR='$${REPORT_SRCDIR}'" >$(SPELLCHECK_RECIPE_DEBUG_STREAM) ; \ + case "$${REPORT_SRCDIR}" in \ + "") ;; \ + */) ;; \ + *) REPORT_SRCDIR="$${REPORT_SRCDIR}/" ;; \ + esac ; \ + echo " ASPELL Spell checking on $${REPORT_PREFIX}$${REPORT_SRCDIR}$${REPORT_SRC_ONE}"; \ + OUT="`(sed 's,^\(.*\)$$, \1,' | $(ASPELL) -a $(ASPELL_NUT_TEXMODE_ARGS) $(ASPELL_NUT_COMMON_ARGS) 2>&1) < "$(SPELLCHECK_SRCDIR)/$(SPELLCHECK_SRC_ONE)"`" \ && { if test -n "$$OUT" ; then OUT="`echo "$$OUT" | $(EGREP) -b -v '$(ASPELL_OUT_NOTERRORS)' `" ; fi; \ test -z "$$OUT" ; } \ || { RES=$$? ; \ @@ -313,22 +517,22 @@ $(SPELLCHECK_DIR)/$(SPELLCHECK_SRC_ONE)-spellchecked: $(SPELLCHECK_DIR)/$(SPELLC exit $$RES; } ; \ touch "$@" -spellcheck: +spellcheck: @if test "$(SPELLCHECK_ENV_DEBUG)" != no ; then \ echo "ASPELL DEBUG : information about the setup follows:"; \ LANG=$(ASPELL_ENV_LANG); LC_ALL=$(ASPELL_ENV_LANG); export LANG; export LC_ALL; \ $(ASPELL) --help || true; \ - dpkg -l |grep -i aspell || true ; \ - echo "ASPELL automatic execution line is : ( sed 's,^\(.*\)$$, \1,' < docfile.txt | $(ASPELL) -a -t $(ASPELL_NUT_COMMON_ARGS) | $(EGREP) -b -v '$(ASPELL_OUT_NOTERRORS)' )" ; \ + (command -v dpkg) && ( dpkg -l | grep -i aspell ) || true ; \ + echo "ASPELL automatic execution line is : ( sed 's,^\(.*\)$$, \1,' < docfile.txt | $(ASPELL) -a $(ASPELL_NUT_TEXMODE_ARGS) $(ASPELL_NUT_COMMON_ARGS) | $(EGREP) -b -v '$(ASPELL_OUT_NOTERRORS)' )" ; \ echo "ASPELL proceeding to spellchecking job..."; \ else true; fi - @FAILED="" ; LANG=C; LC_ALL=C; export LANG; export LC_ALL; \ + +@FAILED="" ; LANG=C; LC_ALL=C; export LANG; export LC_ALL; \ for docsrc in $(SPELLCHECK_SRC); do \ if test "$(SPELLCHECK_ENV_DEBUG)" != no ; then \ - echo "ASPELL MAKEFILE DEBUG: Will see from `pwd` if '$(SPELLCHECK_DIR)/$${docsrc}-spellchecked' is up to date" >&2; \ + echo "ASPELL MAKEFILE DEBUG: Will see from `pwd` if '$(SPELLCHECK_SRCDIR)/$${docsrc}-spellchecked' is up to date" >&2; \ else true ; fi ; \ - $(MAKE) -s -f "$(abs_top_builddir)/docs/Makefile" SPELLCHECK_SRC_ONE="$${docsrc}" SPELLCHECK_DIR="$(SPELLCHECK_DIR)" "$(SPELLCHECK_DIR)/$${docsrc}-spellchecked" \ - || FAILED="$$FAILED $(SPELLCHECK_DIR)/$$docsrc"; \ + $(MAKE) $(AM_MAKEFLAGS) -s -f "$(abs_top_builddir)/docs/Makefile" SPELLCHECK_SRC="" SPELLCHECK_SRC_ONE="$${docsrc}" SPELLCHECK_BUILDDIR="$(SPELLCHECK_BUILDDIR)" SPELLCHECK_SRCDIR="$(SPELLCHECK_SRCDIR)" "$(SPELLCHECK_BUILDDIR)/$${docsrc}-spellchecked" \ + || FAILED="$$FAILED $(SPELLCHECK_SRCDIR)/$$docsrc"; \ done ; \ if test -n "$$FAILED" ; then \ echo "=====================================================================" ; \ @@ -359,14 +563,14 @@ spellcheck-sortdict: $(abs_builddir)/$(NUT_SPELL_DICT).sorted $(abs_builddir)/$(NUT_SPELL_DICT).sorted: $(abs_srcdir)/$(NUT_SPELL_DICT) @cp -pf $(abs_srcdir)/$(NUT_SPELL_DICT) $(abs_builddir)/$(NUT_SPELL_DICT).bak-pre-sorting @LANG=$(ASPELL_ENV_LANG); LC_ALL=$(ASPELL_ENV_LANG); export LANG; export LC_ALL; ( \ - WORDLIST="`tail -n +2 < "$<" | sort | uniq`"; \ + WORDLIST="`tail -n +2 < "$?" | sort | uniq`"; \ WORDCOUNT="`echo "$$WORDLIST" | wc -l`"; \ - head -1 < "$<" | while read P L C E ; do echo "$$P $$L $$WORDCOUNT $$E"; break; done ; \ + head -1 < "$?" | while read P L C E ; do echo "$$P $$L $$WORDCOUNT $$E"; break; done ; \ echo "$$WORDLIST"; \ ) > "$@" @cp -f "$@" "$(abs_builddir)/$(NUT_SPELL_DICT)" @if [ "$(abs_builddir)" != "$(abs_srcdir)" ] ; then \ - cp -f "$@" "$<" || true ; \ + cp -f "$@" "$?" || true ; \ cp -f "$(abs_builddir)/$(NUT_SPELL_DICT).bak-pre-sorting" "$(abs_srcdir)/" || true ; \ fi @@ -374,29 +578,113 @@ DISTCLEANFILES = $(NUT_SPELL_DICT).bak-pre-sorting .$(NUT_SPELL_DICT).sorted $(N spellcheck-interactive: @FAILED="" ; for docsrc in $(SPELLCHECK_SRC); do \ - echo "Spell checking on $(SPELLCHECK_DIR)/$$docsrc"; \ - LANG=$(ASPELL_ENV_LANG) LC_ALL=$(ASPELL_ENV_LANG) $(ASPELL) check $(ASPELL_NUT_COMMON_ARGS) $(SPELLCHECK_DIR)/$$docsrc || \ - FAILED="$$FAILED $(SPELLCHECK_DIR)/$$docsrc"; \ + echo "Spell checking on $(SPELLCHECK_SRCDIR)/$$docsrc"; \ + LANG=$(ASPELL_ENV_LANG) LC_ALL=$(ASPELL_ENV_LANG) $(ASPELL) check $(ASPELL_NUT_COMMON_ARGS) $(SPELLCHECK_SRCDIR)/$$docsrc || \ + FAILED="$$FAILED $(SPELLCHECK_SRCDIR)/$$docsrc"; \ done ; \ if test -n "$$FAILED" ; then \ echo "FAILED interactive spellcheck for the following sources (relative to `pwd`): $$FAILED" >&2 ; \ exit 1; \ fi ; exit 0 - $(MAKE) spellcheck-sortdict + +$(MAKE) $(AM_MAKEFLAGS) spellcheck-sortdict @echo "------------------------------------------------------------------------"; \ echo "Custom dictionary file $(NUT_SPELL_DICT) may have been updated now."; \ echo "Use 'git add -p docs/$(NUT_SPELL_DICT) && git checkout -- docs/$(NUT_SPELL_DICT) && make spellcheck-sortdict && git add -p docs/$(NUT_SPELL_DICT)'"; \ echo "to review changes (please DO NOT REMOVE LINES that aspell chose to drop,"; \ echo "because other systems might not know these words in their system dictionaries)"; \ echo "------------------------------------------------------------------------" + else !HAVE_ASPELL -# This rule woulf probably just fail; normally with no ASPELL there are no callers for it +# This rule would probably just fail; normally with no ASPELL there are no callers for it */*-spellchecked *-spellchecked: Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) - @echo " SKIP-ASPELL $@ : Documentation spell check not available since 'aspell' was not found." >&2 + @echo " SKIP-ASPELL $@ : Documentation spell check not available since 'aspell' was not found (or missing its English dictionary)." >&2 spellcheck: - @echo "Documentation spell check not available since 'aspell' was not found." + @echo "Documentation spell check not available since 'aspell' was not found (or missing its English dictionary)." spellcheck-interactive: - @echo "Documentation spell check not available since 'aspell' was not found." + @echo "Documentation spell check not available since 'aspell' was not found (or missing its English dictionary)." endif !HAVE_ASPELL + +# When building out-of-tree, be sure to have all asciidoc resources +# under the same dir structure (tool limitation) +PREP_SRC = $(EXTRA_DIST) $(SPELLCHECK_SRC_DEFAULT) + +# NOTE: Some "make" implementations prefix a relative or absent path to +# the filenames in PREP_SRC, others (e.g. Sun make) prepend the absolute +# path to locate the sources, so we end up with bogus trees under docs/. +# Code below tries to detect and truncate this mess, including possible +# source texts located in/under parent dirs. +$(abs_top_builddir)/docs/.prep-src-docs: $(PREP_SRC) + @cd "$(@D)" || exit ; \ + linkroot="$(abs_builddir)" ; \ + if test x"$(abs_srcdir)" = x"$(abs_builddir)" ; then \ + COUNT=0; \ + for F in $(PREP_SRC) ; do \ + case "$$F" in \ + /*) F="`echo "$$F" | sed "s#^$(abs_top_srcdir)/*#./#"`"; \ + if test x"$${linkroot}" = x"$(abs_builddir)" ; then \ + linkroot="$(abs_top_builddir)" ; \ + cd "$(abs_top_builddir)" ; \ + fi ;; \ + esac ; \ + if ! test -e "$${F}-prepped" ; then \ + touch "$${F}-prepped" || exit ; \ + COUNT="`expr $$COUNT + 1`" ; \ + fi ; \ + done ; \ + if ! test -e "$@" ; then touch "$@" ; fi ; \ + else \ + COUNT=30 ; \ + touch "$@.$$$$" ; \ + while test -e "$@.working" -a "$$COUNT" -gt 0 ; do sleep 1; COUNT="`expr $$COUNT - 1`"; done ; \ + touch "$@.working" ; \ + if test -n "`find "$@" -newer "$@.$$$$" 2>/dev/null`" ; then \ + rm -f "$@.$$$$" "$@.working" ; \ + exit 0; \ + fi ; \ + rm -f "$@.$$$$" ; \ + COUNT=0; \ + linksrcroot="$(abs_srcdir)" ; \ + for F in `echo $(PREP_SRC) | tr ' ' '\n' | sort -n | uniq` ; do \ + case "$$F" in \ + /*) F="`echo "$$F" | sed "s#^$(abs_top_srcdir)/*#./#"`"; \ + if test x"$${linkroot}" = x"$(abs_builddir)" ; then \ + linkroot="$(abs_top_builddir)" ; \ + linksrcroot="$(abs_top_srcdir)" ; \ + cd "$(abs_top_builddir)" ; \ + fi ;; \ + "$(srcdir)"/*) F="`echo "$$F" | sed 's#^$(srcdir)/*#./#'`" ;; \ + esac ; \ + D="`dirname "$$F"`" ; \ + $(MKDIR_P) "$${linkroot}/$$D" || { rm -f "$@.working" ; exit 1 ; } ; \ + if ! test -s "$${linkroot}/$$F" && test -s "$${linksrcroot}/$$F" ; then \ + echo " LN '$${linksrcroot}/$$F' => '$${linkroot}/$$F' (PWD = '`pwd`')" >&2 ; \ + ln -fs "$${linksrcroot}/$$F" "$${linkroot}/$$F" || { rm -f "$@.working" ; exit 1 ; } ; \ + COUNT="`expr $$COUNT + 1`" ; \ + fi ; \ + if ! test -e "$${linkroot}/$${F}-prepped" ; then \ + touch "$${linkroot}/$${F}-prepped" || { rm -f "$@.working" ; exit 1 ; } ; \ + COUNT="`expr $$COUNT + 1`" ; \ + fi ; \ + done ; \ + fi ; \ + if test "$$COUNT" -gt 0 -o ! -e "$@" ; then touch "$@" ; fi + @rm -f "$@.working" + +# Dirs to clean, etc. +clean-local: + $(AM_V_at)rm -rf *.chunked *.bak tmp + $(AM_V_at)for F in $(PREP_SRC) ; do \ + case "$$F" in \ + /*) F="`echo "$$F" | sed "s#^$(abs_top_srcdir)/*#./#"`"; cd "$(abs_top_builddir)" ;; \ + esac ; \ + if test x"$(abs_srcdir)" != x"$(abs_builddir)" ; then \ + if test -L "$$F" || test -h "$$F" ; then \ + rm -f "$$F" ; \ + fi ; \ + fi ; \ + rm -f "$${F}-prepped" ; \ + done ; \ + rm -f "$(abs_top_builddir)/docs/.prep-src-docs"* + .PHONY: html html-chunked html-single pdf man diff --git a/docs/acknowledgements.txt b/docs/acknowledgements.txt index 7597bb224b..77d0fd30ab 100644 --- a/docs/acknowledgements.txt +++ b/docs/acknowledgements.txt @@ -8,8 +8,8 @@ This project is the result of years of work by many individuals and companies. Many people have written or tweaked the software; the drivers, clients, server and documentation have all received valuable attention from numerous sources. -Many of them are listed within the source code, AUTHORS file, release notes, and -mailing list archives, but some prefer to be anonymous. +Many of them are listed within the source code, AUTHORS file, release notes, +and mailing list archives, but some prefer to be anonymous. This software would not be possible without their help. @@ -19,8 +19,10 @@ The NUT Team Active members ~~~~~~~~~~~~~~ -- Jim Klimov: project leader (since 2020), OpenIndiana and OmniOS packager, CI ops and infra -- Arnaud Quette: ex-project leader (from 2005 to 2020), Debian packager and jack of all trades +- Jim Klimov: project leader (since 2020), OpenIndiana and OmniOS packager, + CI dev/ops and infra +- Arnaud Quette: ex-project leader (from 2005 to 2020), Debian packager + and jack of all trades - Charles Lepple: senior lieutenant - Daniele Pezzini: senior developer - Kjell Claesson: senior developer @@ -120,7 +122,7 @@ list, and the rest of the information is available via the link:http://article.gmane.org/gmane.comp.monitoring.nut.user/8173[list archives]. -* link:https://nag.company/en[NAG], through Alexey Kazancev and Igor Ryabov, +* link:https://nag.company/en[NAG], through Alexey Kazancev and Igor Ryabov, has added support for SNR-UPS-LID-XXXX models as usb-hid UPS. * link:http://www.ablerex.com.tw/[Ablere Electronics Co., Ltd.] contributed diff --git a/docs/asciidoc-vars.conf b/docs/asciidoc-vars.conf new file mode 100644 index 0000000000..d194923745 --- /dev/null +++ b/docs/asciidoc-vars.conf @@ -0,0 +1,151 @@ +ifndef::asciidoc-vars-nut-included[] +:asciidoc-vars-nut-included: true +// NOTE: The big block of comments and definitions below comes from +// NUT::docs/asciidoc-vars.conf and is included into top-level document +// sources by maintenance recipes directly (`make maintainer-asciidocs`), +// due to current limitations of the GitHub Web UI asciidoc renderer. +// Hopefully it can be dropped in favor of compact include definitions +// (see README.adoc for anticipated example) after this issue is resolved +// on their side: +// * https://github.com/github/markup/issues/1095 +// +// This file should be included into NUT documentation sources to consistently +// define certain expandable attributes, with contents defined based on the +// rendition target (e.g. GitHub Web UI, plain text, locally built HTML/PDF...) +// Note that currently GitHub Web UI references lead to nut-website (as of +// last built and published revision), not to neighboring documents in the +// source browser (which would make sense for branch revisions, etc.) due +// to certain complexity about referencing other-document sections with a +// partially functional rendering engine there. Exploration and fixes are +// welcome (actually working links like +// https://github.com/networkupstools/nut/tree/master#installing or +// https://github.com/networkupstools/nut/blob/master/UPGRADING.adoc#changes-from-274-to-280 +// do seem promising)! +// +// Since the GitHub UI does not allow use of custom asciidoc configuration +// files, or generally does not process the `include:` requests at this time, +// clumsy expandable attributes had to be used (usually a set including a +// prefix with meaningful name, and one or more separators and/or a suffix +// with shortened names). For our classic documentation renditions, they +// should resolve to properly defined macros from `docs/asciidoc.conf` +// (usually named same as the variables defined here, for simplicity): +// * `linkdoc` allows to refer to a file under `docs/` directory (or +// its nut-website rendition). +// * `xref` substitutes the asciidoc shorthand '<< >>' syntax with +// attributes that conditionally expand to: +// - links on GitHub (references can point at most to a section of +// level docs/common.xsl's ), or +// - xref asciidoc macros when generating docs. +// * `linksingledoc` guarantees that, when chunked HTML is generated, +// the link always points to a non-chunked file. +// * `linkman2` allows to support different names for the manpage and +// the command shown. This is also needed to properly display links +// to manpages in both GitHub and generated docs without defining an +// attribute for each manpage. +// +// Optional attributes set by callers: +// * `website-url` (defaulted below) may be used for "historic website" +// snapshot builds... hopefully +// * `website` is used as a boolean toggle in our recipes for nut-website +// vs. offline documentation renditions +// * `env-github` is used as a boolean toggle, set by GitHub Web-UI renderer +// * `(top_)srcdir` and `(top_)builddir` can be set by `Makefile.am` +// calling the `a2x` tool, since some of the files with the asciidoc +// mark-up are only generated or post-processed during build and +// (due to `make dist` restrictions) being build products, they may +// not reside in same directory as static source text files which +// reference or include them. Note that the non-`top` paths would +// normally differ based on location of the `Makefile` involved +// (e.g. workspace root, or the `docs`, or `docs/man` directories). +// These variables are expected to be absolute paths, or ones relative +// to asciidoc-selected `:base_dir`, and to end with a relevant path +// separator, or be empty -- so in all cases letting the resulting +// string resolve meaningfully in the filesystem during docs build. +// +// Please keep the remaining comments and definitions as one big block +// so it does not become a series of empty paragraphs in the rendered +// documents! +// +ifndef::website-url[] +:website-url: https://www.networkupstools.org/ +endif::website-url[] +// +ifndef::srcdir[] +:srcdir: +endif::srcdir[] +// +ifndef::builddir[] +:builddir: +endif::builddir[] +// +ifndef::top_srcdir[] +:top_srcdir: +endif::top_srcdir[] +// +ifndef::top_builddir[] +:top_builddir: +endif::top_builddir[] +// +// +// Address links on GitHub vs docs +// (note: 'env-github' attribute is set on GitHub) +// +// - when generating docs: +ifndef::env-github[] +// * xref -> xref +// syntax: {xref}{x-s}[] +// -> xref:[] +:xref: xref: +:x-s: +// * link to doc -> our macro +// syntax: {linkdoc}{ld-s}[] +// -> linkdoc:[] +:linkdoc: linkdoc: +:ld-s: +// * link to single doc -> our macro +// syntax: {linksingledoc}{lsd-s}[] +// -> linksingledoc:[] +:linksingledoc: linksingledoc: +:lsd-s: +// * link to manpage -> our macro +// syntax: {linkman2}{lm-s}{lm-c}{lm-e} +// -> linkman2:[,] +:linkman2: linkman2: +:lm-s: [ +:lm-c: , +:lm-e: ] +endif::env-github[] +// +// - on GitHub: +ifdef::env-github[] +// In our normal builds, Makefile variables convey the needed paths +// (used relatively below as `image:images/ci/...png` etc.) +:imagesdir: docs +// * xref -> link +// syntax: {xref}{x-s}[] +// In order for it to work, can reference at most a section of +// level docs/common.xsl's +// -> {website-url}docs/user-manual.chunked/.html[] +:xref: {website-url}docs/user-manual.chunked/ +:x-s: .html +// * link to doc -> link +// syntax: {linkdoc}{ld-s}[] +// -> {website-url}docs/.chunked/index.html[] +:linkdoc: {website-url}docs/ +:ld-s: .chunked/index.html +// * link to single doc -> link +// syntax: {linksingledoc}{lsd-s}[] +// -> {website-url}docs/.html[] +:linksingledoc: {website-url}docs/ +:lsd-s: .html +// * link to manpage -> link +// syntax: {linkman2}{lm-s}{lm-c}{lm-e} +// All the fields are mandatory. +// -> {website-url}docs/man/.html[()] +:linkman2: {website-url}docs/man/ +:lm-s: .html[ +:lm-c: ( +:lm-e: )] +endif::env-github[] +endif::asciidoc-vars-nut-included[] +// diff --git a/docs/asciidoc.conf b/docs/asciidoc.conf index fedf952a37..3e8283c624 100644 --- a/docs/asciidoc.conf +++ b/docs/asciidoc.conf @@ -1,12 +1,20 @@ -## NUT macros: linkman, linkdoc +## NUT macros: linkman, linkman2, linkdoc, linksingledoc # # Usage: linkman:command[manpage-section] # This macro allows to handle variable manpage location, depending on the # document type +# Usage: linkman2:command-page[displayed-command,manpage-section] +# Just like linkman macro, but supports different names for the page and the +# command shown. # -# Note, {0} is the manpage section, while {target} is the command. +# Note: +# - in linkman, {0} is the manpage section, while {target} is the command. +# - in linkman2, {0} is the whole list of attributes, {1} is the command to be +# shown, {2} is the manpage section, while {target} is the command page. # -# Example: linkman:ups.conf[5] +# Example: +# linkman:ups.conf[5] +# linkman2:ups_conf[ups.conf,5] # # Show NUT link as: (
); if section is defined, else just show # the command. @@ -16,6 +24,9 @@ # Usage: linkdoc:document[display title,[anchor]] # This macro allows to handle variable NUT documentation location, depending # on the document type +# Usage: linksingledoc:document[display title,[anchor]] +# Just like linkdoc macro, but, when chunked HTML is generated, this one always +# points to a non-chunked file # # Note, {1} is the display title, {2} is the optional anchor name, # {0} is the whole set of args ({1}...{n}) and {target} is the @@ -23,33 +34,54 @@ # Example: # linkdoc:user-manual[user manual,NUT_Security] # linkdoc:developer-guide[developer guide,_status_data] +# linksingledoc:FAQ[shopping tips,_which_ups_should_i_buy] [macros] (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= +(?su)[\\]?(?Plinkman2):(?P\S*?)\[(?P.*?)\]= (?su)[\\]?(?Plinkdoc):(?P\S*?)\[(?P.*?)\]= +(?su)[\\]?(?Plinksingledoc):(?P\S*?)\[(?P.*?)\]= ifdef::basebackend-docbook[] +# AsciiDoc <= ~8.6.9 tests the wrong attribute and, as a consequence of that, it produces both and . +# To workaround this issue, implement the xref macro by ourselves. +[xref-inlinemacro] +{0} +{0%} + ifdef::xhtml11_format[] [linkman-inlinemacro] {target}{0?({0})} +[linkman2-inlinemacro] +{1={target}}{2?({2})} [linkdoc-inlinemacro] {1} +[linksingledoc-inlinemacro] +{1} endif::xhtml11_format[] # FIXME: linkdoc does not support 'anchor' ifdef::chunked_format[] [linkman-inlinemacro] {target}{0?({0})} +[linkman2-inlinemacro] +{1={target}}{2?({2})} [linkdoc-inlinemacro] {1} +[linksingledoc-inlinemacro] +{1} endif::chunked_format[] # PDF output points online versions -# FIXME: linkdoc does not support 'anchor' +# FIXME: linkdoc and linksingledoc don't support 'anchor' ifdef::pdf_format[] [linkman-inlinemacro] -{target}{0?({0})} +{target}{0?({0})} +[linkman2-inlinemacro] +{1={target}}{2?({2})} [linkdoc-inlinemacro] {1} +[linksingledoc-inlinemacro] +{1} endif::pdf_format[] endif::basebackend-docbook[] 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/cables/Makefile.am b/docs/cables/Makefile.am index 8cdb1d3a70..6e8a2c4efd 100644 --- a/docs/cables/Makefile.am +++ b/docs/cables/Makefile.am @@ -1,2 +1,4 @@ +# Network UPS Tools: cable docs + CLEANFILES = *-spellchecked MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/docs/cables/apc.txt b/docs/cables/apc.txt index 5803779024..94d2c32daf 100644 --- a/docs/cables/apc.txt +++ b/docs/cables/apc.txt @@ -258,4 +258,3 @@ had some shielding issues from the manufacturer that could maybe cause | NOTE: Solder a wire to the metal shell for use as a FRAME-GND | | | +-----------------------------------------------------------------+ - diff --git a/docs/cables/ge-imv-victron.txt b/docs/cables/ge-imv-victron.txt index 6a8441746d..d38b83926d 100644 --- a/docs/cables/ge-imv-victron.txt +++ b/docs/cables/ge-imv-victron.txt @@ -56,4 +56,4 @@ UPS PC 9 pin connector ---------------------------------------------- GE is General Electric Company. -On July 31, 2001, the General Electric Company, acquisited IMV (Victron) company. +On July 31, 2001, the General Electric Company, acquired IMV (Victron) company. diff --git a/docs/cables/mgeups.txt b/docs/cables/mgeups.txt index ea4c4f0e62..64a5bdc7dc 100644 --- a/docs/cables/mgeups.txt +++ b/docs/cables/mgeups.txt @@ -49,4 +49,4 @@ Notes trouble communication on some Unices if cabled. However, new models (such as Ellipse, Evolution, Pulsar and Comet EXtreme, EXtreme C, ...) handle this, so you can safely cable pin 6 or - use a standard (bundled) "windows" cable (Ref 66049). \ No newline at end of file + use a standard (bundled) "windows" cable (Ref 66049). diff --git a/docs/cables/powerware.txt b/docs/cables/powerware.txt index 1db1a77a14..0b839910c2 100644 --- a/docs/cables/powerware.txt +++ b/docs/cables/powerware.txt @@ -23,4 +23,3 @@ Pin 2 <-----------> Pin 8 Pin 4 <-----------> Pin 4 Pin 5 <-----------> Pin 1 Shells Grounded Both Ends - diff --git a/docs/cables/repotec.txt b/docs/cables/repotec.txt index 3d26a70019..a7569fc0c7 100644 --- a/docs/cables/repotec.txt +++ b/docs/cables/repotec.txt @@ -23,4 +23,3 @@ PC UPS 5 (GND) -------------+-- 4 | +-- 7 - diff --git a/docs/chunked.xsl b/docs/chunked.xsl index 7f09a80f28..05c2f66324 100644 --- a/docs/chunked.xsl +++ b/docs/chunked.xsl @@ -14,4 +14,5 @@ images/icons/ images/icons/ + diff --git a/docs/ci-farm-lxc-setup.txt b/docs/ci-farm-lxc-setup.txt index 2ab82a4fe5..c388d6fb47 100644 --- a/docs/ci-farm-lxc-setup.txt +++ b/docs/ci-farm-lxc-setup.txt @@ -14,6 +14,31 @@ have in their environments. Common preparations ~~~~~~~~~~~~~~~~~~~ +* Example list of packages for Debian-based systems may include (not + necessarily is limited to): ++ +---- +:; apt install lxc lxcfs lxc-templates \ + ipxe-qemu qemu-kvm qemu-system-common qemu-system-data \ + qemu-system-sparc qemu-system-x86 qemu-user-static qemu-utils \ + virt-manager virt-viewer virtinst ovmf \ + libvirt-daemon-system-systemd libvirt-daemon-system \ + libvirt-daemon-driver-lxc libvirt-daemon-driver-qemu \ + libvirt-daemon-config-network libvirt-daemon-config-nwfilter \ + libvirt-daemon libvirt-clients + +# TODO: Where to find virt-top - present in some but not all releases? +# Can fetch sources from https://packages.debian.org/sid/virt-top and follow +# https://www.linuxfordevices.com/tutorials/debian/build-packages-from-source +# Be sure to use 1.0.x versions, since 1.1.x uses a "better-optimized API" +# which is not implemented by libvirt/LXC backend. +---- ++ +NOTE: This claims a footprint of over a gigabyte of new packages when + unpacked and installed to a minimally prepared OS. Much of that would + be the graphical environment dependencies required by several engines + and tools. + * Prepare LXC and LIBVIRT-LXC integration, including an "independent" (aka "masqueraded) bridge for NAT, following https://wiki.debian.org/LXC and https://wiki.debian.org/LXC/SimpleBridge @@ -26,6 +51,9 @@ Common preparations pairs, one per line (so one per container) *** `systemctl restart lxc-net` to apply config (is this needed after setup of containers too, to apply new items before booting them?) +*** For troubleshooting, see `/var/lib/misc/dnsmasq.lxcbr0.leases` + (in some cases you may have to rename it away and reboot host to + fix IP address delegation) * Install qemu with its `/usr/bin/qemu-*-static` and registration in `/var/lib/binfmt` @@ -33,14 +61,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 +105,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 +113,25 @@ 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 ------ +====== + +Initial container installation (for various guest OSes) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * 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 +143,46 @@ 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.: +** ...or for EOLed distros, use the Debian Archive server. +*** Install the container with Debian Archive as the mirror like this: + ------ :; 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 ------ +*** Note you may have to add trust to their (now expired) GPG keys for + packaging to verify signatures made at the time the key was valid, + by un-symlinking (if appropriate) the debootstrap script such as + `/usr/share/debootstrap/scripts/jessie`, commenting away the + `keyring /usr/share/keyrings/debian-archive-keyring.gpg` line and + setting `keyring /usr/share/keyrings/debian-archive-removed-keys.gpg` + instead. You may further have to edit `/usr/share/debootstrap/functions` + and/or `/usr/share/lxc/templates/lxc-debian` to honor that setting (the + `releasekeyring` was hard-coded in version I had installed), e.g. in the + latter file ensure such logic as below, and re-run the installation: ++ +------ +... + # If debian-archive-keyring isn't installed, fetch GPG keys directly + releasekeyring="`grep -E '^keyring ' "/usr/share/debootstrap/scripts/$release" | sed -e 's,^keyring ,,' -e 's,[ #].*$,,'`" 2>/dev/null + if [ -z $releasekeyring ]; then + releasekeyring=/usr/share/keyrings/debian-archive-keyring.gpg + fi + if [ ! -f $releasekeyring ]; then +... +------ ** ...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 +193,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,9 +215,105 @@ 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. +** For Arch Linux you would need `pacman` tools on the host system, so see + https://wiki.archlinux.org/title/Install_Arch_Linux_from_existing_Linux#Using_pacman_from_the_host_system + for details. + On a Debian/Ubuntu host (assumed ready for NUT builds per + linkdoc:config-prereqs.txt) it would start like this: ++ +------ +:; apt-get update +:; apt-get install meson ninja-build cmake + +# Some dependencies for pacman itself; note there are several libcurl builds; +# pick another if your system constraints require you to: +:; apt-get install libarchive-dev libcurl4-nss-dev gpg libgpgme-dev + +:; git clone https://gitlab.archlinux.org/pacman/pacman.git +:; cd pacman + +# Iterate something like this until all needed dependencies fall +# into line (note libdir for your host architecture): +:; rm -rf build; mkdir build && meson build --libdir=/usr/lib/x86_64-linux-gnu + +:; ninja -C build +# Depending on your asciidoc version, it may require that `--asciidoc-opts` are +# passed as equation to a vale (not space-separated from it). Then apply this: +# diff --git a/doc/meson.build b/doc/meson.build +# - '--asciidoc-opts', ' '.join(asciidoc_opts), +# + '--asciidoc-opts='+' '.join(asciidoc_opts), +# and re-run (meson and) ninja. + +# Finally when all succeeded: +:; sudo ninja -C build install +:; cd +------ ++ +You will also need `pacstrap` and Debian `arch-install-scripts` package +does not deliver it. It is however simply achieved: +------ +:; git clone https://github.com/archlinux/arch-install-scripts +:; cd arch-install-scripts +:; make && sudo make PREFIX=/usr install +:; cd +------ ++ +It will also want an `/etc/pacman.d/mirrorlist` which you can populate for +your geographic location from https://archlinux.org/mirrorlist/ service, +or just fetch them all (don't forget to uncomment some `Server =` lines): +------ +:; mkdir -p /etc/pacman.d/ +:; curl https://archlinux.org/mirrorlist/all/ > /etc/pacman.d/mirrorlist +------ ++ +And to reference it from your host `/etc/pacman.conf` by un-commenting the +`[core]` section and `Include` instruction, as well as adding `[community]` +and `[extra]` sections with same reference, e.g.: +------ +[core] +### SigLevel = Never +SigLevel = PackageRequired +Include = /etc/pacman.d/mirrorlist + +[extra] +### SigLevel = Never +SigLevel = PackageRequired +Include = /etc/pacman.d/mirrorlist + +[community] +### SigLevel = Never +SigLevel = PackageRequired +Include = /etc/pacman.d/mirrorlist +------ ++ +And just then you can proceed with LXC: +------ +:; lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-archlinux-amd64 -t archlinux -- \ + -a x86_64 -P openssh,sudo +------ ++ +In my case, it had problems with GPG keyring missing (using one in host +system, as well as the package cache outside the container, it seems) +so I had to run `pacman-key --init; pacman-key --refresh-keys` on the +host itself. Even so, `lxc-create` complained about updating some keyring +entries and I had to go one by one picking key servers (serving different +metadata) like this: +------ +:; pacman-key --keyserver keyserver.ubuntu.com --recv-key 6D1655C14CE1C13E +------ ++ +In the worst case, see `SigLevel = Never` for `pacman.conf` to not check +package integrity (seems too tied into thinking that host OS is Arch)... ++ +It seems that pre-fetching the package databases with `pacman -Sy` on +the host was also important. + +Initial container-related setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Add the "name,IP" line for this container to `/etc/lxc/dnsmasq-hosts.conf` on the host, e.g.: @@ -174,13 +335,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 +352,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 +362,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 +424,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 +447,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 +459,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 +480,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 +498,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 +522,95 @@ 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. +Arch Linux containers +^^^^^^^^^^^^^^^^^^^^^ + +Arch Linux containers prepared by procedure above include only a minimal +footprint, and if you missed the `-P pkg,list` argument, they can lack +even an SSH server. Suggestions below assume this path to container: +------ +:; ALTROOT=/srv/libvirt/rootfs/jenkins-archlinux-amd64/rootfs/ +------ + +Let `pacman` know current package database: +------ +:; grep 8.8.8.8 $ALTROOT/etc/resolv.conf || (echo 'nameserver 8.8.8.8' > $ALTROOT/etc/resolv.conf) +:; chroot $ALTROOT pacman -Syu +:; chroot $ALTROOT pacman -S openssh sudo +:; chroot $ALTROOT systemctl enable sshd +:; chroot $ALTROOT systemctl start sshd +------ + +This may require that you perform bind-mounts above, as well as "passthrough" +the `/var/cache/pacman/pkg` from host to guest environment (in `virsh edit`, +and bind-mount for `chroot` like for `/proc` et al above). + +It is possible that `virsh console` would serve you better than `chroot`. +Note you may have to first `chroot` to set the `root` password anyhow. + + 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 +621,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 +635,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 +655,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 +676,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 +685,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 +702,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 +750,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 +778,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 +834,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..6fc72d7ddc 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,32 @@ 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. +====== + +NOTE: On Windows systems, the second serial port (COM2), equivalent to +"/dev/ttyS1" on Linux, would be "\\\\.\\COM2". References: linkman:ups.conf[5], linkman:nutupsdrv[8], linkman:bcmxcp_usb[8], -linkman:blazer[8], +linkman:blazer_usb[8], +linkman:nutdrv_qx[8], linkman:richcomm_usb[8], +linkman:riello_usb[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] @@ -185,16 +201,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 +230,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 +244,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,34 +289,42 @@ 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 +As a special case, `LISTEN * ` (with an asterisk) will try to +listen on "ANY" IP address for both and IPv6 (`::0`) and IPv4 (`0.0.0.0`), +subject to `upsd` command-line arguments, or system configuration or support. +Note that if the system supports IPv4-mapped IPv6 addressing per RFC-3493, +and does not allow to disable this mode, then there may be one listening +socket to handle both address families. + 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 +351,32 @@ 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. + +NOTE: Normally `upsd` requires that at all `LISTEN` directives defined +in the 'upsd.conf' file are honoured (except for mishaps possible with +many names of `localhost`), and refuses to start otherwise. If you want +to allow start-up in cases where at least one but possibly not all of +the `LISTEN` directives were honoured, you can enable the +`ALLOW_NOT_ALL_LISTENERS true` option in the 'upsd.conf' file. +Note you would have to restart `upsd` to pick up the `LISTEN`ed IP address +if it appears later, so probably configuring `LISTEN *` is a better choice +in such cases. + On operating systems with service management frameworks, the data server life-cycle is managed by `nut-server` service. @@ -343,6 +389,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 +397,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 +414,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 +473,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 +497,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 +576,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 +594,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 +612,9 @@ 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 (if it had saved +a PID file earlier): upsd -c reload @@ -534,16 +623,27 @@ 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 that such integration +generally forgoes saving of PID files, so `upsd -c ` would not work. +If your workflow requires to manage these daemons beside the OS provided +framework, you can customize it to start `upsd -FF` and save the PID file. + +NUT releases after 2.8.0 define aliases for these units, so if your Linux +distribution uses NUT-provided unit definitions, `systemctl reload upsd` +may also work. + +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 +656,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 +685,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 +727,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 +745,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 +764,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 +787,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 +814,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 +855,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 +908,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 +919,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 +959,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 +977,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 +1008,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 +1030,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..6523b7bbbc 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -21,10 +21,27 @@ Some of the below are alternatives, e.g. compiler toolkits (gcc vs. clang) or SSL implementations (OpenSSL vs Mozilla NSS) -- no problem installing both, at a disk space cost. -NOTE: Some NUT branches may need additional or different software versions +[NOTE] +====== +Some NUT branches may need additional or different software versions that are not yet included into `master` branch dependencies, e.g. the DMF (Dynamic Mapping Files) sub-project needs LUA 5.1 for build and run-time, -and some Python modules for build. +and some Python modules for build, e.g. using OS packaging or custom call +to `pip install pycparser`. + +In case your system still provides a Python 2.x environment (and for some +reason you want to use it instead of Python 3.x), but does not anymore +provide a `pip` nor `pycparser` packages for it, you may need to use an +external bootstrap first, e.g.: + +------ +# Fetch get-pip.py for python 2.7 +:; curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py +:; python2 get-pip.py +:; python2 -m pip --version +:; python2 -m pip install pycparser +------ +====== More packages and/or system setup may be needed to actually run NUT with all features enabled; chapters below concern just with building it. @@ -35,29 +52,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 fightwarn . :; ./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,47 +86,108 @@ 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 -* For intensive rebuilds, `ccache` is recommended +* 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 +====== -* 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 +For intensive rebuilds, `ccache` is recommended. Note that instructions +below detail how to provide its directory with symlinks as `/usr/lib/ccache` +which is not the default case in all OS distributions. Recent versions of +the NUT `ci_build.sh` script allow to override the location by using the +`CI_CCACHE_SYMLINKDIR` environment variable, which is cumbersome and only +recommended for build agents with immutable system areas, etc. -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. +Build prerequisites to make NUT from scratch on various Operating Systems +------------------------------------------------------------------------- + +Debian 10/11/12 +~~~~~~~~~~~~~~~ + +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 \ - make autoconf automake libltdl-dev libtool-bin libtool \ + git python perl curl \ + make autoconf automake libltdl-dev libtool \ valgrind \ cppcheck \ pkg-config \ - gcc g++ clang \ - asciidoc source-highlight python3-pygments dblatex aspell \ + gcc g++ clang + +# NOTE: Older Debian-like distributions may lack a "libtool-bin" +:; apt-get install \ + libtool-bin + +# 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 localization maintenance (currently in Python NUT-Monitor app), +# provide an `msgfmt` implementation, e.g.: +# :; apt-get install gettext +# +# To install the Python NUT-Monitor app, you may need some modules: +# :; apt-get install pip +# For Python3: +# :; python3 -m pip install PyQt5 configparser + +# 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 +# Optionally for sd_notify integration: +:; apt-get install \ + libsystemd-dev + # NOTE: Some older Debian-like distributions, could ship "libcrypto-dev" # and/or "openssl-dev" instead of "libssl-dev" by its modern name +# and may lack a libgpiod2 + libgpiod-dev altogether :; apt-get install \ libcppunit-dev \ libssl-dev libnss3-dev \ @@ -120,65 +201,87 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). libavahi-common-dev libavahi-core-dev libavahi-client-dev # For libneon, see below +# NOTE: Older Debian-like distributions may lack a "libgpiod-dev" +# Others above are present as far back as Debian 7 at least :; apt-get install \ - lua5.1-dev + libgpiod-dev + +# NOTE: Some distributions lack a lua*-dev and only offer the base package +:; apt-get install lua5.1 +:; apt-get install lua5.1-dev || true :; 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 -in the target chroot (or do this from the running container). +NOTE: For Jenkins agents, also need to `apt-get install openjdk-11-jdk-headless`. +You may have to ensure that `/proc` is mounted in the target chroot +(or do this from the running container). For DMF, additionally install `python-pycparser` (or in newer distributions, -an explicitly versioned package, e.g. `python3-pycparser`), and `libxml2-dev`. +an explicitly versioned package, e.g. `python3-pycparser` and hacks detailed +above if you want to test with Python 2.x specifically), and `libxml2-dev`; +on some distributions you may also need a separate `libxml2-utils` package +for the `xmllint` tool: +------ +:; apt-get install libxml2-dev +:; apt-get install libxml2-utils || true -CentOS 7 -~~~~~~~~ +:; apt-get install python-pycparser || apt-get install python3-pycparser +------ + +CentOS 6 and 7 +~~~~~~~~~~~~~~ CentOS is another popular baseline among Linux distributions, being a free derivative of the RedHat Linux, upon which many other distros are based as well. These systems typically use the RPM package manager, using directly `rpm` command, or `yum` or `dnf` front-ends depending on their generation. +For CI farm container setup, prepared root filesystem archives from +http://download.proxmox.com/images/system/ worked sufficiently well. + +Prepare CentOS repository mirrors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 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,42 +292,97 @@ 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`: +For systemd support on CentOS 7 (no equivalent found for CentOS 6), +you can use backports repository below: +------ +:; curl https://copr.fedorainfracloud.org/coprs/jsynacek/systemd-backports-for-centos-7 + > /etc/yum.repos.d/systemd-backports-for-centos-7.repo +------ + +For CentOS 6 (the oldest I could try) the situation is similar, with sites like +https://www.getpagespeed.com/server-setup/how-to-fix-yum-after-centos-6-went-eol +detailing how to replace `/etc/yum.repos.d/` contents (you can wholesale rename +the existing directory and populate a new one with `curl` downloads from the +article), and additional key trust for EPEL packages: +------ +:; yum install https://dl.fedoraproject.org/pub/archive/epel/6/x86_64/epel-release-6-8.noarch.rpm +------ + +Set up CentOS packages for NUT +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Instructions below apply to both CentOS 6 and 7 (a few nuances for 6 commented). + +General developer system helpers mentioned in link:ci-farm-lxc-setup.txt[]: ------ :; yum update :; yum install \ - sudo vim mc p7zip pigz pbzip2 + sudo vim mc p7zip pigz pbzip2 tar +------ + +To have SSH access to the build VM/Container, you may have to install and +enable it: +------ +:; yum install \ + openssh-server openssh-clients + +:; chkconfig sshd on +:; service sshd start + +# If there are errors loading generated host keys, remove mentioned files +# including the one with .pub extension and retry with: +#:; service sshd restart ------ 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. +NOTE: On CentOS, `libusb` means 0.1.x and `libusbx` means 1.x.x API version +(latter is not available for CentOS 6). 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 \ + file \ + 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' +# Note that CentOS 6 includes python-2.6.x and does not serve newer versions + +# 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" +# Optionally for sd_notify integration (on CentOS 7+, not on 6): +:; yum install \ + systemd-devel + +# NOTE: "libusbx" is the CentOS way of naming "libusb-1.0" (not in CentOS 6) # vs. the older "libusb" as the package with "libusb-0.1" :; yum install \ cppunit-devel \ @@ -241,6 +399,7 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos #?# is python-augeas needed? exists at least... #?# no (lib)i2c-devel ... #?# no (lib)ipmimonitoring-devel ... would "freeipmi-ipmidetectd" cut it at least for run-time? +#?# no (lib)gpio(d)-devel - starts with CentOS 8 (or extra repositories for later minor releases of CentOS 7) # Some NUT code related to lua may be currently limited to lua-5.1 # or possibly 5.2; the former is default in CentOS 7 releases... @@ -249,45 +408,328 @@ 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+). +(not available for CentOS 6). + +Arch Linux +~~~~~~~~~~ + +Update the lower-level OS and package databases: +------ +:; pacman -Syu +------ + +Install tools and prerequisites for NUT: +------ +:; pacman -S --needed \ + base-devel \ + autoconf automake libtool libltdl \ + clang gcc \ + ccache \ + git \ + vim python perl \ + pkgconf \ + cppcheck valgrind + +# For spell-checking, highly recommended if you would propose pull requests: +:; pacman -S --needed \ + aspell en-aspell + +# For man-page doc types generation: +:; pacman -S --needed \ + asciidoc + +# For other doc types (PDF, HTML) generation - massive packages (TEX, X11): +:; pacman -S --needed \ + source-highlight dblatex + +# For CGI graph generation - massive packages (X11): +:; pacman -S --needed \ + gd + +# Optionally for sd_notify integration: +:; pacman -S --needed \ + systemd + +:; pacman -S --needed \ + cppunit \ + openssl nss \ + augeas \ + libusb \ + neon \ + net-snmp \ + freeipmi \ + avahi + +#?# no (lib)gpio(d) + +:; pacman -S --needed \ + lua51 + +:; pacman -S --needed \ + bash dash busybox ksh93 +------ + +Recommended for NUT CI farm integration (matching specific toolkit versions +in a build matrix), and note the unusual location of `/usr/lib/ccache/bin/` +for symlinks: +------ +:; gcc --version +gcc (GCC) 12.2.0 +... + +# Note: this distro delivers "gcc" et al as file names +# so symlinks like this may erode after upgrades. +# TODO: Rename and then link?.. +:; (cd /usr/bin \ + && for V in 12 12.2.0 ; do for T in gcc g++ cpp ; do \ + ln -fsr $T $T-$V ; \ + done; done) +:; (cd /usr/lib/ccache/bin/ \ + && for V in 12 12.2.0 ; do for T in gcc g++ ; do \ + ln -fsr /usr/bin/ccache $T-$V ; \ + done; done) + + +:; clang --version +clang version 14.0.6 +... + +:; (cd /usr/bin && ln -fs clang-14 clang++-14 && ln -fs clang-14 clang-cpp-14) +:; (cd /usr/lib/ccache/bin/ \ + && for V in 14 ; do for T in clang clang++ ; do \ + ln -fsr /usr/bin/ccache $T-$V ; \ + done; done) +------ + +Also for CI build agents, a Java environment (JDK11+ since summer 2022) is +required: +------ +# Search for available Java versions: +:; pacman -Ss | egrep 'jre|jdk' + +# Pick one: +:; pacman -S --needed \ + jre11-openjdk-headless +------ + +Slackware Linux 15 +~~~~~~~~~~~~~~~~~~ + +Another long-term presence in the Linux landscape, and sometimes the baseline +for appliances, the Slackware project recently hit release 15 in 2022, averaging +two years per major release. + +It can be installed e.g. in a VM, using ISO images from the project site; see: + +* http://www.slackware.com/ => http://www.slackware.com/getslack/ => + https://mirrors.slackware.com/slackware/slackware-iso/slackware64-15.0-iso/ +* https://slackware.nl/slackware/slackware64-current-iso/ + +You would have to first log in as `root` and run `cgdisk` to define partitioning +for your virtual HDD, such as the common `/boot`, `swap` and `/` Linux layout, +and run `setup` to install "everything". + +Note that "out of the box" Slackware does not currently on networked package +repositories and calculated dependency trees, so one has to know exactly what +they want installed. + +Third-party projects for package managers are also available, e.g. +link:https://slackpkg.org/documentation.html[`slackpkg`] => see also +https://docs.slackware.com/slackware:slackpkg and +https://slackpkg.org/stable/ : +---- +:; wget https://slackpkg.org/stable/slackpkg-15.0.10-noarch-1.txz && \ + installpkg slackpkg-15.0.10-noarch-1.txz +---- + +Uncomment a mirror from `/etc/slackpkg/mirrors` according to your location +and other preferences (or use the top-listed default), and begin with: +---- +:; slackpkg update +---- + +Note that packages may be only installed or re-installed/upgraded as separate +explicit operations, so the procedure to bring your system into needed shape +is a bit cumbersome (and each command may by default be interactive with a +choice menu), e.g.: +---- +:; for P in \ + bash mc vim sudo \ + ; do slackpkg info "$P" || slackpkg install $P || break ; done +---- + +For procedures below, this is automated via `root` profile to become a +`slackpkg-install` command: +---- +:; grep "slackpkg-install" ~/.profile || { + echo 'slackpkg-install() { for P in "$@" ; do echo "=== $P:"; slackpkg info "$P" || slackpkg install "$P" || break ; done; }' >> ~/.profile + } +:; . ~/.profile +---- + +If something has a hiccup, it suggests to look for the right name, e.g.: +---- +:; slackpkg search python +---- + +For NUT dependencies and build tools: +---- +:; slackpkg update + +# Baseline toolkits: +# Note there is no cppcheck, cppunit, valgrind... +# Note clang compiler tools are part of llvm package +:; slackpkg-install \ + ccache time \ + coreutils diffutils \ + git python3 perl curl \ + make autoconf automake libtool binutils \ + pkg-config \ + gcc llvm + +# For spell-checking, highly recommended if you would propose pull requests: +:; slackpkg-install aspell{,-en} + +# Note there is no direct "asciidoc" nor "a2x", just the competing project +# "rubygem-asciidoctor" that NUT currently has no recipes for, so you can +# not compile man/html/pdf docs here, per the default repository. See below +# for tools from alternative repositories, which seem to work well. Manpage +# compilation would require docbook-xml resources; older versions are in +# this package https://slackbuilds.org/repository/15.0/system/docbook-xml/ +# and recent ones are in not-installed part of main repository: +:; slackpkg-install linuxdoc-tools + +# More on Python (for NUT-Monitor UI): +:; slackpkg-install \ + python-pip qt5 gettext-tools gettext + +# For CGI graph generation - massive packages (X11): +:; slackpkg-install \ + gd + +# General dependencies: +:; slackpkg-install \ + openssl openssl-solibs mozilla-nss \ + libusb \ + net-snmp \ + neon + +# Shells: +:; slackpkg-install \ + bash dash ksh93 +---- + +Some more packages are available on the side, including Java (useful +e.g. to make this environment into a fully fledged Jenkins worker). +Other common NUT dependencies absent from primary Slackware repositories +can be found and downloaded (seek `*.txz` package files, although a few +are named `*.tgz`) from here, and passed to `installpkg`: + +* http://www.slackware.com/~alien/slackbuilds/openjdk11/ +* http://www.slackware.com/~alien/slackbuilds/asciidoc/ +* http://www.slackware.com/~alien/slackbuilds/cppunit/ +* http://www.slackware.com/~alien/slackbuilds/lua/ (5.1 in "stable") and + http://www.slackware.com/~alien/slackbuilds/lua53/ (5.4 in "current" + as of Slackware 15.1 candidate in the works) -- note that LUA is not + needed for the current NUT code base, but may become needed after import + of features from forks + +...and even the environment for Windows cross-builds, ancient compilers +and modern toolkits to cover all bases: + +* http://www.slackware.com/~alien/slackbuilds/MinGW-w64/ +* http://www.slackware.com/~alien/slackbuilds/docker/ +* http://www.slackware.com/~alien/slackbuilds/gcc34/ +* http://www.slackware.com/~alien/slackbuilds/gcc5/ + +FWIW, another "more official" but older Java package seems to be at: + +* http://www.slackware.com/~alien/slackbuilds/openjdk/ +* https://slackbuilds.org/repository/15.0/development/jdk/ + +An example routine to install the latest instance of a package could be +like this: +---- +:; wget -m -l1 http://www.slackware.com/~alien/slackbuilds/asciidoc/pkg/ && \ + find . -name '*.t?z' + +:; installpkg ./www.slackware.com/~alien/slackbuilds/asciidoc/pkg/asciidoc-8.1.0-noarch-2.tgz +---- + +Upon community members' recommendations, Sotirov's SlackPack is also considered +a reputable repository: https://sotirov-bg.net/slackpack/ and should cover most +if not all of the dependencies required for NUT building (including PowerMan, +IPMI etc.) + +NOTE: If setting up a CI farm agent with builds in RAM disk, keep in mind that +default mount options for `/dev/shm` preclude script execution. Either set up +the agent in a non-standard fashion (to use another work area), or if this is +a dedicated machine -- relax the mount options in `/etc/fstab`. Here is an +example with `noexec` which we *must avoid* for such use-case: +---- +:; mount | grep /dev/shm +tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,inode64) +---- 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 \ cppunit \ - openssl nss \ + nss \ augeas \ libmodbus \ neon \ @@ -296,54 +738,64 @@ below. freeipmi \ avahi +# NOTE: At least on FreeBSD 12, system-provided crypto exists and is used +# by libnetsnmp, libneon, etc. - but is not marked as a package. Conversely, +# the openssl-1.1.1k (as of this writing) can be installed as a package into +# /usr/local/lib and then causes linking conflicts. The core system-provided +# build of openssl does include headers and is useful for NUT build "as is". +# ONLY INSTALL THIS PACKAGE IF REQUIRED (may get problems to rectify later): +:; test -e /lib/libcrypto.so -a -e /usr/lib/libssl.so || \ + pkg install openssl + :; pkg install \ lua51 :; 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 suffixed symlinks to compiler tools (e.g. `gcc-10` beside `gcc10` installed by package). -NOTE: For Jenkins agents, also need to `pkg install openjdk8` (or 11+) -- -and do note its further OS configuration suggestions for special filesystem -mounts. +NOTE: For Jenkins agents, also need to `pkg install openjdk11` (11 or 17 +required since summer 2022) -- 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 to `pkg install libxml2` (might be pulled as a dependency of e.g. `libneon` already). -OpenBSD 6.4 +OpenBSD 6.5 ~~~~~~~~~~~ -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. @@ -351,94 +803,266 @@ below. OpenBSD delivers many versions of numerous packages, you should specify your pick interactively or as part of package name (e.g. `autoconf-2.69p2`). +NOTE: For the purposes of builds with Jenkins CI agents, since summer 2022 it +requires JDK11 which was first delivered with OpenBSD 6.5. Earlier iterations +used OpenBSD 6.4 and version nuances in this document may still reflect that. + During builds, you may have to tell system dispatcher scripts which version to use (which feels inconvenient, but on the up-side for CI -- this system allows to test many versions of auto-tools in the same agent), e.g.: ------ -:; export AUTOCONF_VERSION=2.69 AUTOMAKE_VERSION=1.10 +:; export AUTOCONF_VERSION=2.69 AUTOMAKE_VERSION=1.13 ------ 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. -NOTE: The OpenBSD 6.4 `install64.iso` installation includes a set of packages +NOTE: The OpenBSD 6.5 `install65.iso` installation includes a set of packages that seems to exceed whatever is available on network mirrors; for example, the CD image included `clang` program while it is not available to `pkg_add`, -at least not via http://ftp.netbsd.hu/mirrors/openbsd/6.4/packages/amd64/ +at least not via http://ftp.netbsd.hu/mirrors/openbsd/6.5/packages/amd64/ mirror. The `gcc` version on CD image differed notably from that in the -networked repository (4.2.x vs 4.9.x) +networked repository (4.2.x vs 4.9.x). +You may have to echo a working base URL (part before "6.5/..." into the +`/etc/installurl` file, since the old distribution is no longer served by +default site. + +------ +# Optionally, make the environment comfortable, e.g.: +:; pkg_add sudo bash mc wget rsync ----- :; 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): +# :; pkg_add python-2.7.15p0 py-pip +# and/or: +# :; pkg_add python-3.6.6p1 py3-pip +# although you might succeed specifying shorter names and the packager +# will offer a list of matching variants (as it does for "python" above). +# 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 \ openssl nss \ augeas \ libusb1 \ - neon \ net-snmp \ avahi +# For netxml-ups driver the library should suffice; however for nut-scanner +# you may currently require to add a `libneon.so` symlink (the package seems +# to only deliver a numbered SO library name), e.g.: +:; pkg_add neon && \ + if ! test -s /usr/local/lib/libneon.so ; then + ln -s "`cd /usr/local/lib && ls -1 libneon.so.* | sort -n | tail -1`" /usr/local/lib/libneon.so + fi + # Select a LUA-5.1 (or possibly 5.2?) version :; pkg_add \ lua :; pkg_add \ - bash dash busybox ksh93 ----- + bash dash ksh93 -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 \ + argp-standalone \ 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 ; \ ) ----- + +:; ( cd /usr/bin && for T in gcc g++ cpp ; do ln -s "$T" "$T-4.2.1" ; done ) +:; ( cd /usr/lib/ccache && for T in gcc g++ cpp ; do ln -s "$T" "$T-4.2.1" ; done ) + +:; ( cd /usr/bin && for T in clang clang++ clang-cpp ; do ln -s "$T" "$T-7.0.1" ; done ) +:; ( cd /usr/lib/ccache && for T in clang clang++ clang-cpp ; do ln -s "$T" "$T-7.0.1" ; 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 `gcc` installed by package) into `/usr/lib/ccache`. -NOTE: For Jenkins agents, also need to `pkg_add jre` or `pkg_add jdk` -(pick version 1.8 or 8, or 11+). +NOTE: For Jenkins agents, also need to `pkg_add jdk` (if asked, pick version +11 or 17); can request `pkg_add jdk%11`. You would likely have to update +the trusted CA store to connect to NUT CI, see e.g. (raw!) download from +https://gist.github.com/galan/ec8b5f92dd325a97e2f66e524d28aaf8 +but ensure that you run it with `bash` and it does `wget` the certificates +(maybe with `--no-check-certificate` option if the OS does not trust current +internet infrastructure either), and revise the suggested certificate files +vs. https://letsencrypt.org/certificates/ and/or comments to that gist. 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). -OpenIndiana 2021.04 +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 may be not part of NetBSD +base installation. It is not required for "legacy" builds arranged by just +`autogen.sh` and `configure` scripts. + +Also note that the `install-sh` helper added by autotools from OS-provided +resources when generating the `configure` script may be old and its directory +creation mode is not safe for parallel-`make` installations. If this happens, +you can work around by `make MKDIRPROG="mkdir -p" install -j 8` for example. + +------ +:; 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] +====== +(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 +------ +====== + +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 +1074,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 +1134,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. @@ -513,8 +1190,9 @@ 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+). +NOTE: For Jenkins agents, also need to `pkg install runtime/java/openjdk11` +for JRE/JDK 11. Java 11 or 17 is required to run Jenkins agents after +summer 2022. For DMF, additionally install `library/python/pycparser` (or a versioned one matching your installed Python distribution, e.g. `library/python/pycparser-39` @@ -533,25 +1211,38 @@ 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 ----- -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). +# 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' +------ + +Your OmniOS version may lack 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: ----- +NOTE: As of July 2022, a `libusb-1` package recipe was proposed for the +`omnios-extra` repository (NUT itself and further dependencies may also +appear there, per +link:https://github.com/networkupstools/nut/issues/1498[issue #1498]). + +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,15 +1251,346 @@ 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 whatever they need to happen. + +NOTE: For Jenkins agents, also need to `pkg install runtime/java/openjdk11` +for JRE/JDK 11. Java 11 or 17 is required to run Jenkins agents after +summer 2022. + +Solaris 8 +~~~~~~~~~ + +Builds for a platform as old as this are not currently covered by CI, however +since the very possibility of doing this was recently verified, some notes +follow. + +For context: Following a discussion in the mailing list starting at +https://alioth-lists.debian.net/pipermail/nut-upsuser/2022-December/013051.html +and followed up by GitHub issues and PR: + +* https://github.com/networkupstools/nut/issues/1736 +* https://github.com/networkupstools/nut/issues/1737 + (about a possible but not yet confirmed platform problem) +* https://github.com/networkupstools/nut/pull/1738 + +...recent NUT codebase was successfully built and self-tested in a Solaris 8 +x86 VM (a circa 2002 release), confirming the project's adherence to the +goal that if NUT ran on a platform earlier, so roughly anything POSIX-ish +released this millennium and still running, it should still be possible -- +at least as far as our part of equation is concerned. + +That said, platform shows its age vs. later standards (script interpreters +and other tools involved), and base "complete install" lacked compilers, +so part of the tested build platform setup involved third-party provided +package repositories. + +One helpful project was extensive notes about preparation of the Solaris 8 VM +(and our further comments there), which pointed to the still active "tgcware" +repository and contains scripts to help prepare the freshly installed system: + +* https://github.com/mac-65/Solaris_8_x86_VM +* https://github.com/mac-65/Solaris_8_x86_VM/issues/1 +* http://jupiterrise.com/tgcware/sunos5.8_x86/stable/ + +Note that scripts attached to the notes refer to older versions of the +packages than what is currently published, so I ended up downloading +everything from the repository into the VM and using shell wildcards +to pick the packages to install (mind the package families with similar +names when preparing such patterns). + +After the OS, tools and feasible third-party dependencies were installed, +certain environment customization was needed to prepare for NUT build in +particular (originally detailed in GitHub issues linked above): + +* For `CONFIG_SHELL`, system `dtksh` seems to support the syntax (unlike + default `/bin/sh`), but for some reason segfaults during `configure` tests. + Alternatively `/usr/tgcware/bin/bash` (4.4-ish) can be used successfully. + System-provided bash 2.x is too old for these scripts. +* To run `ci_build.sh` CI/dev-testing helper script, either the shebang + should be locally fixed to explicitly call `/usr/tgcware/bin/bash`, or + the build environment's `PATH` should point to this `bash` implementation + as a first hit. If we want to primarily use OS-provided tools, this latter + option may need a bit of creative setup; I made a symlink into the + `/usr/lib/ccache` directory which has to be first anyway (before compilers). +* The system-provided default `grep` lacks the `-E` option which was preferred + over generally obsoleted `egrep` since + link:https://github.com/networkupstools/nut/pull/1660[PR #1660] -- however + pre-pending `/usr/xpg4/bin` early in the `PATH` fixes the problem. +* The builds of `gcc` in TGCWARE repository were picky about shared objects + linking as needed to run them, so `LD_LIBRARY_PATH` had to refer to its + library directories (generally this is frowned upon and should be a last + resort). +* Due to lack of Python in that OS release, NUT augeas support had to be + disabled when preparing the build from Git sources (generated files may + be available as part of distribution tarballs however): + `WITHOUT_NUT_AUGEAS=true; export WITHOUT_NUT_AUGEAS; ./autogen.sh` + +Overall, the successful test build using the NUT standard CI helper script +`ci_build.sh` had the following shell session settings: + +---- +### Common pre-sets from .profile or .bashrc: +### bash-2.03$ echo $PATH +### /usr/bin:/usr/dt/bin:/usr/openwin/bin:/bin:/usr/ucb:/usr/tgcware/bin:/usr/tgcware/gnu:/usr/tgcware/gcc42/bin:/usr/tgcware/i386-pc-solaris2.8/bin + +### bash-2.03$ echo $LD_LIBRARY_PATH +### /usr/lib:/usr/tgcware/lib:/usr/tgcware/gcc42/lib:/usr/tgcware/i386-pc-solaris2.8/lib + +### Further tuning for the build itself: +:; git clean -fffdddxxx +:; CONFIG_SHELL=/usr/tgcware/bin/bash \ + WITHOUT_NUT_AUGEAS=true \ + PATH="/usr/xpg4/bin:$PATH" \ + /usr/tgcware/bin/bash ./ci_build.sh +---- + +MacOS with homebrew +~~~~~~~~~~~~~~~~~~~ + +Some CI tests happen on MacOS using a mix of their default xcode environment +for compilers, and Homebrew community packaging for dependencies (including +`bash` since the system one is too old for `ci_build.sh` script syntax). + +See `.travis.yml` and `.circleci/config.yml` for practical details of the +setup. + +Currently known dependencies for basic build include: +---- +# Optional for a quick spin: +:; HOMEBREW_NO_AUTO_UPDATE=1; export HOMEBREW_NO_AUTO_UPDATE + +# Required: +:; brew install ccache bash libtool pkg-config gd libusb neon net-snmp openssl +---- + +Note that ccache is installed in a different location than expected by default +in the `ci_build.sh` script, so if your system allows to add the symbolic link +to `/usr/local/opt/ccache/libexec` as `/usr/lib/ccache` -- please do so as the +easiest way out. Alternatively, to prepare building sessions with `ci_build.sh` +you can: +---- +:; export CI_CCACHE_SYMLINKDIR="/usr/local/opt/ccache/libexec" +---- + + +Windows builds +~~~~~~~~~~~~~~ + +There have been several attempts to adjust NUT codebase to native builds +for Windows, as well as there are many projects helping to produce portable +programs. + +Further TODO for Windows would include: + +* MSVCRT (ubiquitous) vs UCRT (more standards compliant, but since Windows 10) + https://docs.microsoft.com/en-us/cpp/porting/upgrade-your-code-to-the-universal-crt?view=msvc-160 + +* libusb-0.1 vs libusb-1.0 + +NOTE: Native mingw, MSYS2, etc. builds on Windows are known to suffer from +interaction with antivirus software which holds executable files open and +so not writable by the linker. This may cause random steps in the `configure` +script or later during the build to fail. If that happens to you, disable the +antivirus completely or exempt at least the NUT build area from protection. + +Windows with mingw +^^^^^^^^^^^^^^^^^^ + +See `scripts/Windows/README.adoc` for original recommendations for the effort, +including possibilities of cross-builds with mingw available in Linux. + +Unfortunately these did not work for me at the time of testing, yielding +some issues downloading mingw both in Windows and Linux environments. +So I explored other downloads, as detailed below. + +See also: + +* https://winlibs.com/ +* https://azrael.digipen.edu/~mmead/www/public/mingw/ +* https://www.mingw-w64.org/downloads/ + +NOTE: Seems the mingw installer has problems with current authentication +and redirect on SourceForge. You can download and unpack 7z archives from +https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ +into e.g. `C:\Progra~1\mingw-w64\x86_64-8.1.0-release-posix-seh-rt_v6-rev0` +location on your Windows system. Then for building further NUT dependencies +see `scripts/Windows/README.adoc`. + +Windows with MSYS2 +^^^^^^^^^^^^^^^^^^ + +The MSYS2 ecosystem is available at https://www.msys2.org/ and builds upon +earlier work by link:https://www.mingw-w64.org[MinGW-w64] (in turn a fork +of link:https://sourceforge.net/projects/mingw/[MinGW.org (aka mingw-w32)]) +and link:http://cygwin.com/[Cygwin] projects, to name a few related efforts. +It also includes `pacman` similar to that in Arch Linux for easier dependency +installation, and many packages are available "out of the box" this way. + +The project is currently sponsored by Microsoft and seems to be supported by +Visual Studio Code IDE for building and debugging projects, for more details +see https://code.visualstudio.com/docs/cpp/config-mingw + +Notable pages of the project include: + +* https://www.msys2.org/ with current download link and first-installation + instructions +* https://www.msys2.org/wiki/MSYS2-introduction/ for general overview +* https://packages.msys2.org/search?t=binpkg for search in package repository + +After downloading and installing MSYS2 archive for the first time, they +suggest to start by updating the base ecosystem (using their terminal): +---- +:; pacman -Syu +---- + +Wait for metadata and base package downloads, agree that all MSYS2 programs +including your terminal would be closed/restarted, and wait for this stage +to complete. + +Run it again to refresh more of the ecosystem, now without restarting it: +---- +:; pacman -Syu +---- + +Finally, install tools and prerequisites for building NUT; note that some +of the recommended package names are "umbrellas" for several implementations, +and the `pacman` would ask you which (or "all") to install in those cases. + +NOTE: Suggestions below use `x86_64` generic variants where possible, and +`clang` where available to try both build toolkits on the platform. If you +want to build `i686` (32-bit) or alternate backends (e.g. `ucrt` instead +of default `msvcrt`), poke the repository search to see what is available. + +NOTE: To build NUT with `ci_build.sh` (and generally -- to help `configure` +script find the dependencies listed below), start the terminal session with +"MSYS2 MinGW x64" shortcut. Other options set up the environment variables +for toolkits listed in their shortcut names, and so tend to prefer "wrong" +flags and paths to dependencies (if you have several variants installed). +The "MSYS2 MinGW UCRT x64" was also reported to work. + +To avoid toolkit variant mismatches, you may require to use their specific +builds preferentially: +---- +PATH="/mingw64/bin:$PATH" +export PATH +---- +...and also add these lines to the `~/.bashrc` file. + +---- +# This covers most of the common FOSS development baseline, including +# python, perl, autotools, gcc, clang, git, binutils, make, pkgconf... +:; pacman -S --needed \ + base-devel mingw-w64-x86_64-toolchain \ + autoconf-wrapper automake-wrapper libtool mingw-w64-x86_64-libltdl \ + clang gcc \ + ccache mingw-w64-x86_64-ccache \ + git aspell aspell-en \ + vim python \ + mingw-w64-x86_64-python-pygments + +# PThreads come as an extra feature; note there are many variants, +# see https://packages.msys2.org/search?t=binpkg&q=pthread +:; pacman -S --needed \ + mingw-w64-x86_64-winpthreads-git \ + mingw-w64-clang-x86_64-winpthreads-git + +# Note that MSYS2 includes libusb-1.0 "natively" +# The NUT codebase adjustments for Windows might at this moment expect older +# ecosystem via https://github.com/mcuee/libusb-win32 -- subject to fix then. +:; pacman -S --needed \ + mingw-w64-x86_64-libusb \ + mingw-w64-clang-x86_64-libusb + +# Seems that the older libusb-win32 (libusb-0.1) is also available as: +:; pacman -S --needed \ + mingw-w64-x86_64-libusb-win32 \ + mingw-w64-clang-x86_64-libusb-win32 + +# Alternately there is libusb-compat (libusb-1.0 codebase exposing the older +# libusb-0.1 API) which SHOULD NOT be installed along with the real libusb-0.1: +# :; pacman -S --needed mingw-w64-x86_64-libusb-compat-git mingw-w64-clang-x86_64-libusb-compat-git + +# This also pulls *-devel of several other projects: +:; pacman -S --needed \ + mingw-w64-x86_64-neon libneon-devel + +# Other dependencies: +:; pacman -S --needed \ + mingw-w64-x86_64-libmodbus-git \ + mingw-w64-clang-x86_64-libmodbus-git \ + mingw-w64-x86_64-libgd \ + mingw-w64-clang-x86_64-libgd + +# For C++ tests: +:; pacman -S --needed \ + mingw-w64-x86_64-cppunit \ + mingw-w64-clang-x86_64-cppunit +---- + + +`ccache` wrapper scripts are available as e.g. `/mingw64/lib/ccache/bin/gcc` +and lack a set for `clang` tools; easy-peasy fix with: + +---- +:; cd /mingw64/lib/ccache/bin +:; for T in clang clang++ clang-cpp ; do sed "s/gcc/$T/" < gcc > "$T" ; chmod +x "$T" ; done +---- + +Note that default `ccache` seems quirky on Windows MSYS2, possibly due to +mixing of the path separator characters and/or embedding and choking on +the `C:` in path names. Overall it seems unable to create the cache files +after it has created the cache directory tree (though you might have to +pre-create the `${HOME}/.ccache` anyway, as NUT `ci_build.sh` script does. +As found in experimentation, setting the `PATH` consistently for toolkits +involved is very important. + +* https://github.com/ccache/ccache/discussions/784 +* https://sourceforge.net/p/msys2/tickets/253/ + +Notable packages *not found* in the repo: + +* snmp (net-snmp, ucd-snmp) -- instructions in `scripts/Windows/README.adoc` + document now covers building it from source in MSYS2 MinGW x64 environment, + essentially same as for Linux cross builds with proper `ARCH` and `PREFIX` +* libregex (C version, direct NUT `configure` script support was added by + the Windows branch); MSYS2 however includes `libpcre` pulled by some + of the dependencies above... +* augeas +* avahi +* powerman +* ipmi + +Not installed above (yet?): + +* https://packages.msys2.org/search?t=binpkg&q=serial + -- for these need to first check if termios is part of baseline + +Note that `ccache` symlinks for MSYS2 are installed into `/usr/lib/ccache/bin` +directory (not plain `/usr/lib/ccache` as elsewhere). + +NOTE: After you successfully build NUT (perhaps using `ci_build.sh`), if you +install it into a prototype area by `make DESTDIR=... install` then you should +add the third-party shared libraries involved, for that file set to be usable. +Something along these lines: +------ +:; find "$DESTDIR" -name '*.exe' -type f | while read F ; do ldd "$F" \ + | grep ' /mingw64/' ; done | awk '{print $3}' | sort | uniq \ + | while read LIB ; do cp -pf "$LIB" "$DESTDIR/mingw64/bin/" ; done +------ +Keep in mind that a similar trick (or links to `*.dll` -- and symlinks are +problematic on that platform) may be needed in other directories, such as +`sbin` and `cgi-bin`: +------ +:; ( cd "$DESTDIR/mingw64/bin/" && ln *.dll ../sbin && ln *.dll ../cgi-bin ) +------ diff --git a/docs/configure.txt b/docs/configure.txt index 2905c8d28a..3cc3faac24 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -3,20 +3,98 @@ Configure options ================= endif::website[] +[NOTE] +====== +When building NUT from Git sources rather than the distribution tarballs, +you would have to generate the `configure` script and some further needed +files by running `./autogen.sh`. This in turn may require additional tools +and other dependencies on your build environment (referenced just a bit +below). For common developer iterations, porting to new platforms, +or in-place testing, running the `./ci_build.sh` script can be a helpful +one-stop solution. ++ +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). +====== + There are a few options reviewed below that can be given to `configure` script to tweak your compilations. See also `./configure --help` for a current and complete listing for the current version of the codebase. +NUT tracks `configure` options used during build, so you can view them +to produce a replacement by asking NUT programs for `--help` or for +`--version` with debugging enabled (first), e.g.: + +---- +:; upsd -DV +Network UPS Tools upsd 2.8.1 +Network UPS Tools version 2.8.1 configured with flags: --with-all=auto --with-doc=skip ... +---- + +A more industrial approach is to use `lib/libupsclient-config --config-flags` +where supported. + +Keeping a report of NUT configuration +------------------------------------- + + --enable-keep_nut_report_feature + +If this option is enabled (not currently default), the report displayed +by `configure` script will be kept in a file when the script ends, and +installed into the data directory. + +In-place replacement defaults +----------------------------- + +A common situation for NUT builds is to verify whether current version +of the codebase (e.g. recent and not-yet-packaged release, or a Git branch) +solves some issues of an existing deployment. Such tests are simplified +if the new build of NUT plays by the same systems integration rules as +the already deployed (e.g. package-delivered) version, specifically about +filesystem access permissions and configuration file locations. + + --enable-inplace-runtime + +Tries to detect and pre-set `configure` defaults for run-time settings +(which you can still override if needed, but no longer *must* specify +explicitly to be on same page as the existing setup), e.g.: + +* --sysconfdir +* --with-user +* --with-group + +If the installed NUT version supports reporting of `CONFIG_FLAGS` used +during its build, the `configure` script will try to take those values +into account when running in this mode. + +NOTE: This does not currently rely on the configuration report optionally +installed by `--enable-keep_nut_report_feature` above, but might do so +eventually. + 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 +104,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 +119,88 @@ 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. +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. +Drivers with dynamic data mapping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + --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) and xmllint / lua 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 +GPIO bus 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-gpio + +Build and install GPIO drivers (default: auto-detect) + +Note that on Linux you need to install libgpiod library and development package +or files. This seems to be present in distributions released after roughly 2018. +Other platforms are not currently supported, but may be in the future. + +Modbus drivers +~~~~~~~~~~~~~~ + + --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 +208,240 @@ 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). + +The possible documentation type values are: -If the "--with-doc" argument is passed without a list, or lists just "=yes" -or "=all", it enables all supported formats with a "=yes". +* `html-single` for single page HTML, +* `html-chunked` for multi-paged HTML, +* `pdf` for a PDF file, and +* `man` for the usual manpages. -An (explicit!) "--with-doc=auto" argument tries to enable all supported -formats with an "=auto". +Other values understood for this option are listed below: -A "--with-doc=no" quietly skips generation of all types of documentation, -including manpages. +* 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` +Python support +~~~~~~~~~~~~~~ + +NUT includes a client binding `PyNUT` module, as well as an optional GUI +application `NUT-Monitor` (not to be confused with `nut-monitor` service +name for `upsmon` as delivered by some OS distribution packages). Both +python 2.7 and several versions of python 3.x are supported and regularly +tested by NUT CI farm builds; other versions may work or not. + +Also some of the source configuration (including activity in `autogen.sh` +script and later in certain `Makefile`s during build) relies on presence +of `python` (and `perl`) interpreters. If they are not available, e.g. on +older operating systems, certain features are skipped -- but you may have +to `export` special environment variables to signal to `autogen.sh` that +this is an expected situation (it would suggest which, for your current +NUT version). + +NOTE: For CI builds (or similar reproducible developer activity) performed +with `ci_build.sh` -- since this is an area which impacts both `configure` +script generation by the helper `autogen.sh` script and subsequently the +choices made by the `configure` script itself, settings can be made by +exporting the `PYTHON` environment variable which should evaluate to some +way to call the correct interpreter (e.g. `python2.7`, `/usr/bin/env python` +or `/usr/bin/python3`). + +The `configure` script does support equivalent options, whose defaults +come from detection of certain program names by current `PATH` setting. +Further use of these interpreter names and other paths during NUT build +and installation is managed by Makefile variable expansion, as prepared +by the `configure` script. + +Also note that it is not required to use the same `PYTHON` implementation +for `autogen.sh` to do its job, and for the `configure` script option. + +As noted above, both python-2.x and python-3.x variants are supported. +You can consult the `m4/nut_check_python.m4` file for detection methods used. +If both interpreter generations are present, and a particular un-versioned +`PYTHON` is not specified or detected, then selected/detected `PYTHON3` is +chosen as the ultimate `PYTHON` value. + +For majority of uses in the build procedure and products, the generation +of Python does not matter and the un-versioned `PYTHON` value is substituted +into files as the script shebang, used to find the `site-packages` location, +etc. + +One exception is generation of NUT-Monitor GUI application which has been +separated for `NUT-Monitor-py2gtk2` and `NUT-Monitor-py3qt5` due to further +backend platform technical differences -- these build products specifically +use `PYTHON2` and `PYTHON3` substitutions. They may be co-installed on the +same system. A dispatcher shell script `NUT-Monitor` is used to launch the +preferred (newest) or the only existing implementation. + +Please note that by default NUT tries to make use of everything in your +build environment, so if both Python generation are detected -- the binding +module will be delivered into both, and two versions of NUT-Monitor GUI +application will be installed. If you want to avoid that behaviour on a +build system with both interpreters present, you can explicitly specify +to build e.g. `--without-python2 --with-python=/usr/bin/python-3.9`. + +The settings below may be of particular interest to non-distribution +packaging efforts with their own dedicated directory trees: + + --with-python=SHEBANG_PATH + +Specify a definitive version you want used for majority of the Python +code (except version-dependent scripts, see above). + +The `SHEBANG_PATH` should be a full program pathname, optionally with +one argument, e.g. `/usr/bin/python-3.9` or `/usr/bin/env python2`. + +Defaults (in order): +* `python`, `python3` or `python2` program if present in `PATH` by such name, +* or the newest of `PYTHON3` or `PYTHON2` values (specified or detected below). + + --with-python2=SHEBANG_PATH + --with-python3=SHEBANG_PATH + +For version-dependent scripts (see above) or to default the newest Python +version if not specified by `--with-python` option or detected otherwise, +you can provide the preferred version and implementation of Python 2 or 3 +respectively. + +Conversely, if neither of these configure options were specified, but some +`--with-python` program was specified or detected, and its report says it +has Python major version 2 or 3, then the versioned interpreter string would +point to that. + + --with-nut_monitor + +Install the NUT-Monitor GUI application (depending on Python 2 or 3 version +availability), and optional `desktop-file-install` integration). + + --with-pynut + +Install the PyNUT module files for general consumption into "site-packages" +location of the currently chosen Python interpreter(s): yes, no, auto. +or dedicated as the required dependency of NUT-Monitor application (app). + +[WARNING] +========= +The module files are installed into a particular Python version's location +such as `/usr/lib/python2.7/dist-packages` even if you specify a relaxed +or un-versioned interpreter like `python2` (which would be used in scripts +for the NUT-Monitor application and possible other consumers of the module). +If the preferred Python version in the deployed system changes later (so the +`python2` symlink for this example would point elsewhere) the module import +would become not resolvable for such consumers, until it is installed into +that other Python's "site-packages" location. +========= + +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-spellcheck (default: auto) + +Activate recipes for documentation source file spelling checks with `aspell` +tool. Default behavior depends on availability of the tool, so if present -- +it would run for `make check` by default, to facilitate quicker acceptance +of contributions. + + --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). + + --with-unmapped-data-points (default: no) + +Build SNMP and USB-HID subdrivers with entries discovered by the scripts +which generated them from data walks, but developers did not rename yet +to NUT mappings conforming to `docs/nut-names.txt` standards. Production +driver builds must not include any non-standard names. + +I want it all! +~~~~~~~~~~~~~~ --with-all (no default) @@ -151,42 +449,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). - --with-ssl (default: auto-detect) - --with-nss (default: auto-detect) - --with-openssl (default: auto-detect) +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 +521,50 @@ 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 +See also `--enable-inplace-runtime`. + +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 +574,133 @@ 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. See also `--enable-inplace-runtime`. -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 + --libexecdir=PATH -The UPS drivers will be installed to this path. By default they -install to "/bin", i.e., /usr/local/ups/bin. +Sets the installation path for "executable libraries" -- helper scripts +or programs that are not intended for direct and regular use by people, +and rather are implementation details of services. Depending on the +build configuration, this can include the `nut-driver-enumerator.sh`, +`sockdebug`, and others. The default is `/libexec`. -The "driverpath" global directive in the ups.conf file overrides this -at run time. +Package distributions may want to use this option to customize this path +to include the package name, e.g. set it to `/libexec/nut`. + + --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. --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 +708,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 +722,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 +733,29 @@ 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-libsystemd=(auto|yes|no) + --with-libsystemd-includes=CFLAGS + --with-libsystemd-libs=LDFLAGS + +If the build system provides `libsystemd` headers, NUT binaries can be +built with tighter integration to this service management framework. +In this case NUT daemons (`upsd`, `upsmon`, `upslog` and drivers) would +report their life-cycle milestones (`READY`, `RELOADING`, `STOPPING`) and +support the watchdog reports (if enabled in their respective units by +end-user -- not done by default since the numbers depends on monitoring +system performance). Default: "auto" (integration enabled if detected). --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 @@ -392,30 +763,39 @@ 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. +Changes the directory where NUT pid files are stored for processes running +as `root`. By default this is `/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. +Change the default location of the state sockets created by the drivers +to interact with the data server `upsd`. Default is `/var/state/ups`. -The NUT_STATEPATH environment variable overrides this at run time. +The `NUT_STATEPATH` environment variable overrides this at run time. -Default is /var/state/ups. + --with-powerdownflag=FILEPATH +Change the default location (full filename path) of the POWERDOWNFLAG +created by `upsmon` (as `root`) to tell the late-shutdown integration +that this machine should tell all UPSes for which it is a "primary" NUT +server to cut power to the load. Default is `/etc/killpower` on POSIX +systems and `"C:\\killpower"` (note the double backslashes) on Windows. 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 +806,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 +825,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 +837,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..ec60aa3982 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -2,8 +2,8 @@ Network UPS Tools Developer Guide _________________________________ -:Author: Russell_Kroll,_Arnaud_Quette,_Charles_Lepple_and_Peter_Selinger -:Author Initials: RK, AQ, CL & PS +:Author: Russell_Kroll,_Arnaud_Quette,_Charles_Lepple,_Peter_Selinger,_Jim_Klimov_and_NUT_project_community_contributors +:Author Initials: RK, AQ, CL, PS & JK Introduction ============ @@ -38,12 +38,12 @@ include::sock-protocol.txt[] [[augeas]] -include::../scripts/augeas/README[] +include::../scripts/augeas/README.adoc[] [[devscan]] -include::../tools/nut-scanner/README[] +include::../tools/nut-scanner/README.adoc[] [[new-clients]] @@ -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,27 +103,38 @@ 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. +[[dev-simu-disco]] + +Simulated devices discovery +--------------------------- + +Any simulation file that is saved in the sysconfig directory can be +automatically discovered and configured using nut-scanner: ++ + $ nut-scanner -n + $ nut-scanner --nut_simulation_scan ++ [[dev-recording]] 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 +146,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 @@ -150,7 +162,7 @@ include::macros.txt[] [[roadmap]] -include::../TODO[] +include::../TODO.adoc[] [[nut-names]] @@ -169,4 +181,4 @@ include::daisychain.txt[] Appendix C: NUT libraries complementary information =================================================== -include::../lib/README[] +include::../lib/README.adoc[] diff --git a/docs/developers.txt b/docs/developers.txt index 57088290dc..b989da0424 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 ----------- @@ -101,6 +101,9 @@ Portability Avoid things that will break on other systems. All the world is not an x86 Linux box. +C comments +~~~~~~~~~~ + There are still older systems out there that don't do C++ style comments. -------------------------------------- @@ -108,6 +111,9 @@ There are still older systems out there that don't do C++ style comments. // Not like this. -------------------------------------- +Variable declarations go on top +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Newer versions of gcc allow you to declare a variable inside a function after code, somewhat like the way C++ operates, like this: @@ -123,8 +129,28 @@ 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. +miserably for anyone on an older system. That means you must not use it. + +Note that `gcc` only warns about this with `-pedantic` flag, and `clang` +with a `-Weverything` (possibly `-Wextra`) flag, which can be enabled by +developers with `configure --enable-warnings=...` option values (and made +fatal with `configure --enable-Werror`), to ensure non-regression of code +quality. It was reported that `clang-16` with such options does complain +about non-portability to older C language revisions even if explicitly +building for a newer revision. + +Please note that for the purposes of legacy-compatible variable declarations +(on top of their scopes), a `NUT_UNUSED_VARIABLE(varname)` counts as code and +should be used just below the declarations. Initial assignments to variables +(also as return values of methods) may generally happen as part of their +declarations. + +You can use scoping (e.g. `do { ... } while (0);`) where it makes sense +to constrain visibility of temporary variables, such as in `switch/case` +blocks. + +Variable declaration in loop block syntax +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another feature that does not work on some compilers (e.g. conforming to "ANSI C"/C89/C90 standard) is initial variable declaration inside a @@ -140,8 +166,11 @@ function do_stuff(void) } -------------------------------------------------------------------------------- +Other hints +~~~~~~~~~~~ + TIP: At this point NUT is expected to work correctly when built with a -C99 (or rather GNU99 on many systems) or newer standard. +"strict" C99 (or rather GNU99 on many systems) or newer standard. The NUT codebase may build in a mode without warnings made fatal on C89 (GNU89), but the emitted warnings indicate that those binaries may crash. @@ -149,8 +178,18 @@ By the end of 2021, NUT codebase has been revised to pass GNU and strict-C mode builds with C89 standard with the GCC toolkit (and on systems that do have the newer features in libraries, just hide them in standard headers); however CLANG toolkit is more restrictive about the C99+ syntax used. -If somebody in the community requires to build and run NUT on systems -that old, pull requests to fix the offending coding issues are welcome. +That said, some systems refuse to expose methods or types available in +their system headers and binary libraries if strict-C mode is used alone, +without extra system-specific defines to enable more than the baseline. + +It was also seen that cross-builds (e.g. NUT for Windows using mingw on +Linux) may be unable to define `WIN32` and/or find symbols for linking +when using a strict-C language standard. + +The C++ support expects C++11 or newer (not really configured or tested +for older C++98 or C++03), modulo features that were deprecated in later +language revisions (C++14 onwards) as highlighted by warnings from newer +compilers. Note also that the NUT codebase currently relies on certain features, such as the printf format modifiers for `(s)size_t`, use of `long long`, @@ -161,8 +200,16 @@ revisions). Many of the "offences" against the older standard actually come from system and third-party header files. That said, the NUT CI farm does run non-regression builds with GNU C89 -and strict C89 standard revisions and minimal passing warnings level, +and "strict" C89 standard revisions and minimal passing warnings level, to ensure that codebase is and remains at least basically compliant. +We try to cover a few distributions from early 2000's for this, either +in regular CI builds or one-off local builds for community members with +a zoo of old systems. + +If somebody in the community actually requires to build and run NUT on +systems that old, where newer compilers are not available, pull requests +to fix the offending coding issues in some way that does not break other +use-cases are welcome. Continuous Integration and Automated Builds ------------------------------------------- @@ -171,6 +218,9 @@ To ease and automate the build scenarios which were deemed important for quality assurance and non-regression checks of NUT, several solutions were introduced over time. +Build automation tools and scripts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ci_build.sh ^^^^^^^^^^^ @@ -267,7 +317,8 @@ Jenkins CI ^^^^^^^^^^ Since mid-2021, the NUT CI farm is implemented by several virtual servers -courteously provided by http://fosshost.org +courteously provided by link:http://fosshost.org[Fosshost] and later by +link:https://www.digitalocean.com/?refcode=d2fbf2b9e082&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge[DigitalOcean]. These run various operating systems as build agents, and a Jenkins instance to orchestrate the builds of NUT branches and pull requests on those agents. @@ -354,6 +405,27 @@ Some further details about the NUT CI farm workers are available in link:config-prereqs.txt[config-prereqs.txt] and link:ci-farm-lxc-setup.txt[ci-farm-lxc-setup.txt] documents. +AppVeyor CI +^^^^^^^^^^^ + +Primarily used for building NUT for Windows on Windows instances provided +in the cloud -- and so ensure non-regression as well as downloadable archives +with binary installation prototype area, intended for enthusiastic testing +(proper packaging to follow). NUT for Windows build-ability was re-introduced +soon after NUT 2.8.0 release. + +This relies on a few prerequisite packages and a common NUT configuration, +as coded in the `appveyor.yml` file in the NUT codebase. + +CircleCI +^^^^^^^^ + +Primarily used for building NUT for MacOS on instances provided in the cloud, +and so ensure non-regression across several Xcode releases. + +This relies on a few prerequisite packages and a common NUT configuration, +as coded in the `.circleci/config.yml` file in the NUT codebase. + Travis CI ^^^^^^^^^ @@ -362,12 +434,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 @@ -427,6 +502,399 @@ Finally, note that such pre-set warnings can be mixed with options passed through `CFLAGS` or `CXXFLAGS` values to your local `configure` run, but it is up to your compiler how it interprets the resulting mix. +Integrated Development Environments (IDEs) and debugging NUT +------------------------------------------------------------ + +Much of NUT has been coded using classic editors of developers' preference, +like `vi`, `nano`, Midnight Commander `mcedit`, `gedit`/`pluma`, NotePad++ +and tools like `meld` or WinMerge for file comparison and merge. + +Modern IDEs however do offer benefits, specifically for live debugging +sessions in a more convenient fashion than with command-line `gdb` directly. +They also simplify writing AsciiDoc files with real-time rendering support. + +NOTE: Due to use of `libtool` wrappers in "autotools" driven projects, +it may be tricky to attach the debugger (mixing the correct `LD_LIBRARY_PATH` +or equivalent with a binary under a `.libs` subdirectory; on some platforms +you may be better off copying shared objects to the directory with the binary +being tested). + +IDEs that were tested to work with NUT development and real-time debugger +tracing include: + +* Sun NetBeans 8.2 on Solaris, Linux (including local and remote build + and debug ability); +* Apache NetBeans 17 on Windows with MSYS2 support (as MinGW toolkit); +* Visual Studio Code (VSCode) on Windows with MSYS2 support. + +Some supporting maintenance and development is doable with IntelliJ IDEA, +making some things easier to do than with a simple Notepad, but it does +not handle C/C++ development as such. + +IDE notes on Windows +~~~~~~~~~~~~~~~~~~~~ + +General settings for builds on Windows +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When working in a native Windows environment with +link:https://www.msys2.org/[MSYS2] (providing MinGW x64 among other things), +you may need to ensure certain environment variables are set before you start +the IDE (shortcuts and wrappers that start your console apply them via shell). + +WARNING: If you set such environment variables system-wide for your user +profile (or wrap the IDE start-up by a script to set them), it may compromise +your ability to use *other* MSYS2 profiles and/or other builds of these +toolkits (packaged by e.g. Git for Windows or PERL for Windows projects) +generally, or in the same IDE session, respectively. +You may want to do this in a dedicated user account! + +Examples below assume you installed MSYS2 into `C:\msys64` (by default) and +are using the "MinGW X64" profile for GCC builds (nuances may differ for +32-bit, CLANG, UCRT and other profile variants). + +Also keep in mind that not all dependencies and tools involved in a +fully-fledged NUT build are easily available or usable on Windows (e.g. +the spell checker). See the link:config-prereqs.txt[] for better detailed +package lists for different operating systems including Windows, and feel +welcome to post pull requests with suggestions about new tool-chains that +might fare better than those already tried and documented. + +* Make sure its tools are in the `PATH`: ++ +Control Panel => "Edit the system environment variables" => + "Environment variables..." (button) => + "Edit..." or create "New..." `Path` setting ("User variable" level suffices) => + +** Make sure `C:\msys64\mingw64\bin` and `C:\msys64\usr\bin` are both there. +** Depending on further installed toolkits, you may want to add + `C:\Program Files\Git\cmd` or `C:\Program Files\Microsoft VS Code\bin` + (preferably use deployment-dependent spellings without white-space like + `Progra~1` to err on the safe side of variable expansions later). + +* Make sure that MSYS2 (and tools which integrate with it) know its home: ++ +Open Environment variables window as above, and + "Edit..." or create "New..." `MSYS_HOME` setting => + Set to `C:\msys64\mingw64\bin` +* Restart the IDE (if already running) for it to acknowledge the system + configuration change. + +Otherwise, NetBeans for example claims there is no shell for it to run `make` +or open Terminal pane windows, and fails to start the built programs due to +lack of DLL files they were linked against (such as `libssl` usually needed +for any networked part of the codebase). + +You might still have to fiddle with DLL files built in other directories of +the NUT project, when preparing to debug certain programs, e.g. for `dummy-ups` +testing you may need to: +------ +:; cp ./clients/.libs/libupsclient-6.dll ./drivers/.libs/ +------ + +To ensure builds with debug symbols, you may add `CFLAGS` and `CXXFLAGS` set +to `-g3 -gdwarf-2` or similar to `configure` options, or if that confuses +the cross-build (it tends to assume those values are part of GCC path), +you may have to hack them into your local copy of `configure.ac`, after the +`AM_INIT_AUTOMAKE([subdir-objects])` line: +------ +CFLAGS="$CFLAGS -g3 -gdwarf-2" +CXXFLAGS="$CXXFLAGS -g3 -gdwarf-2" +------ +...and re-run the `./autogen.sh` script. + +GDB on Windows +^^^^^^^^^^^^^^ + +Examples below assume that whichever IDE you are using, the primary goal is +to debug some issues with NUT on that platform. + +This may require you to craft a configuration file for the GNU Debugger, +e.g. `C:\Users\abuild\.gdbinit` for the examples below. One is not required +however, and may be missing. + +Another thing to keep in mind is that with `libtool` involved, the actual +binary for testing would be in a `.libs` subdirectory and you may have some +fun with ensuring that DLLs are found to start them -- see the notes above. + +NetBeans on Windows +^^^^^^^^^^^^^^^^^^^ + +When you install newer link:https://netbeans.apache.org/[Apache NetBeans] +releases (14, 17 as of this writing), you may need to enable the use of +"NetBeans 8.2 Plugin Portal" (check under Tools/Plugins/Settings) and +install the "C/C++" plugin only available there at the moment. +In turn, that older build of a plugin package may require that your system +provides the `unpack200(.exe)` tool which was shipped with JDK11 or older +(you may have to install that just to get the tool, or copy its binary from +another system). + +Under Tools/Options menu open the C/C++ tab and further its Build Tools sub-tab. + +NOTE: NetBeans allows you to easily define different Tool Collections, +including those associated with a different build host (accessible over SSH +and source/build paths optionally shared over NFS or similar technology, or +copied over). This allows you to run the IDE on your desktop while debugging +a build running on a server or embedded system. + +Make sure you have a MinGW Tool Collection for the "localhost" build host with +such settings as: + +|=== +| Option name | Sample value + +| Family | GNU MinGW +| Encoding | UTF-8 +| Base Directory | `C:\msys64\mingw64\bin` +| C Compiler | `C:\msys64\mingw64\bin\gcc.exe` +| C++ Compiler | `C:\msys64\mingw64\bin\g++.exe` +| Assembler | `C:\msys64\mingw64\bin\as.exe` +| Make Command | `C:\msys64\usr\bin\make.exe` +| Debugger Command | `C:\msys64\mingw64\bin\gdb.exe` +|=== + +In the Code Assistance sub-tab check that there are toolkit-specific and +general include paths, e.g. both C and C++ Compiler settings might involve: + +|=== +| `C:\msys64\mingw64\lib\gcc\x86_64-w64-mingw32\12.2.0\include` +| `C:\msys64\mingw64\include` +| `C:\msys64\mingw64\lib\gcc\x86_64-w64-mingw32\12.2.0\include-fixed` +| `C:\msys64\mingw64\x86_64-w64-mingw32\include` +|=== + +On top of that, C++ Compiler settings may include: + +|=== +| `C:\msys64\mingw64\include\12.2.0` +| `C:\msys64\mingw64\include\12.2.0\x86_64-w64-mingw32` +| `C:\msys64\mingw64\include\12.2.0\backward` +|=== + +In the "Other" sub-tab, set default standards to C99 and C++11 to match common +NUT codebase expectations. + +Finally, open/create a "nut" project pointing to your git checkout workspace. + +Next part of configuration regards build/debug configurations, which you can +find on the toolbar or as File / Project Properties. + +The main configuration for debugging a particular binary (and NUT has tons +of those, good luck in case you want to debug several simultaneously) is +in the *Run* and *Debug* categories. You may want to define different +Configuration profiles to track the individual Run/Debug settings for +different tested binaries, while the Build/Make settings would remain the same. +Alternatively, you may set the *Make* category's "Build Result" as the path to +the binary you would test, and use `${OUTPUT_PATH}` variable as its name in +the "Run Command" (still likely need custom arguments) and "Symbol File" below. + +When you investigate interactions of two or more programs, but only want to +debug (step through) just one of them, you are advised to run each of the +others from a dedicated terminal session, and just bump their debug verbosity. + +* In the *Build* category, set the Build Host (localhost) and Tool Collection + (MinGW). In expert part of the settings, un-check "platform-independent" + and revise that the `TOOLS_PATH=C:\msys64\mingw64\bin` while the + `UTILITIES_PATH=C:\msys64\usr\bin`. + +* In the *Pre-Build* category likely keep the Working Directory as `.` and + the `Pre-Build First` generally unchecked (so only enable it to reconfigure + the project, which takes time and is not needed for every rebuild iteration), + but you may still pre-set the Command line to something like the following + (on one line): ++ +------ +bash -c "rm -f configure Makefile; ./autogen.sh && + ./configure CC='${IDE_CC}' CXX='${IDE_CXX}' + --with-all=auto --with-docs=skip" +------ ++ +In some cases, NOT specifying the `CC`, `CXX` and the flags actually succeeds +while passing their options fails the configuration ("Compiler can not create +executables" etc.) probably due to path resolution issues between the native +and MinGW environments. ++ +NOTE: In practice, you may have an easier time using NUT `./ci_build.sh` helper or +running a more specific `./autogen.sh && ./configure ...` spell similar to +the above example or customized otherwise, in the MinGW x64 console window +to actually configure a NUT source code setup, than to maintain one via the IDE. +Running (re-)builds with the IDE (as you just edit non-recipe sources and +iterate with a debugger) using externally configured Makefiles works fine. + +* In the *Make* category you may want to customize for parallelized builds on + multi-CPU systems with something like: +** Build Command: `${MAKE} -j 6 -f Makefile` +** Clean Command: `${MAKE} -f Makefile clean` + +* In the *Run* category you should set the "Run Command" to point to your + binary (note the `.libs` sub-directory, and see comments above regarding + possibly needed copies of shared objects) and its arguments (all on one + line), e.g.: ++ +------ +C:\Users\abuild\Desktop\nut\drivers\.libs\usbhid-ups.exe -s ups -x port=auto + -d1 -DDDDDD +------ ++ +Other useful settings may be to keep "Build First" checked, and if the +"Internal Terminal" does not work for you as the debugged program's console -- +set the "Console Type" to "External Terminal" of type "Command Window". +Unfortunately, NetBeans on Windows may have issues running terminal tabs +unless CygWin is installed. + +* In the *Debug* category you should set the "Symbol File" to point to your + tested binary (e.g. `C:\Users\abuild\Desktop\nut\drivers\.libs\usbhid-ups.exe` + to match the "Run Command" example above) and specify "Follow Fork Mode" as + "child" and "Detach On Fork" as "off". "Reverse Debugging" may be useful too + in some situations. Finally, select your "Gdb Init File" if you have one, + e.g. `C:\Users\abuild\.gdbinit`. + +Microsoft VS Code +^^^^^^^^^^^^^^^^^ + +With this IDE you can benefit from numerous Extensions from its Marketplace, +the ones found useful for NUT development and debugging include: + +* AsciiDoc (by asciidoctor) +* EditorConfig for VS Code (by EditorConfig) +* C/C++ (by Microsoft) +* C/C++ Extension pack (by Microsoft) +* Makefile tool (by Microsoft) +* MSYS2/Cygwin/MinGW/Clang support (by okhlybov) +* Native Debug (GDB, LLDB ... Debugger support; by WebFreak) + +Configurations are tracked locally in JSON files where you would need to add +some entries. Examples below highlight the needed keys and values; your files +may have others: + +* `.vscode/launch.json` (can create one via Run/Add Configuration... menu + defines ways to launch the debug session for a program: ++ +------ +{ + "configurations": [ + { + "name": "CPPDBG GDB usbhid-ups", + "type": "cppdbg", + "request": "launch", + "program": "C:\\Users\\abuild\\Desktop\\nut\\drivers\\.libs\\usbhid-ups.exe", + "additionalSOLibSearchPath": "C:\\Users\\abuild\\Desktop\\nut\\.inst\\mingw64\\bin", + "stopAtConnect": true, + "args": ["-s", "ups", "-DDDDDD", "-d1", "-x", "port=auto"], + "stopAtEntry": false, + "cwd": "C:\\Users\\abuild\\Desktop\\nut", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "C:\\msys64\\mingw64\\bin\\gdb.exe", + "targetArchitecture": "x64", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + "preLaunchTask": "make usbhid-ups" + }, + { + // Alternately with LLDB (clang), the rest looks like above: + "name": "CPPDBG LLDB usbhid-ups", + "MIMode": "lldb", + "miDebuggerPath": "C:\\msys64\\usr\\bin\\lldb.exe", + }, + ... + ] +} +------ + +* `.vscode/tasks.json` defines other tasks, such as the `preLaunchTask` + mentioned above (assuming you have configured the build externally in + the MinGW x64 terminal session): ++ +------ +{ + "tasks": [ + { + "type": "shell", + "label": "make usbhid-ups", + "command": "C:\\msys64\\usr\\bin\\make usbhid-ups", + "options": { + "cwd": "${workspaceFolder}/drivers" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + ... + ] +} +------ + +* `.vscode/c_cpp_properties.json` defines general compiler settings, e.g.: ++ +------ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "C:\\msys64\\mingw64\\include\\libusb-1.0", + "C:\\msys64\\mingw64\\include", + "C:\\msys64\\usr\\include" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "compilerPath": "C:\\msys64\\mingw64\\bin\\gcc.exe", + "cStandard": "c99", + "cppStandard": "c++11", + "intelliSenseMode": "windows-gcc-x64", + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} +------ + + +IntelliJ IDEA +^^^^^^^^^^^^^ + +It is worth mentioning IntelliJ IDEA as another free (as of Community Edition) +and popular IDE, however it is of limited use for NUT development. + +Its ecosystem does feature a good AsciiDoc plugin, Python and of course the +Java/Groovy support, so IDEA is helpful for maintenance of NUT documentation, +helper scripts and CI recipes. + +It lacks however C/C++ language support (allegedly a different product in the +IntelliJ portfolio is dedicated to that), so for the core NUT project sources +it is just a fancy text editor (with `.editorconfig` support) without syntax +highlighting or codebase cross-reference aids, build/run/debug support, etc. + +Still, it is possible to run builds and tests in embedded or external terminal +session -- so it is not worse than editing with legacy tools, and navigation +or code-base-wide search is arguably easier. + +//////// +TODO: +Make note of settings (and Run as Administrator) to use symlinks in MinGW x64. +Check if required for sane (iterative re-)builds? ;) +//////// + Coding style ------------ @@ -450,15 +918,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 +951,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 +986,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 ------------------------------------------------------------------------------- @@ -531,6 +1007,12 @@ if (condition) { } ------------------------------------------------------------------------------- +NOTE: Earlier revisions of coding style might suggest avoiding braces if just +one line is added as condition/loop/etc. handling code. Current approach is to +welcome them even for single lines: on one hand, this confirms the intention +that only this line is the conditional code; on another, this minimizes the +context differences for later code comparisons, relocation, refactoring, etc. + Un-used variables and function arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -547,6 +1029,10 @@ body of the function (or an `#ifdef` branch of its code) using the `NUT_UNUSED_VARIABLE(varname)` as a routine call inside a function body, referring to the macro defined in `common.h`. +Please note that for the purposes of legacy-compatible variable declarations +(on top of their scopes), `NUT_UNUSED_VARIABLE(varname)` counts as code and +should happen below the declarations. + To display in a rough example: ------------------------------------------------------------------------------- @@ -560,6 +1046,13 @@ To display in a rough example: Miscellaneous coding style tools -------------------------------- +NUT codebase includes an `.editorconfig` file which should be supported +by most of the IDEs and text editors nowadays. Many support this format +specification (at least partially) out of the box, possibly with some +configuration toggle in the GUI. Others may need a plugin, see more at +https://editorconfig.org/#pre-installed page. There are also command-line +tools to verify and/or enforce compliance of source files to configuration. + You can go a long way towards converting your source code to the NUT coding style by piping it through the following command: @@ -575,7 +1068,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 +1091,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 +1183,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 +1232,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 ---------------------- @@ -873,6 +1376,43 @@ You may notice that some older commits have `[[SVN:####]]` tags and Fossil-ID footers. These were lifted from the old SVN commit messages using reposurgeon, and should *not* be used as a guide for future commits. +Commit sign-off +--------------- + +Please also note that since 2023 we explicitly ask for contributions to be +"Signed Off" according to "Developer Certificate of Origin" as represented +in the `LICENSE-DCO` file in the root of NUT source tree (verbatim copy of +Version 1.1 of DCO published at https://developercertificate.org/ web site). +This is exactly the same one created and used by the Linux kernel developers. + +This is a developer's certification that he or she has the right to submit +the patch for inclusion into the project. Simply submitting a contribution +implies this agreement, however, please include a "Signed-off-by" tag in +every patch (this tag is a conventional way to confirm that you agree to +the DCO). In other words, this tag certifies that committer has the rights +to submit this work under the same license as the project and agrees to the +terms of a Developer Certificate of Origin. + +Note that while git commit hook tricks are available to automatically sign +off all commits, these signatures are intended to be a conscious (legally +meaningful) act -- hence they are not automated in git core with an easy +configuration option. + +For more details see: + +* https://github.com/networkupstools/nut/issues/1994 +* https://stackoverflow.com/questions/1962094/what-is-the-sign-off-feature-in-git-for +* https://stackoverflow.com/questions/15015894/git-add-signed-off-by-line-using-format-signoff-not-working + +You are also encouraged to set up a PGP key, make its public part known, and +use it to sign your git commits (in addition to the `Signed-Off-By` tag) by +also passing a `-S` option or calling `git config commit.gpgsign true` once. +Numerous public articles can walk you through this ordeal, including: + +* https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits +* https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key +* https://www.kernel.org/doc/html/v4.19/process/maintainer-pgp-guide.html + Repository etiquette and quality assurance ------------------------------------------ @@ -923,11 +1463,11 @@ Here is an example workflow: # Hack away git add changed-file.c - git commit + git commit -s # Fix a typo in a file or commit message: - git commit -a --amend + git commit -s -a --amend # Someone committed something to the central repository. Fetch it. @@ -939,19 +1479,25 @@ Here is an example workflow: git push username my-new-feature ------------------------------------------------------------------------------ -If you are new to Git, but are familiar with SVN, the -link:http://git-scm.com/course/svn.html[following link] may be of use. +If you are new to Git, but are familiar with SVN, some of the following links +may be of use: + +* link:https://web.archive.org/web/20191224210950/https://git-scm.com/course/svn.html[Git - SVN Crash Course (archived)] +* link:https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git[Git and Other Systems - Migrating to Git] +* link:https://www.git-tower.com/learn/git/ebook/en/command-line/appendix/from-subversion-to-git[Switching from Subversion to Git] +* link:https://www.atlassian.com/git/tutorials/migrating-overview[Migrate from SVN to Git] [[building]] 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 +1505,60 @@ 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. -Before pushing your commits upstream, please run +make distcheck-light+. +See `ci_build.sh` for automating many practical scenarios, for easier +iterations. + +It is optional, but highly recommended, to have Python 2.x or 3.x, and Perl, +to generate some files included into the `configure` script whose presence +is checked by autotools when it is generated. Neutered files can be just +"touched" to pass the `autogen.sh` if these interpreters are not available, +and effectively skip those parts of the build later on -- `autogen.sh` will +then advise which special environment variables to `export` in your situation +and re-run it. +====== + +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/docinfo.xml.in b/docs/docinfo.xml.in index ed3faaf311..20284705e6 100644 --- a/docs/docinfo.xml.in +++ b/docs/docinfo.xml.in @@ -1,22 +1,160 @@ - + - @PACKAGE_VERSION@ + @PACKAGE_VERSION@ @NUT_SOURCE_GITREV@ @now@ - Current release of Network UPS Tools (NUT). + Current release snapshot of Network UPS Tools (NUT). + + + + + + + 2.8.1 + 2023-10-31 + JK + + Some changes to API, docs and recipes, in particular to simplify local + builds and tests (e.g. to help end-users check if current NUT codebase + trunk has already fixed an issue they see with a packaged installation). + Revived NUT for Windows effort, further improved other OS integrations. + NUT became reference for "UPS management protocol", Informational RFC 9271. + Documentation files refactored to ease maintenance. + More drivers and new driver categories introduced. + + + + + 2.8.0 + 2022-04-26 + JK + + Change of maintainer. + Many changes to API, docs (both style and content), and recipes, + with a stress on non-regression test-ability, run-time debug-ability, + general codebase maintainability, as well as OS integrations (notably + nut-driver-enumerator for systemd and SMF service instance maintenance). + Added a lot in area of CI support and documented pre-requisite package + lists for numerous platforms, and CI agent set-up. Added libusb-1.x + support and many new driver categories (and drivers), and daisychain + device connection support. Instant commands enhanced with TRACKING to + enable protocol-based waiting for completion of a particular INSTCMD + or SET operation. + + + + + 2.7.4 + 2016-03-09 + AQ + + NUT variables namespace updated, in particular for outlet groups, + alarms and thresholds, ATS devices, and battery.charger.status to + supersede CHRG and DISCHRG flags published in ups.status readings. + NUT network protocol extended with NUMBER type; some API changes. + + + + + 2.7.3 + 2015-04-22 + AQ + + Documentation revised, including some API changes. + Added NUT DDL links. + NUT variables namespace updated. + + + + + 2.7.2 + 2014-04-17 + AQ + + The nut-website project was offloaded into a separate repository. + FreeDesktop HAL support was removed (obsoleted in GNOME consumer). + Introduced nutdrv_atcl_usb driver. + + + + + 2.7.1 + 2013-11-19 + CL + + NUT source codebase migrated from SVN to Git (and from Debian + infrastructure to GitHub source code hosting). jNut binding + split into a separate project. Introduced libnutclient (C++ + binding), al175, apcupsd-ups and nutdrv_qx drivers, Mozilla + NSS support for simpler licensing than OpenSSL, and a newer + apcsmart implementation. + Documentation support enhanced with a spell checker, contents + massively updated to reflect project changes. + + + + + 2.6.5 + 2012-08-08 + AQ + + New macosx-ups driver, new implementation of mge-shut driver. + NUT variables namespace updated. Docs cleaned up and revised. + + + + + 2.6.4 + 2012-05-31 + AQ + + New NUT network protocol commands (LIST CLIENTS, LIST RANGE and + NETVER), and socket protocol commands (ADDRANGE, DELRANGE). + NUT variables namespace updated. Introduced nut-recorder tool. + + + + + 2.6.3 + 2012-01-04 + AQ + + No substantial changes to documentation. + + + + + 2.6.2 + 2011-09-15 + AQ + + Introduced nut-scanner tool and nut-ipmipsu driver, + systemd support, and a new apcsmart implementation. + + + + + 2.6.1 + 2011-06-01 + AQ + + Introduced default.* and override.* optional settings in ups.conf, + an ups.efficiency report, and outlet.0 special handling. - 2.6.0 2011-01-14 - + AQ - First release of AsciiDoc documentation for Network UPS Tools (NUT). + First release of AsciiDoc documentation for Network UPS Tools (NUT). diff --git a/docs/docinfo.xml.sh b/docs/docinfo.xml.sh new file mode 100755 index 0000000000..bb18d86bd9 --- /dev/null +++ b/docs/docinfo.xml.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +# Helper script to generate DOCINFO document revision list for NUT PDF docs. +# Primarily used by maintainers as part of a NUT release preparation - +# see docs/maintainer-guide.txt for more details. +# Note that relying on git tags alone is a chicken-and-egg problem here +# (the updated file should be part of a new release), so it rather helps +# catch up with missed entries. +# Copyright (C) 2023 by Jim Klimov +# Licensed under GPLv2+ terms + +SCRIPTDIR="`dirname "$0"`" \ +&& SCRIPTDIR="`cd "${SCRIPTDIR}" && pwd`" \ +&& [ -n "${SCRIPTDIR}" ] && [ -d "${SCRIPTDIR}" ] \ +|| exit + +[ -n "${DOCINFO_XML-}" ] || DOCINFO_XML="${SCRIPTDIR}/docinfo.xml.in" + +generate_tags() { + # NUT v2.6.0 is the oldest release with asciidoc rendered into PDF + for RELTAG in `git tag -l 'v*' --contains v2.6.0 | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sed -e 's,-.*$,,' | sort -nr | uniq` ; do + NUT_RELEASE="`echo "$RELTAG" | sed -e 's,^v,,'`" + grep "${NUT_RELEASE}" "${DOCINFO_XML}" >/dev/null 2>&1 && continue + + NUT_RELEASER="`git log -1 --pretty=format:'%cn' "${RELTAG}" | tr -d 'a-z '`" + # Beware to not use "|" in formatting below! Used for sed magic later. + git log -1 --pretty=tformat:' %n @NUT_RELEASE@%n %cs%n @NUT_RELEASER@%n %n %n' \ + "${RELTAG}" \ + | sed \ + -e 's,@NUT_RELEASE@,'"${NUT_RELEASE}"',' \ + -e 's,@NUT_RELEASER@,'"${NUT_RELEASER}"',' + done +} + +NEWTAGS="`generate_tags`" +[ -n "$NEWTAGS" ] || { echo "SKIP: No new releases found (without a record in DOCINFO)" >&2; exit 0; } + +#echo "TODO: Add the following DOCINFO tags:" >&2 +#echo "NEWTAGS: $NEWTAGS" >&2 + +# Have to hide and un-hide EOLs (via "|") and escape +# the leading space to enforce it in "sed a" command +# (it eats whitespace otherwise). +echo "INJECTING to DOCINFO_XML:" >&2 +NEWTAGS_SED="`echo "$NEWTAGS" | tr '\n' '|' | sed -e 's,\([/<>]\),\\1,g' -e 's,^ ,\\\\ ,'`" || exit +echo "NEWTAGS_SED: $NEWTAGS_SED" >&2 + +sed -e '//a'"$NEWTAGS_SED" \ + < "${DOCINFO_XML}" \ + | tr '|' '\n' \ + > "${DOCINFO_XML}.tmp" + +diff -bu "${DOCINFO_XML}" "${DOCINFO_XML}.tmp" + +echo "Was the change acceptable? Press Y to modify the original '${DOCINFO_XML}' file [Y/N]" >&2 +read LINE +case "$LINE" in + y|Y|yes|YES) + mv -f "${DOCINFO_XML}.tmp" "${DOCINFO_XML}" + echo "You may want to: git add -p `basename "${DOCINFO_XML}"`" >&2 + echo "...and: make `basename "${DOCINFO_XML}" .in`" >&2 + ;; + *) echo "NOT APPLYING the change! See '${DOCINFO_XML}.tmp' for investigation!" >&2 +esac diff --git a/docs/documentation.txt b/docs/documentation.txt index b8d946aa00..ddc2d1f425 100644 --- a/docs/documentation.txt +++ b/docs/documentation.txt @@ -12,7 +12,7 @@ ifdef::website[] - NUT User Manual (link:docs/user-manual.chunked/index.html[online]) (link:docs/user-manual.pdf[PDF]) - Cables information (link:cables.html[online]) (link:docs/cables.pdf[PDF]) - link:docs/man/index.html#User_man[User manual pages] -- link:ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported +- link:ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported; see also link:stable-hcl.html[the HCL] - link:docs/solaris-usb.html[Notes on NUT monitoring of USB devices in Solaris and related operating systems] endif::website[] ifndef::website[] @@ -20,9 +20,11 @@ ifndef::website[] - linkdoc:user-manual[NUT user manual] - <> - link:../man/index.html#User_man[User manual pages] -- link:http://www.networkupstools.org/ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported +- link:https://www.networkupstools.org/ddl/index.html#_supported_devices[Devices Dumps Library (DDL)]: Provides information on how devices are supported; see also link:https://www.networkupstools.org/stable-hcl.html[the HCL] - link:../solaris-usb.html[Notes on NUT monitoring of USB devices in Solaris and related operating systems] endif::website[] +- link:https://github.com/networkupstools/ConfigExamples/releases/latest[NUT Configuration Examples] book maintained by Roger Price +- link:https://github.com/networkupstools/nut/wiki[NUT GitHub Wiki] Developer Documentation ----------------------- @@ -30,6 +32,8 @@ Developer Documentation ifdef::website[] - NUT Developer Guide (link:docs/developer-guide.chunked/index.html[online]) (link:docs/developer-guide.pdf[PDF]) - NUT Packager Guide (link:docs/packager-guide.chunked/index.html[online]) (link:docs/packager-guide.pdf[PDF]) +- NUT Release Notes (link:docs/release-notes.chunked/index.html[online]) (link:docs/release-notes.pdf[PDF]) +- NUT Change Log (link:docs/ChangeLog.chunked/index.html[online]) (link:docs/ChangeLog.pdf[PDF]) - link:ups-protocols.html[UPS protocols library] - link:docs/man/index.html#Developer_man[Developer manual pages] - link:nut-qa.html[NUT Quality Assurance] @@ -38,10 +42,12 @@ endif::website[] ifndef::website[] - linkdoc:developer-guide[NUT Developer Guide] - linkdoc:packager-guide[NUT Packager Guide] +- linkdoc:release-notes[NUT Release Notes] +- linkdoc:ChangeLog[NUT Change Log] - link:ups-protocols.html[UPS protocols library] - link:../man/index.html#Developer_man[Developer manual pages] - link:nut-qa.html[NUT Quality Assurance] -- link:http://www.networkupstools.org/ddl/index.html[Devices Dumps Library (DDL)]: Provides simulation data to the linkman:dummy-ups[8] driver +- link:https://www.networkupstools.org/ddl/index.html[Devices Dumps Library (DDL)]: Provides simulation data to the linkman:dummy-ups[8] driver endif::website[] Data dumps for the DDL @@ -63,6 +69,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 ------------- @@ -93,6 +110,7 @@ These are writeups by users of the software. - link:http://buffalo.nas-central.org/wiki/Install_a_UPS_%28nut%29[Install a UPS (nut) on a Buffalo NAS] '(various authors)' - link:http://blog.pointbre.com/2903/nutnetwork-ups-tool-korean-guidebook.html[NUT Korean GuideBook] '(PointBre)' - link:https://www.jamesridgway.co.uk/monitoring-eaton-5sc-ups-scripts-and-integration-network-tools-home-assistant/amp/[USB UPS, notifications, and Home Assistant] '(James Ridgway)' +- link:https://www.hirschler.solutions/posts/2022/06/powerwalker-ups-on-fedora-server-36[PowerWalker UPS on Fedora Server 36] '(Michael Hirschler)' Video articles are also available: diff --git a/docs/download.txt b/docs/download.txt index a4852bf8b2..d4d2ea6110 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -12,7 +12,7 @@ You should always use PGP/GPG to verify the signatures before using any source c You can use the ifdef::website[] -link:docs/user-manual.chunked/ar01s09.html#verifySourceSig[following procedure] +link:docs/user-manual.chunked/NUT_Security.html#verifySourceSig[following procedure] endif::website[] ifndef::website[] <>. @@ -23,14 +23,14 @@ to do so. Stable tree: {tree_version} ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz[nut-{revision}.tar.gz] -- link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sig[PGP/GPG signature] -- link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sha256[SHA-256 sum] -- link:http://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.md5[MD5 sum] -- link:http://www.networkupstools.org/source/{tree_version}/new-{revision}.txt[Release notes] -- link:http://www.networkupstools.org/source/{tree_version}/ChangeLog[ChangeLog] +- link:https://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz[nut-{revision}.tar.gz] +- link:https://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sig[PGP/GPG signature] +- link:https://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.sha256[SHA-256 sum] +- link:https://www.networkupstools.org/source/{tree_version}/nut-{revision}.tar.gz.md5[MD5 sum] +- link:https://www.networkupstools.org/source/{tree_version}/new-{revision}.txt[Release notes] +- link:https://www.networkupstools.org/source/{tree_version}/ChangeLog[ChangeLog] -You can also browse the link:http://www.networkupstools.org/source/{tree_version}/[stable source directory]. +You can also browse the link:https://www.networkupstools.org/source/{tree_version}/[stable source directory]. Development tree: ~~~~~~~~~~~~~~~~~ @@ -51,6 +51,13 @@ following script in the directory you just checked out: $ ./autogen.sh +NOTE: it is optionally recommended to have Python 2.x or 3.x, and Perl, to +generate some files included into the `configure` script, presence is checked +by autotools when it is generated. Neutered files can be just "touched" to +pass the `autogen.sh` if these interpreters are not available, and effectively +skip those parts of the build later on -- `autogen.sh` will then advise which +special environment variables to `export` in your situation and re-run it. + Then refer to the ifdef::website[] link:docs/user-manual.chunked/index.html[NUT user manual] @@ -68,12 +75,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,18 +86,28 @@ 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 ~~~~~~~~~~~~~~ -link:http://www.networkupstools.org/source/[Browse source directory] +link:https://www.networkupstools.org/source/[Browse source directory] Binary packages @@ -100,45 +115,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:https://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:https://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:https://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] Virtualization packages ----------------------- @@ -146,7 +186,25 @@ Virtualization packages VMware ~~~~~~ -- NUT client 2.7.4 for ESXi 5.0, 5.1, 5.5 and 6.0 (offsite, RenƩ Garcia) - - * link:http://rene.margar.fr/2012/05/client-nut-pour-esxi-5-0/[blog entry (French)] - * link:http://rene.margar.fr/downloads/NutClient-ESXi500-1.4.0.tar.gz[VIB package (v1.4.0)] +- NUT client for VMware ESXi (several versions of both; offsite, by RenƩ Garcia). + Since the hypervisor manager environment lacks access to hardware ports, this + package only includes the `upsmon` client integration, and a NUT server must + run in a VM with passed-through ports. ++ +See link:https://github.com/networkupstools/nut/wiki/NUT-and-VMware-(ESXi)[NUT + and VMware (ESXi) page on NUT Wiki] for more community-contributed details. ++ +Note that the VIB package versioning is independent of NUT or VMware versions, + they are however mentioned in downloadable file names. As of this writing, + there are builds spanning VMware ESXi 5.0-8.0 and NUT 2.7.4-2.8.0. ++ +WARNING: This module is provided "as is" and is not approved by VMware, +you may lose VMware support if you install it. Use it at your own risks. + + * link:https://github.com/rgc2000/NutClient-ESXi[GitHub repository with build recipes], + including link:https://github.com/rgc2000/NutClient-ESXi/releases[binary releases] + * link:https://rene.margar.fr/2012/05/client-nut-pour-esxi-5-0/[Original blog entry (French)] + * link:https://rene.margar.fr/2012/05/client-nut-pour-esxi-5-0/comment-page-22/#comment-13325[Historic + details of the recipe evolution] + * link:https://rene.margar.fr/downloads/NutClient-ESXi500-1.4.0.tar.gz[VIB package + (in fact automatically redirects to latest build)] diff --git a/docs/features.txt b/docs/features.txt index 631bdbcac5..502e7fc88b 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -5,7 +5,7 @@ NUT provides many features, and is always improving. Thus this list may lag behind the current code. Features frequently appear during the development cycles, so be sure to look at -the link:http://www.networkupstools.org/download.html[release notes and change logs] +the link:https://www.networkupstools.org/download.html[release notes and change logs] to see the latest additions. //////////////////////////////////////////////////////////////////////////////// @@ -18,7 +18,7 @@ More and more appliances manufacturers are bundling NUT... Multiple manufacturer and device support ---------------------------------------- -- Monitors many UPS, PDU, ATS, PSU and SCD models from more than 140 +- Monitors many UPS, PDU, ATS, PSU and SCD models from more than 170 manufacturers with a unified interface (link:stable-hcl.html[Hardware Compatibility List]). @@ -26,7 +26,7 @@ manufacturers with a unified interface common interface: * serial, * USB, - * network (SNMP, Eaton / MGE XML/HTTP). + * network (SNMP, Eaton / MGE XML/HTTP, IPMI). Multiple architecture support ----------------------------- @@ -38,7 +38,7 @@ common set of tools, even crossing architectures. Apple's OS X, commercial Solaris and open-source illumos distros, IRIX, HP/UX, Tru64 Unix, and AIX. -- Windows users may be able to build it directly with Cygwin. +- Windows users may be able to build it directly with MSYS2, MinGW or Cygwin. There is also a port of the client-side monitoring to Windows called WinNUT. - Your system will probably run it too. You just need a good C compiler and diff --git a/docs/hid-subdrivers.txt b/docs/hid-subdrivers.txt index e48ecf7b95..1657735423 100644 --- a/docs/hid-subdrivers.txt +++ b/docs/hid-subdrivers.txt @@ -127,13 +127,44 @@ 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 ~~~~~~~~~~~~~~~~~~~ In preparation for writing a subdriver for a device that is currently unsupported, run usbhid-ups with the following command line: - drivers/usbhid-ups -DD -u root -x explore -x vendorid=XXXX -x port=auto -s ups + drivers/usbhid-ups -DD -u root -x explore -x vendorid=XXXX \ + -x port=auto -s ups (substitute your device's 4-digit VendorID instead of "XXXX"). This will produce a bunch of debugging information, including a number @@ -143,12 +174,13 @@ This information forms the initial basis for a new subdriver. You should save this information to a file, e.g.: drivers/usbhid-ups -DD -u root -x explore -x vendorid=XXXX \ - -x port=auto -s ups 2>&1 | tee /tmp/info + -x port=auto -s ups -d1 2>&1 | tee /tmp/info + +You can now create an initial "stub" subdriver for your device by using +helper script `scripts/subdriver/gen-usbhid-subdriver.sh`. -You can create an initial "stub" subdriver for your device by using -script scripts/subdriver/gen-usbhid-subdriver.sh. Note: this only creates -a "stub" and needs to be further customized to be useful (see -"Customization" below). +NOTE: this only creates a driver code "stub" which needs to be further +customized to be actually useful (see "Customization" below). Use the script as follows: @@ -171,6 +203,40 @@ You can then recompile `usbhid-ups`, and start experimenting with the new subdriver. +Updating a subdriver +~~~~~~~~~~~~~~~~~~~~ + +You may have a device from vendor (and maybe model) whose support `usbhid-ups` +already claims. However, you may feel that the driver does not represent all +data points that your device serves. This may be possible, as vendors tend to +use the same identifiers for unrelated products, as well as produce revisions +of devices with same marketed name but different internals (due to chip and +other components availability, cost optimization, etc.) Even without sinister +implications, UPS firmwares evolve and so bugs and features can get added, +fixed and removed over time with truly the same hardware being involved. + +In this case you should follow the same instructions as above for "Writing +a subdriver", but specify the same subdriver name as the one which supports +your device family already. + +Then compare the generated source file with the one already committed to NUT +codebase, paying close attention to `..._hid2nut[]` table which maps "usage" +names to NUT data points. There may be several "usage" values served by +different device models or firmware versions, that provide same information +for a NUT data point, such as `input.voltage`. For the `hid2nut` mapping +tables, first hit wins (so you may e.g. prefer to check values with better +precision first). + +Using a GUI tool with partial-line difference matching and highlighting, +such as Meld or WinMerge, is recommended for this endeavour. + +For new data points in `hid2nut` tables be sure to not invent new names, +but use standard ones from `docs/nut-names.txt` file. Temporarily, the +`experimental.*` namespace may be used. +If you need to standardize a name for some concept not addressed yet, +please do so via nut-upsdev mailing list discussion. + + Customization ~~~~~~~~~~~~~ @@ -194,6 +260,140 @@ subdrivers are written: - powercom-hid.c/h - tripplite-hid.c/h +NOTE: To test existing data points (including those not yet translated +to standard NUT mappings conforming to <>), you can use custom drivers built after you +`./configure --with-unmapped-data-points`. +Production driver builds must not include any non-standard names. + +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. + +Finally note that such fix-ups may be not applicable to all devices or +firmware versions for what they assume their target audience is. If you +suspect that the fix-up method is actually causing problems, you can quickly +disable it with `disable_fix_report_desc` driver option for `usbhid-ups`. +If the problem does dissipate, please find a way to identify your "fixed" +hardware/firmware vs. those models where existing fix-up method should be +applied, and post a pull request so the NUT driver would handle both cases. + + +Investigating report descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Beside looking for problems with report descriptor processing in NUT code, +it is important to make sure what data the device actually serves on the +wire, and if it is logically consistent with the protocol requirements. + +While here, keep in mind that USB protocol on the wire has a specified +order of bytes involved, while processing on your computer may lay them +out differently due to bitness and endianness of the current binary build. +General NUT codebase (`libhid.c`, `hidparser.c`) aims to abstract this, +so application code like drivers can deal with their native numeric data +types, but when troubleshooting, do not rule out possibility of flaws +there as well. And certainly do not code any assumptions about ordered +multiple-byte ranges in a protocol buffer. + +For a deep dive into the byte stream, you will need additional tools: + +* get/build/install link:https://regina-rexx.sourceforge.io[regina-rexx] +* get/install link:https://github.com/abend0c1/hidrdd[HIDRDD] (uses REXX + as the interpreter) + +Typical troubleshooting of suspected firmware/protocol issues goes like this: + +* Turn the NUT `usbhid-ups` driver debug verbosity level up to 5 (or more) + and restart the driver, so it would record the HEX dump of report descriptor +* Look for reports from the driver of any problems it has already detected + and possibly amended (LogMin/LogMax, report descriptor fix-ups) +* Extract the HEX dump of the report descriptor from USB driver output from + the first step above, and run it through HIDRDD (and/or REXX directly, + per example below). +* Look at the HIDRDD output, with reference to any documents related to your + device and the USB/HID power devices class available in NUT documentation, + e.g. at https://www.networkupstools.org/ups-protocols.html +* Especially look for inconsistencies in the USB HID report descriptors (RD): + * between the min/max (logical and physical) values, + * the sizes of the report fields they apply to, + * the expected physical values (e.g., supply and output voltages, + over-voltage/under-voltage transfer points, ...) +* If you're seeing unexpected values for particular variables, look at the + raw data that is being sent, decide whether it makes sense in the context + of the logical and physical min/max values from the report descriptor. +* Read the NUT code, tracing through how each value gets processed looking + for where the result deviates from expectations... +* Think, code, test, rinse, repeat, post a PR :) + +.Example direct use of REXX +=========================== +Example adapted from https://github.com/networkupstools/nut/issues/2039 + +Run a NUT `usb-hid` driver with at least debug verbosity level 3 (`-DDD`) +to get a report descriptor dump starting with a line like this: +---- + 3.670755 [D3] Report Descriptor: (909 bytes) => 05 84 09 04 a1 01 ... +---- + +...and copy-paste those reported lines as input into `rexx` tool, which +would generate a C source file including human-worded description and +a relevant data structure: + +---- +:; rexx rd.rex -d --hex 05 84 09 04 a1 01 85 01 09 18 ... 55 b1 02 c0 c0 c0 + +//-------------------------------------------------------------------------------- +// Decoded Application Collection +//-------------------------------------------------------------------------------- + +/* +05 84 (GLOBAL) USAGE_PAGE 0x0084 Power Device Page +09 04 (LOCAL) USAGE 0x00840004 UPS (Application Collection) +A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00840004: Page=Power Device Page, Usage=UPS, Type=Application Collection) +85 01 (GLOBAL) REPORT_ID 0x01 (1) +09 18 (LOCAL) USAGE 0x00840018 Outlet System (Physical Collection) +... +*/ + +// All structure fields should be byte-aligned... +#pragma pack(push,1) + +//-------------------------------------------------------------------------------- +// Power Device Page featureReport 01 (Device <-> Host) +//-------------------------------------------------------------------------------- + +typedef struct +{ + uint8_t reportId; // Report ID = 0x01 (1) + // Collection: CA:UPS CP:OutletSystem CP:Outlet + int8_t POW_UPSOutletSystemOutletSwitchable; // Usage 0x0084006C: Switchable, Value = to + int8_t POW_UPSOutletSystemOutletDelayBeforeStartup; // Usage 0x00840056: Delay Before Startup, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletDelayBeforeShutdown; // Usage 0x00840057: Delay Before Shutdown, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletDelayBeforeReboot; // Usage 0x00840055: Delay Before Reboot, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletSwitchable_1; // Usage 0x0084006C: Switchable, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletDelayBeforeStartup_1; // Usage 0x00840056: Delay Before Startup, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletDelayBeforeShutdown_1; // Usage 0x00840057: Delay Before Shutdown, Value = -1 to 60 + int8_t POW_UPSOutletSystemOutletDelayBeforeReboot_1; // Usage 0x00840055: Delay Before Reboot, Value = -1 to 60 +} featureReport01_t; + +#pragma pack(pop) +---- +=========================== + Shutting down the UPS ~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/history.txt b/docs/history.txt index 4db0a359e3..77159050ff 100644 --- a/docs/history.txt +++ b/docs/history.txt @@ -336,6 +336,55 @@ the serial port were dropped from the tree. This tree was released as 2.0.0. +Backwards and Forwards Compatibility (NUT v1.x vs. v2.x) +-------------------------------------------------------- + +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 +so many new variables and commands. The resulting mess grew out of +control over the years. + +During the 1.3 development cycle, all variables and instant commands +were renamed to fit into a tree-like structure. There are major groups, +like input, output and battery. Members of those groups have been +arranged to make sense - input.voltage and output.voltage compliment +each other. The old names were UTILITY and OUTVOLT. The benefits in +this change are obvious. + +The 1.4 clients can talk to either type of server, and can handle either +naming scheme. 1.4 servers have a compatibility mode where they can +answer queries for both names, even though the drivers are internally +using the new format. + +When 1.4 clients talk to 1.4 or 2.0 (or more recent) servers, they will +use the new names. + +Here's a table to make it easier to visualize: + +[options="header"] +|============================================= +| 4+| Server version +| *Client version* | 1.0 | 1.2 | 1.4 | 2.0+ +| 1.0 | yes | yes | yes | no +| 1.2 | yes | yes | yes | no +| 1.4 | yes | yes | yes | yes +| 2.0+ | no | no | yes | yes +|============================================= + +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" +instead. + +Authors of separate monitoring programs should have used the 1.4 series +to write support for the new variables and command names. Client +software can easily support both versions as long as they like. If upsd +returns 'ERR UNKNOWN-COMMAND' to a GET request, you need to use REQ. + networkupstools.org ------------------- @@ -350,7 +399,7 @@ attached to exploits.org is not going to work. The solution was to register a new domain and set up mirrors. There are two initial web servers, with more on the way. The main project URL has changed -from `http://www.exploits.org/nut/` to http://www.networkupstools.org. +from `http://www.exploits.org/nut/` to https://www.networkupstools.org. The actual content is hosted on various mirrors which are updated regularly with rsync, so the days of dribbling bits through my DSL should be over. diff --git a/docs/images/ci/AppVeyor_logo-2x.png b/docs/images/ci/AppVeyor_logo-2x.png new file mode 100644 index 0000000000..f289509e1e Binary files /dev/null and b/docs/images/ci/AppVeyor_logo-2x.png differ diff --git a/docs/images/ci/AppVeyor_logo-ar21.png b/docs/images/ci/AppVeyor_logo-ar21.png new file mode 100644 index 0000000000..f3c277613b Binary files /dev/null and b/docs/images/ci/AppVeyor_logo-ar21.png differ diff --git a/docs/images/ci/CircleCI_vertical_black_logo.png b/docs/images/ci/CircleCI_vertical_black_logo.png new file mode 100644 index 0000000000..9691c7e881 Binary files /dev/null and b/docs/images/ci/CircleCI_vertical_black_logo.png differ diff --git a/docs/images/ci/DO_Powered_by_Badge_blue.png b/docs/images/ci/DO_Powered_by_Badge_blue.png new file mode 100644 index 0000000000..0d0163b023 Binary files /dev/null and b/docs/images/ci/DO_Powered_by_Badge_blue.png differ diff --git a/docs/images/ci/DO_Powered_by_Badge_blue_140pxW.png b/docs/images/ci/DO_Powered_by_Badge_blue_140pxW.png new file mode 100644 index 0000000000..cb9a3136d5 Binary files /dev/null and b/docs/images/ci/DO_Powered_by_Badge_blue_140pxW.png differ diff --git a/docs/images/ci/GitHub-Mark-140pxW.png b/docs/images/ci/GitHub-Mark-140pxW.png new file mode 100644 index 0000000000..755ab5e84d Binary files /dev/null and b/docs/images/ci/GitHub-Mark-140pxW.png differ diff --git a/docs/images/ci/GitHub-Mark-ea2971cee799.png b/docs/images/ci/GitHub-Mark-ea2971cee799.png new file mode 100644 index 0000000000..e28a837306 Binary files /dev/null and b/docs/images/ci/GitHub-Mark-ea2971cee799.png differ diff --git a/docs/images/ci/OC_logo-watercolor-256.png b/docs/images/ci/OC_logo-watercolor-256.png new file mode 100644 index 0000000000..7302bd9742 Binary files /dev/null and b/docs/images/ci/OC_logo-watercolor-256.png differ diff --git a/docs/images/ci/OC_logo_merged_140x26.png b/docs/images/ci/OC_logo_merged_140x26.png new file mode 100644 index 0000000000..67a6d7bc61 Binary files /dev/null and b/docs/images/ci/OC_logo_merged_140x26.png differ diff --git a/docs/images/ci/OC_logo_merged_171x32.png b/docs/images/ci/OC_logo_merged_171x32.png new file mode 100644 index 0000000000..a50bb86c32 Binary files /dev/null and b/docs/images/ci/OC_logo_merged_171x32.png differ diff --git a/docs/images/ci/OC_logotype.png b/docs/images/ci/OC_logotype.png new file mode 100644 index 0000000000..e34eae456a Binary files /dev/null and b/docs/images/ci/OC_logotype.png differ diff --git a/docs/images/ci/fosshost.org_Host_Dark_56px.png b/docs/images/ci/fosshost_org_Host_Dark_56px.png similarity index 100% rename from docs/images/ci/fosshost.org_Host_Dark_56px.png rename to docs/images/ci/fosshost_org_Host_Dark_56px.png diff --git a/docs/images/ci/fosshost.org_Host_Light_309px.png b/docs/images/ci/fosshost_org_Host_Light_309px.png similarity index 100% rename from docs/images/ci/fosshost.org_Host_Light_309px.png rename to docs/images/ci/fosshost_org_Host_Light_309px.png diff --git a/docs/images/ci/fosshost.org_Host_Light_38px.png b/docs/images/ci/fosshost_org_Host_Light_38px.png similarity index 100% rename from docs/images/ci/fosshost.org_Host_Light_38px.png rename to docs/images/ci/fosshost_org_Host_Light_38px.png diff --git a/docs/images/ci/gandi-ar21.png b/docs/images/ci/gandi-ar21.png new file mode 100644 index 0000000000..08e19e6183 Binary files /dev/null and b/docs/images/ci/gandi-ar21.png differ diff --git a/docs/images/ci/gandi-ar21.svg b/docs/images/ci/gandi-ar21.svg new file mode 100644 index 0000000000..9f543bde84 --- /dev/null +++ b/docs/images/ci/gandi-ar21.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/ci/jenkins-nut-transparent-bg-140pxW.png b/docs/images/ci/jenkins-nut-transparent-bg-140pxW.png new file mode 100644 index 0000000000..e5cfeb9c4d Binary files /dev/null and b/docs/images/ci/jenkins-nut-transparent-bg-140pxW.png differ diff --git a/docs/images/ci/jenkins-nut.css b/docs/images/ci/jenkins-nut.css index 464f0e2d45..634bc00de0 100644 --- a/docs/images/ci/jenkins-nut.css +++ b/docs/images/ci/jenkins-nut.css @@ -6,8 +6,8 @@ * as published at https://www.jenkins.io/artwork/ collection * under https://creativecommons.org/licenses/by-sa/3.0/ license * - * Fosshost logo derived (resized to readable height in banner line) - * from https://fosshost.org/media/ + * DigitalOcean logo derived (resized to readable height in banner line) + * from https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_blue.png */ /* Custom style for Jenkins */ @@ -39,33 +39,33 @@ object-fit: contain; } .logo:after { -content: "CI for NUT"; +content: "NUT CI farm"; font-weight: bold; -font-size: 40px; +font-size: 24px; /* font-family: "Brush Script MT", cursive; */ margin-left: 20px; margin-right: 200px; color: Gold; -line-height: 40px; +line-height: 24px; } .logo { -background: url("/userContent/layout/fosshost.org_Host_Dark_56px.png"); +background: url("/userContent/layout/DO_Powered_by_Badge_blue_140pxW.png"); background-repeat: no-repeat; /* background-attachment: fixed; */ background-position: right; background-origin: content-box; - padding-right: 4px; - /* Glue to the search box, to hide FH logo plank color gap with background on the right side */ + padding-right: 14px; + /* Glue to the search box, to hide DO logo plank color gap with background on the right side */ } -/* Match color of fosshost logo */ +/* Match/contrast color of DigitalOcean logo */ .page-header { background-color: #2a2a2a; } /* .page-footer__footer-id-placeholder { -background: url("/userContent/layout/fosshost.org_Host_Dark_56px.png") no-repeat; +background: url("/userContent/layout/fosshost_org_Host_Dark_56px.png") no-repeat; } */ diff --git a/docs/images/ci/jenkins-nut.txt b/docs/images/ci/jenkins-nut.txt index d7356823e7..b7ca13378a 100644 --- a/docs/images/ci/jenkins-nut.txt +++ b/docs/images/ci/jenkins-nut.txt @@ -16,7 +16,11 @@ downscaled image for the Jenkins dashboard banner. There are variants in several resolutions available, and options with a larger or smaller NUT logo served on the Jenkins butler's plate. -The fosshost.org_Host_Dark_56px.png was downscaled from resources +The fosshost_org_Host_Dark_56px.png was downscaled from resources at https://fosshost.org/media/ to fit into the Jenkins dashboard banner and remain readable. Thanks to Fosshost for providing the machines involved in the new NUT CI farm! + +Logos of CI hosting companies and other organizations which help +the NUT project in daily operations were downloaded in 2023 for +the Acknowledgements chapter in the top-level `README.adoc`. diff --git a/docs/maintainer-guide.txt b/docs/maintainer-guide.txt index 3c16955a67..51c7561ab4 100644 --- a/docs/maintainer-guide.txt +++ b/docs/maintainer-guide.txt @@ -43,7 +43,7 @@ These are the general rules that apply to mailing list moderation: to store the file(s). - other (ie report or request requiring more than 1 mail): REJECT with a -message explaining the reason. The following can serve as a base: + message explaining the reason. The following can serve as a base: Dear XXX, Your message to the nut-upsXXX mailing was rejected because you must @@ -66,27 +66,133 @@ Release process New process: - we will only work on the trunk for the day to day bugfixing and -standard modifications (what was mostly happening in Testing -currently), + standard modifications (what was mostly happening in Testing + currently), - the trunk will be used to generate the testing releases (only using -the tags, after a small freeze period), + the tags, after a small freeze period), - bigger changes, invasive modifications and cutting edge developments -will have to be addressed in separate branches, until stabilization. -When things are ok and validated to enter the trunk, merging these -branches into the trunk can happen. + will have to be addressed in separate branches, until stabilization. + When things are ok and validated to enter the trunk, merging these + branches into the trunk can happen. + I insist on the *validation* to enter the trunk, since some changes might have to wait for major releases, to match our current release process. -SANDBOX (to be completed and pushed) +MAINTAINER SANDBOX (to be completed and pushed) + +* be sure to proceed with the below steps on a system provisioned with all + possible build prerequisites for NUT, especially regarding documentation + and nut-website building and checking, and with maintainer GPG keys in + the chain + +* clean up "in-development" bits from files, e.g.: +** TODO etc. referring planned future in the `NEWS.adoc` and `UPDATING.adoc` + files +** comment away the top-most (auto-resolved) NUT version and build date + in `docs/docinfo.xml.in` -- DO NOT add (or at least commit) an entry + for the actual fixed release version and date just yet +** NOTE: The `docs/docinfo.xml.sh` script can help update the list for + existing git tags, if some were skipped before. This can be used to + cheat a bit about adding the new entry (with a draft tag). +** commit this change (to add a revert-commit after the release tag), e.g.: +---- +:; git commit -sm 'NEWS.adoc, UPGRADING.adoc, docs/docinfo.xml.in: finalize text before NUT v2.8.0 release' +---- + +* revise the contents of `NEWS.adoc` and `UPDATING.adoc` files; verify that + any recent changes to drivers (including `main.c` and other major impact + from common code) and sub-drivers (`*-hid.c` for `usbhid-ups`, `*-mib.c` + for `snmp-ups`, `nutdrv_qx_*` etc.) have been reflected in bumps to their + `DRIVER_VERSION` or equivalen macros +** ideally maintained during development, as features are getting merged for + community testing and future development baseline in the master branch +* NOTE that the `ChangeLog` file is currently not tracked in SCM +* update this document: `docs/maintainer-guide.txt` as it inevitably requires +* commit these finishing touches + +* bump the release identification: +** add an entry in `docs/docinfo.xml.in` for the actual fixed release version + and date +** revise `.github/workflows/PyNUTClient.yml` for fallback `TAG_NAME` naming +** revise `appveyor.yml` for branch naming +** revise `scripts/Windows/build-mingw-nut.sh` for fallback value of `VER_OPT` +** update version to (ex: 2.8.0) in `configure.ac` +** commit with a relevant release message, e.g.: +---- +:; git commit -sm 'Update versions for release of NUT v2.8.0' +---- + +* last-minute update against possible master-branch changes (and be sure to + apply the release-version changes described above to your local copy of + the `master` branch, even if originally staged in another): +---- +:; git fetch --all && git rebase upstream/master +---- +* run the last-minute build to be sure the release is sane (no typos in any + recently changed document files, etc.) with `./ci_build.sh` or full ritual: +---- +:; rm -f Makefile configure +:; ./autogen.sh && \ + ./configure --with-all --with-dev --with-doc --enable-spellcheck \ + --enable-warnings --enable-Werror --enable-maintainer-mode && \ + make -j 8 all && \ + make -j 8 spellcheck && \ + make -j 8 distcheck +---- +* create a GPG-signed tag v (ex: v2.8.0): +---- +:; git tag -sm 'Release NUT v2.8.0' +---- +** try to avoid adding signed tags later (ex. v2.8.0-signed) to avoid the + mess in GitHub release URLs (or do amend that post-factum), for more + details see e.g. https://github.com/networkupstools/nut/issues/1971 +* don't forget to push not only the code, but also the tag: +---- +:; git push upstream && git push --tags upstream +---- + +* `make dist` (if you did not `make distcheck` above or had some changes since + then) to store the source tarball, checksum and signature files + +* post-release update of the "in-development" codebase: +** maybe update nut/configure.ac version to .1 (ex: 2.8.0.1) +** `git revert` the commit which cleaned up "in-development" bits above +** Possibly resolve relevant merge conflicts for the changed context -* update version to (ex: 2.7.3) in nut/configure.ac -* create a GPG-signed tag v (ex: v2.7.3) -* `make dist` -* maybe update nut/configure.ac version to .1 (ex: 2.7.3.1) * push commits and tag -* update nut/ and ddl/ submodules in nut-website/ (this should update the website's version as well) -* add download hashes for tarball +* Update `nut-website`: +** rendering should auto-update by NUT CI farm based on source changes, + but in case of urgency -- can be expedited with a prepared workstation + (see README of that project) or from the NUT CI farm agent directly +** add an entry to `news.txt` +** update `nut` and `ddl` submodules in nut-website/ to refer to latest info + as of current release (this should update the website's version as well). + NOTE: for `nut` submodule be sure to refer to the tagged commit, not to + the subsequent "in-development" codebase. +** in `source` submodule add a copy of tarball, checksum and hash files for + download +** `git tag` the website release +** generate and publish a "historic" sub-site snapshot (currently manually) +** update `nut` submodule to current commit ("in-development" codebase), and + the `historic/index.txt` to refer to the snapshot for reference (users of + specific-version packages) +** generate and publish the usual website revision (by CI or manually) + +* check that the website renders properly + +* draft and publish a GitHub release based on the signed tag +** attach the same copy of tarball, checksum and hash files as for nut-website + +* Make sure that the PyNUT module for the tagged release got published to + the main PyPI repository, as a build associated with the NUT release + version. This *should* be ensured by the GitHub actions, but the curent + workflow definition uses both `paths` and `branches`/`tags` triggers and + this may cause a release to be skipped if the PyNUT sources did not change. + The `scripts/python/module/Makefile.am` recipes automate the needed actions + to happen from a prepared maintainer's operating environment. + +* announce on mailing list, IRC, etc. ////////////////////////////////////////////////////////////////////////////// diff --git a/docs/man/.editorconfig b/docs/man/.editorconfig new file mode 100644 index 0000000000..2c3b51973d --- /dev/null +++ b/docs/man/.editorconfig @@ -0,0 +1,7 @@ +# Nested configuration file + +# The following documents with code samples have +# intentional trailing whitespace and should be +# only reformatted manually and attentively: +[{nut.conf,upsmon.conf}.txt] +trim_trailing_whitespace = false 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..4199880306 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -1,4 +1,4 @@ -# Network UPS Tools: man +# Network UPS Tools: man pages # # Notes: @@ -17,8 +17,10 @@ # files from source tree # Is "egrep == grep -E" always valid? (maybe all a job for configure.ac) -EGREP = egrep -#EGREP = grep -E +#EGREP = egrep +EGREP = grep -E + +all: # Base configuration and client manpages, always installed SRC_CONF_PAGES = \ @@ -37,7 +39,7 @@ MAN_CONF_PAGES = \ upsd.users.5 \ upsmon.conf.5 \ upssched.conf.5 -endif +endif WITH_MANS man5_MANS = $(MAN_CONF_PAGES) @@ -49,14 +51,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 \ @@ -75,7 +83,7 @@ MAN_CLIENT_PAGES = \ upsmon.8 \ upsrw.8 \ upssched.8 -endif +endif WITH_MANS man8_MANS = $(MAN_CLIENT_PAGES) @@ -96,7 +104,7 @@ SRC_TOOL_PAGES = nut-scanner.txt nut-recorder.txt nutconf.txt if WITH_MANS MAN_TOOL_PAGES = nut-scanner.8 nut-recorder.8 nutconf.8 -endif +endif WITH_MANS man8_MANS += $(MAN_TOOL_PAGES) @@ -121,12 +129,12 @@ MAN8_CGI_PAGES = \ upsset.cgi.8 \ upsstats.cgi.8 \ upsimage.cgi.8 -endif +endif WITH_MANS if WITH_CGI man5_MANS += $(MAN5_CGI_PAGES) man8_MANS += $(MAN8_CGI_PAGES) -endif +endif WITH_CGI HTML_CGI_MANS = \ hosts.conf.html \ @@ -166,11 +174,15 @@ 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_nut_simulation.txt \ nutscan_scan_avahi.txt \ nutscan_scan_ipmi.txt \ nutscan_scan_eaton_serial.txt \ + nutscan_display_sanity_check.txt \ + nutscan_display_sanity_check_serial.txt \ + nutscan_display_ups_conf_with_sanity_check.txt \ nutscan_display_ups_conf.txt \ nutscan_display_parsable.txt \ nutscan_cidr_to_ip.txt \ @@ -181,6 +193,7 @@ SRC_DEV_PAGES = \ nutscan_init.txt \ nutscan_get_serial_ports_list.txt \ libupsclient-config.txt \ + sockdebug.txt \ skel.txt if WITH_MANS @@ -279,17 +292,22 @@ 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_nut_simulation.3 \ nutscan_scan_avahi.3 \ nutscan_scan_ipmi.3 \ nutscan_scan_eaton_serial.3 \ + nutscan_display_sanity_check.3 \ + nutscan_display_sanity_check_serial.3 \ + nutscan_display_ups_conf_with_sanity_check.3 \ nutscan_display_ups_conf.3 \ nutscan_display_parsable.3 \ nutscan_cidr_to_ip.3 \ nutscan_new_device.3 \ nutscan_free_device.3 \ nutscan_add_option_to_device.3 \ + nutscan_add_commented_option_to_device.3 \ nutscan_add_device_to_device.3 \ nutscan_get_serial_ports_list.3 \ nutscan_init.3 @@ -300,18 +318,26 @@ upscli_readline_timeout.3: upscli_readline.3 upscli_sendline_timeout.3: upscli_sendline.3 touch $@ +# Alias page for one text describing two commands: +nutscan_add_commented_option_to_device.3: nutscan_add_option_to_device.3 + touch $@ + MAN1_DEV_PAGES = \ libupsclient-config.1 -endif + +MAN8_DEV_PAGES = \ + sockdebug.8 +endif WITH_MANS if WITH_DEV man3_MANS = $(MAN3_DEV_PAGES) +man8_MANS += $(MAN8_DEV_PAGES) if !WITH_PKG_CONFIG man1_MANS = $(MAN1_DEV_PAGES) -endif -# WITH_DEV -endif +endif !WITH_PKG_CONFIG + +endif WITH_DEV HTML_DEV_MANS = \ upsclient.html \ @@ -341,11 +367,15 @@ 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_nut_simulation.html \ nutscan_scan_avahi.html \ nutscan_scan_ipmi.html \ nutscan_scan_eaton_serial.html \ + nutscan_display_sanity_check.html \ + nutscan_display_sanity_check_serial.html \ + nutscan_display_ups_conf_with_sanity_check.html \ nutscan_display_ups_conf.html \ nutscan_display_parsable.html \ nutscan_cidr_to_ip.html \ @@ -356,16 +386,24 @@ HTML_DEV_MANS = \ nutscan_get_serial_ports_list.html \ nutscan_init.html \ libupsclient-config.html \ + sockdebug.html \ skel.html +# Can't make this work on all make implementations at once, so disabled for now +# Anyway it would be the same man-like page for several functions +HTML_DEV_MANS_FICTION = \ + nutscan_add_commented_option_to_device.html + +nutscan_add_commented_option_to_device.html: nutscan_add_option_to_device.html + test -n "$?" -a -s "$@" && rm -f $@ && ln -s $? $@ # Drivers related manpages -# (--with-drivers=...) +# If (--with-drivers=...) then we only build specific documents, however +# still do track (and EXTRA_DIST, and spellcheck) all available sources. if SOME_DRIVERS man8_MANS += $(DRIVER_MAN_LIST) - -else +endif # (--with-serial) SRC_SERIAL_PAGES = \ @@ -404,6 +442,7 @@ SRC_SERIAL_PAGES = \ powerpanel.txt \ rhino.txt \ riello_ser.txt \ + sms_ser.txt \ safenet.txt \ solis.txt \ tripplite.txt \ @@ -412,6 +451,7 @@ SRC_SERIAL_PAGES = \ victronups.txt \ apcupsd-ups.txt +if ! SOME_DRIVERS if WITH_MANS MAN_SERIAL_PAGES = \ al175.8 \ @@ -448,6 +488,7 @@ MAN_SERIAL_PAGES = \ powerpanel.8 \ rhino.8 \ riello_ser.8 \ + sms_ser.8 \ safenet.8 \ solis.8 \ tripplite.8 \ @@ -455,11 +496,11 @@ MAN_SERIAL_PAGES = \ upscode2.8 \ victronups.8 \ apcupsd-ups.8 -endif +endif WITH_MANS if WITH_SERIAL man8_MANS += $(MAN_SERIAL_PAGES) -endif +endif WITH_SERIAL HTML_SERIAL_MANS = \ al175.html \ @@ -496,6 +537,7 @@ HTML_SERIAL_MANS = \ powerpanel.html \ rhino.html \ riello_ser.html \ + sms_ser.html \ safenet.html \ solis.html \ tripplite.html \ @@ -503,18 +545,21 @@ HTML_SERIAL_MANS = \ upscode2.html \ victronups.html \ apcupsd-ups.html +endif ! SOME_DRIVERS # (--with-snmp) SRC_SNMP_PAGES = snmp-ups.txt +if ! SOME_DRIVERS if WITH_MANS MAN_SNMP_PAGES = snmp-ups.8 -endif +endif WITH_MANS if WITH_SNMP man8_MANS += $(MAN_SNMP_PAGES) -endif +endif WITH_SNMP HTML_SNMP_MANS = snmp-ups.html +endif ! SOME_DRIVERS # (--with-snmp_dmf{_lua}) SRC_SNMP_DMF_PAGES = snmp-ups-dmf.txt @@ -536,11 +581,14 @@ SRC_USB_LIBUSB_PAGES = \ blazer-common.txt \ blazer_usb.txt \ nutdrv_atcl_usb.txt \ + nut_usb_addvars.txt \ richcomm_usb.txt \ riello_usb.txt \ tripplite_usb.txt \ usbhid-ups.txt +if ! SOME_DRIVERS +# NOTE: nut_usb_addvars and blazer-common are not standalone man pages if WITH_MANS MAN_USB_LIBUSB_PAGES = \ bcmxcp_usb.8 \ @@ -550,11 +598,11 @@ MAN_USB_LIBUSB_PAGES = \ riello_usb.8 \ tripplite_usb.8 \ usbhid-ups.8 -endif +endif WITH_MANS if WITH_USB man8_MANS += $(MAN_USB_LIBUSB_PAGES) -endif +endif WITH_USB HTML_USB_LIBUSB_MANS = \ bcmxcp_usb.html \ @@ -564,104 +612,143 @@ HTML_USB_LIBUSB_MANS = \ riello_usb.html \ tripplite_usb.html \ usbhid-ups.html +endif ! SOME_DRIVERS # (--with-serial / --with-usb) SRC_SERIAL_USB_PAGES = \ nutdrv_qx.txt +if ! SOME_DRIVERS if WITH_MANS MAN_SERIAL_USB_PAGES = \ nutdrv_qx.8 -endif +endif WITH_MANS if WITH_SERIAL man8_MANS += $(MAN_SERIAL_USB_PAGES) -else +else !WITH_SERIAL if WITH_USB man8_MANS += $(MAN_SERIAL_USB_PAGES) -endif -endif +endif WITH_USB +endif !WITH_SERIAL HTML_SERIAL_USB_MANS = \ nutdrv_qx.html +endif ! SOME_DRIVERS # (--with-neon) SRC_NETXML_PAGES = netxml-ups.txt +if ! SOME_DRIVERS if WITH_MANS MAN_NETXML_PAGES = netxml-ups.8 -endif +endif WITH_MANS if WITH_NEON man8_MANS += $(MAN_NETXML_PAGES) -endif +endif WITH_NEON HTML_NETXML_MANS = netxml-ups.html +endif ! SOME_DRIVERS # (--with-powerman) SRC_POWERMAN_PAGES = powerman-pdu.txt +if ! SOME_DRIVERS if WITH_MANS MAN_POWERMAN_PAGES = powerman-pdu.8 -endif +endif WITH_MANS if WITH_LIBPOWERMAN man8_MANS += $(MAN_POWERMAN_PAGES) -endif +endif WITH_LIBPOWERMAN HTML_POWERMAN_MANS = powerman-pdu.html +endif ! SOME_DRIVERS # (--with-ipmi) SRC_IPMIPSU_PAGES = nut-ipmipsu.txt +if ! SOME_DRIVERS if WITH_MANS MAN_IPMIPSU_PAGES = nut-ipmipsu.8 -endif +endif WITH_MANS if WITH_IPMI man8_MANS += $(MAN_IPMIPSU_PAGES) -endif +endif WITH_IPMI HTML_IPMIPSU_MANS = nut-ipmipsu.html +endif ! SOME_DRIVERS +# (--with-macosx_ups) SRC_MACOSX_PAGES = macosx-ups.txt +if ! SOME_DRIVERS if WITH_MANS MAN_MACOSX_PAGES = macosx-ups.8 -endif +endif WITH_MANS if WITH_MACOSX man8_MANS += $(MAN_MACOSX_PAGES) -endif +endif WITH_MACOSX HTML_MACOSX_MANS = macosx-ups.html +endif ! SOME_DRIVERS +# (--with-modbus) SRC_MODBUS_PAGES = phoenixcontact_modbus.txt \ generic_modbus.txt \ - huawei-ups2000.txt + huawei-ups2000.txt \ + socomec_jbus.txt \ + adelsystem_cbi.txt \ + apc_modbus.txt + +if ! SOME_DRIVERS if WITH_MANS MAN_MODBUS_PAGES = phoenixcontact_modbus.8 \ generic_modbus.8 \ - huawei-ups2000.8 -endif + huawei-ups2000.8 \ + socomec_jbus.8 \ + adelsystem_cbi.8 \ + apc_modbus.8 +endif WITH_MANS if WITH_MODBUS man8_MANS += $(MAN_MODBUS_PAGES) -endif +endif WITH_MODBUS HTML_MODBUS_MANS = phoenixcontact_modbus.html \ generic_modbus.html \ - huawei-ups2000.html + huawei-ups2000.html \ + socomec_jbus.html \ + adelsystem_cbi.html \ + apc_modbus.html +endif ! SOME_DRIVERS +# (--with-linux_i2c) SRC_LINUX_I2C_PAGES = asem.txt pijuice.txt +if ! SOME_DRIVERS if WITH_MANS MAN_LINUX_I2C_PAGES = asem.8 pijuice.8 -endif +endif WITH_MANS if WITH_LINUX_I2C man8_MANS += $(MAN_LINUX_I2C_PAGES) -endif +endif WITH_LINUX_I2C HTML_LINUX_I2C_MANS = asem.html pijuice.html +endif ! SOME_DRIVERS -# SOME_DRIVERS -endif +# (--with-gpio) +SRC_GPIO_PAGES = generic_gpio.txt +if ! SOME_DRIVERS +if WITH_MANS +MAN_GPIO_PAGES = generic_gpio.8 +endif WITH_MANS + +if WITH_GPIO +man8_MANS += $(MAN_GPIO_PAGES) +endif WITH_GPIO + +HTML_GPIO_MANS = generic_gpio.html +endif ! SOME_DRIVERS MAN_MANS = if WITH_MANS @@ -673,6 +760,7 @@ MAN_MANS += \ $(MAN8_CGI_PAGES) \ $(MAN1_DEV_PAGES) \ $(MAN3_DEV_PAGES) \ + $(MAN8_DEV_PAGES) \ $(MAN_SERIAL_PAGES) \ $(MAN_SNMP_PAGES) \ $(MAN_SNMP_DMF_PAGES) \ @@ -683,17 +771,11 @@ MAN_MANS += \ $(MAN_IPMIPSU_PAGES) \ $(MAN_MACOSX_PAGES) \ $(MAN_MODBUS_PAGES) \ - $(MAN_LINUX_I2C_PAGES) -endif + $(MAN_LINUX_I2C_PAGES) \ + $(MAN_GPIO_PAGES) +endif WITH_MANS -# 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) \ @@ -704,7 +786,26 @@ SRC_ALL_PAGES = \ $(SRC_IPMIPSU_PAGES) \ $(SRC_MACOSX_PAGES) \ $(SRC_MODBUS_PAGES) \ - $(SRC_LINUX_I2C_PAGES) + $(SRC_LINUX_I2C_PAGES) \ + $(SRC_GPIO_PAGES) + +if SOME_DRIVERS +# (Legacy note) The list above probably came up empty in this case, so make sure +# that the drivers requested by configure script are documented in the build; +# notably in the linkman-driver-names.txt file. +SRC_DRIVERS_PAGES += \ + $(DRIVER_MAN_LIST_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) EXTRA_DIST = \ $(SRC_ALL_PAGES) \ @@ -718,8 +819,8 @@ if ! SKIP_MANS EXTRA_DIST += dist dist: @echo "ERROR: Manpage building was disabled by configure script, and these pages are required for our proper 'make dist'" >&2 ; false -endif -endif +endif ! SKIP_MANS +endif ! WITH_MANS # For builds done from dist'ed sources, there may be a conflict of timestamps # between original *.txt files and pre-built manpages etc. leading to skipping @@ -745,11 +846,60 @@ HTML_MANS = \ $(HTML_IPMIPSU_MANS) \ $(HTML_MACOSX_MANS) \ $(HTML_MODBUS_MANS) \ - $(HTML_LINUX_I2C_MANS) - -all: - -html-man: $(HTML_MANS) index.html + $(HTML_LINUX_I2C_MANS) \ + $(HTML_GPIO_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"$(abs_srcdir)" != x"$(abs_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],' ; \ + ) > "$@" + @if test ! -s "$@" ; then echo "- No NUT drivers were built" > "$@" ; fi + +linkman-drivertool-names.txt: + @if test x"$(abs_srcdir)" != x"$(abs_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],' ; \ + ) > "$@" + @if test ! -s "$@" ; then echo "- No NUT driver tools were built" > "$@" ; fi + +# 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) + +# Make sure sources are there for out-of-tree builds: +$(HTML_MANS) $(MAN_MANS) index.html: $(abs_top_builddir)/docs/man/.prep-src-docs + +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 @@ -772,7 +922,7 @@ check-html-man: $(HTML_MANS) @FAILED=""; CHECKED=0; LANG=C; LC_ALL=C; export LANG; export LC_ALL; \ for F in $(HTML_MANS) ; do \ CHECKED="`expr $$CHECKED + 1`"; \ - test -s "$$F" && { file "$$F" | $(EGREP) -i '(XML|HTML.*document)' > /dev/null ; } || FAILED="$$FAILED $$F" ; \ + test -s "$$F" && { file "$$F" | $(EGREP) -i '(XML|HTML.*document|symbolic link)' > /dev/null ; } || FAILED="$$FAILED $$F" ; \ done; if test -n "$$FAILED" ; then \ echo "FAILED HTML-man sanity check for:$$FAILED" >&2 ; file $$FAILED >&2 ; exit 1; \ fi; echo "PASSED HTML-man sanity check (checked $$CHECKED files)"; exit 0 @@ -813,9 +963,9 @@ check-man-txt: $(SRC_ALL_PAGES) echo "FAILED man-source sanity check for:$$FAILED" >&2 ; file $$FAILED >&2 ; exit 1; \ fi; echo "PASSED man-source sanity check (checked $$CHECKED files)"; exit 0 -CLEANFILES = *-spellchecked +CLEANFILES = *-spellchecked .prep-src-docs *-prepped -SUFFIXES = .txt .html .1 .3 .5 .8 +SUFFIXES = .txt-prepped .txt .html .1 .3 .5 .8 # For builds with allowed installation of prebuild man pages, check that # they exist in sources (make would pull them automatically as a fallback @@ -837,74 +987,105 @@ DOCBUILD_BEGIN = { \ if test -n "$${A2X_OUTDIR}" && test "$${A2X_OUTDIR}" != '.' ; then \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ - mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + $(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 ; \ + 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 ; \ } -.txt.html: +### Call the prep step consistently to create symlinks (out-of-tree) +### or just touch-files for peace of mind (in-tree builds). Then we +### use these path names (truncated "-prepped") now surely located +### in the builddir as the sources for rendered docs. +*.txt-prepped: $(abs_top_builddir)/docs/man/.prep-src-docs + +#.txt.txt-prepped: $(abs_top_builddir)/docs/man/.prep-src-docs + +### 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. +### Note: `(top_)srcdir` and `(top_)builddir` must end with a path +### separator, or be empty -- so in all cases letting the resulting +### string resolve meaningfully in the filesystem during docs build. +.txt-prepped.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@" \ - -o "$${A2X_OUTDIR}/$(@F)" $< || RES=$$? ; \ + --attribute srcdir="$(abs_srcdir)/" \ + --attribute builddir="$(abs_builddir)/" \ + --attribute top_srcdir="$(abs_top_srcdir)/" \ + --attribute top_builddir="$(abs_top_builddir)/" \ + -o "$${A2X_OUTDIR}/$(@F)" "$(/dev/null`" ; then \ + rm -f "$@.$$$$" "$@.working" ; \ + exit 0; \ + fi ; \ + rm -f "$@.$$$$" ; \ + COUNT=0; \ + linksrcroot="$(abs_srcdir)" ; \ + for F in `echo $(PREP_SRC) | tr ' ' '\n' | sort -n | uniq` ; do \ + case "$$F" in \ + /*) F="`echo "$$F" | sed "s#^$(abs_top_srcdir)/*#./#"`"; \ + if test x"$${linkroot}" = x"$(abs_builddir)" ; then \ + linkroot="$(abs_top_builddir)" ; \ + linksrcroot="$(abs_top_srcdir)" ; \ + cd "$(abs_top_builddir)" ; \ + fi ;; \ + "$(srcdir)"/*) F="`echo "$$F" | sed 's#^$(srcdir)/*#./#'`" ;; \ + esac ; \ + D="`dirname "$$F"`" ; \ + $(MKDIR_P) "$${linkroot}/$$D" || { rm -f "$@.working" ; exit 1 ; } ; \ + if ! test -e "$${linkroot}/$$F" && test -s "$${linksrcroot}/$$F" ; then \ + echo " LN '$${linksrcroot}/$$F' => '$${linkroot}/$$F' (PWD = '`pwd`')" >&2 ; \ + ln -fs "$${linksrcroot}/$$F" "$${linkroot}/$$F" || { rm -f "$@.working" ; exit 1 ; } ; \ + COUNT="`expr $$COUNT + 1`" ; \ + fi ; \ + if ! test -e "$${linkroot}/$${F}-prepped" ; then \ + touch "$${linkroot}/$${F}-prepped" || { rm -f "$@.working" ; exit 1 ; } ; \ + COUNT="`expr $$COUNT + 1`" ; \ + fi ; \ + done ; \ + fi ; \ + if test "$$COUNT" -gt 0 -o ! -e "$@" ; then touch "$@" ; fi + @rm -f "$@.working" MAINTAINERCLEANFILES = Makefile.in .dirstamp clean-local: - rm -rf tmp + $(AM_V_at)rm -rf tmp + $(AM_V_at)for F in $(PREP_SRC) ; do \ + case "$$F" in \ + /*) F="`echo "$$F" | sed "s#^$(abs_top_srcdir)/*#./#"`"; cd "$(abs_top_builddir)" ;; \ + esac ; \ + if test x"$(abs_srcdir)" != x"$(abs_builddir)" ; then \ + if test -L "$$F" || test -h "$$F" ; then \ + rm -f "$$F" ; \ + fi ; \ + fi ; \ + rm -f "$${F}-prepped" ; \ + done ; \ + rm -f "$(abs_top_builddir)/docs/man/.prep-src-docs"* diff --git a/docs/man/adelsystem_cbi.txt b/docs/man/adelsystem_cbi.txt new file mode 100644 index 0000000000..5cbe3d45c1 --- /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: https://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/al175.txt b/docs/man/al175.txt index 4fcd2216ef..276f85f11b 100644 --- a/docs/man/al175.txt +++ b/docs/man/al175.txt @@ -3,16 +3,23 @@ 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 +SYNOPSIS +-------- + +*al175* -h + +*al175* -a 'UPS_NAME' ['OPTIONS'] + +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 +33,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 +50,7 @@ Stop a battery test. VARIABLES --------- + Besides status, this driver reads UPS state into following variables: - *ups.test.result* @@ -53,12 +63,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 +78,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/apc_modbus.txt b/docs/man/apc_modbus.txt new file mode 100644 index 0000000000..07cac400f6 --- /dev/null +++ b/docs/man/apc_modbus.txt @@ -0,0 +1,102 @@ +APC_MODBUS(8) +============= + +NAME +---- + +apc_modbus - Driver for APC Smart-UPS Modbus protocol + +SYNOPSIS +-------- + +*apc_modbus* -h + +*apc_modbus* -a 'UPS_NAME' ['OPTIONS'] + +SUPPORTED HARDWARE +------------------ + +Generally this driver should work for all the APC Modbus UPS devices. Some +devices might expose more than is currently supported, like multiple phases. +A general rule of thumb is that APC devices (or firmware versions) released +after 2010 are more likely to support Modbus than the USB HID standard. + +Tested with the following hardware: + + - SMT1500 (Smart-UPS 1500, Firmware 9.6) + - SMX750 (Smart-UPS X 750, Firmware 10.1) + - SMX1500 (Smart-UPS X 1500, Firmware 15.0) + +Note that you will have to enable Modbus communication. In the front panel of +the UPS, go to Advanced Menu mode, under Configuration and enable Modbus. + +NOTE: This driver was tested with Serial, TCP and USB interfaces for Modbus. +Notably, the Serial ports are not available on all devices nowadays; the TCP +support may require a purchase of an additional network management card; and +the USB support *currently* requires a non-standard build of libmodbus (pull +request against the upstream library is pending, as of at the time of this +publication) as a pre-requisite to building NUT with this part of the support. +For more details (including how to build the custom library and NUT with it) +please see link:https://github.com/networkupstools/nut/pull/2063[NUT PR #2063] + +NOTE: As currently published, this driver supports reading information from +the UPS. Implementation of support to write (set modifiable variables or send +commands) is expected with a later release. This can impact the host shutdown +routines in particular (no ability to actively tell the UPS to power off or +cycle in the end). As a workaround, you can try integrating `apctest` (from +the "apcupsd" project) with a "Test to kill power" into your late-shutdown +procedure, if needed. + +EXTRA ARGUMENTS +--------------- + +This driver also supports the following optional settings: + +include::nut_usb_addvars.txt[] + +*porttype*='value':: +Set the type of the port used. Available values are serial for RS232/485 based +connections, tcp for TCP/IP connections and usb for USB connections. + +*port*='value':: +Depending on the port type you can select a port here. For usb only auto is +supported, for serial you can pass a device path like /dev/ttyS0 and for tcp you +can pass a hostname with optional port like example.com:502. + +*baudrate*='num':: +Set the speed of the serial connection. The default baudrate is 9600. + +*parity*='value':: +Set the parity of the serial connection. Available values are N for none, E for +even and O for odd. The default parity is N (none). + +*databits*='num':: +Set the data bits of the serial connection. The default databits is 8. + +*stopbits*='num':: +Set the stop bits of the serial connection. The default stopbits is 1. + +*slaveid*='num':: +Set the Modbus slave id. The default slave id is 1. + +*response_timeout_ms*='num':: +Set the Modbus response timeout. The default timeout is set by libmodbus. It can +be good to set a higher timeout on TCP connections with high latency. + +AUTHORS +------- + +* Axel Gembe + +SEE ALSO +-------- + +The core driver +~~~~~~~~~~~~~~~~ + +linkman:nutupsdrv[8], linkman:ups.conf[5] + +Internet resources +~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/apcsmart-old.txt b/docs/man/apcsmart-old.txt index f2c8f4ae51..200dffe351 100644 --- a/docs/man/apcsmart-old.txt +++ b/docs/man/apcsmart-old.txt @@ -40,7 +40,7 @@ definition described below. If your 940-0024C cable is broken or missing, use this diagram to build a clone: -http://www.networkupstools.org/cables/940-0024C.jpg +https://www.networkupstools.org/cables/940-0024C.jpg EXTRA ARGUMENTS --------------- @@ -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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/apcsmart.txt b/docs/man/apcsmart.txt index ff20bf9a10..09ba08e011 100644 --- a/docs/man/apcsmart.txt +++ b/docs/man/apcsmart.txt @@ -21,20 +21,22 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -The apcsmart driver should recognize (or at the very least, work with) the -majority of Smart-UPS models - which includes Smart-UPS, Matrix-UPS and Back-UPS -lineups, among few other ones. +The apcsmart driver should recognize (or at the very least, work with) +the majority of Smart-UPS models -- which includes Smart-UPS, Matrix-UPS +and Back-UPS lineups, among few other ones. -Currently, we can roughly divide APC hardware into four groups (note that the -division isn't strict by any means, and the borders between those are pretty fuzzy): +Currently, we can roughly divide APC hardware into four groups (note +that the division isn't strict by any means, and the borders between +those are pretty fuzzy): [very] "old" models:: - These models usually have old APC logo, white color and _no_ programmable - EEPROM; you won't find them listed anywhere on APC's site either. The support - for those will be usually based on driver's compatibility tables, or if the - model (firmware) is not listed in those - the driver will try to follow the very - basic subset of features, while still trying to remain useful. Despite - "smart" tagname, they often tend to behave in pretty dumb way (see the + These models usually have old APC logo, white color and _no_ + programmable EEPROM; you won't find them listed anywhere on APC's + site either. The support for those will be usually based on driver's + compatibility tables, or if the model (firmware) is not listed in + those -- the driver will try to follow the very basic subset of + features, while still trying to remain useful. Despite "smart" + tagname, they often tend to behave in pretty dumb way (see the section below about shutdown behaviour). + -- @@ -44,27 +46,33 @@ division isn't strict by any means, and the borders between those are pretty fuz -- "new" models:: - These models usually come from late 1990s / pre-2009 times. They are often - referred as "3rd. gen". For the most part, they have programmable EEPROM, - report supported commands and capabilities, and should work just fine with the - apcsmart driver. + These models usually come from late 1990s / pre-2009 times. + They are often referred to as "3rd. gen". For the most part, they + have programmable EEPROM, report supported commands and capabilities, + and should work just fine with the apcsmart driver. "microlink" models:: - WARNING: these are not _natively_ supported by *apcsmart* (or *apcupsd*, - for that matter, if you're wondering). Around 2007, APC (now APC Schneider) - decided to go back to its proprietary roots, and all the new models (SMT, - SMX, SURTD) use completely different protocol and cables. If you purchased - a new APC UPS - that uses cable with RJ45 on the one end, and DB-9 on the - other - then you have such model. Your only option to support it through - *NUT* is to purchase a "legacy communications card" - part #AP9620 (google - 'AP9620' for more details). Or if that's not an option, rely on official - software. + WARNING: these are not _natively_ supported by *apcsmart* (or as of + this writing by *apcupsd*, for that matter, if you're wondering). + Around 2007, APC (now APC Schneider) decided to go back to its + proprietary roots, and all the new models (SMT, SMX, SURTD) use + completely different protocol and cables. If you purchased + a new APC UPS -- that uses cable with RJ45 on the one end, and DB-9 + on the other -- then you have such model. Your only option to support + it through *NUT* is to purchase a "legacy communications card" -- + part #AP9620 (google 'AP9620' for more details). Or if that's not + an option, rely on official software. + + + UPDATE: later releases of *apcupsd* claimed support for new APC + protocols, so it is worth checking if *apcupsd* software would + work with your device, and *apcupsd-ups* NUT driver would handle + it as part of NUT-managed ecosystem. Microsol models:: - Several Microsol serial models sold in Brazil have been rebranded as APC - Back-UPS, and the model numbers tend to start with "BZ". If you have one - of these "Nobreaks", they will not work with the *apcsmart* driver - please - see the linkman:solis[8] driver instead. + Several Microsol serial models sold in Brazil have been rebranded + as APC Back-UPS, and the model numbers tend to start with "BZ". + If you have one of these "Nobreaks", they will not work with the + *apcsmart* driver -- please see the linkman:solis[8] driver instead. + -- .Example models: @@ -72,9 +80,9 @@ Microsol models:: * Back-UPS BZ2200BI-BR -- -Another thing to remember is that Smart protocol is not USB protocol. If you -have UPS with both USB and serial ports, then depending on how you connect it, -you will need either apcsmart or usbhid-ups driver. +Another thing to remember is that Smart protocol is not USB protocol. +If you have UPS with both USB and serial ports, then depending on how +you connect it, you will need either apcsmart or usbhid-ups driver. CABLING ------- @@ -86,7 +94,7 @@ definition described below. If your 940-xx24X cable is broken or missing, use this diagram to build a clone: -http://www.networkupstools.org/cables.html#_940_0024c_clone +https://www.networkupstools.org/cables.html#_940_0024c_clone NOTE: The "xx" is either "00" for a short cable, or the number of feet of a longer cable. The "X" is a letter representing the minor revision @@ -105,10 +113,11 @@ Alternatively, you can also provide it on the command line using: TTY MODES --------- -By default the driver works in canonical mode, but it proved to be a problem in -Windows systems. Furthermore there's a possibility of some obscure serial cards -or serial-USB converters that could cause problems as well. You can use -'ttymode=' option to force non-canonical discipline in linkman:ups.conf[5]: +By default the driver works in canonical mode, but it proved to be a +problem in Windows systems. Furthermore there's a possibility of some +obscure serial cards or serial-USB converters that could cause problems +as well. You can use 'ttymode=' option to force non-canonical discipline +in linkman:ups.conf[5]: *ttymode*=raw @@ -121,8 +130,8 @@ NOTE: Any other value will make the driver work in the canonical mode. EXPLANATION OF SHUTDOWN METHODS SUPPORTED BY APC UPSES ------------------------------------------------------ -APC hardware supports a lot of shutdown methods, that themselves can differ in -behaviour quite a bit, depending on the model. +APC hardware supports a lot of shutdown methods, that themselves can +differ in behaviour quite a bit, depending on the model. *S* (soft hibernate):: This is most basic command present in probably all APC models. It will @@ -131,26 +140,27 @@ behaviour quite a bit, depending on the model. + -- "old" models: :: - The behaviour here is unfortunately pretty primitive - when the power - returns, the UPS just wakes up. No grace periods, no min. battery - charge condition, etc. This is probably not what you want. + The behaviour here is unfortunately pretty primitive - + when the power returns, the UPS just wakes up. No grace periods, + no min. battery charge condition, etc. This is probably not what + you want. "new" models: :: - The behaviour here is as expected - the power is cut off after the + The behaviour here is as expected -- the power is cut off after the EEPROM defined grace period. The UPS will wake up when the power - returns, after the EEPROM defined delay AND if the EEPROM defined min. - battery charge level is met. The delay is counted from the power's - return. + returns, after the EEPROM defined delay AND if the EEPROM defined + min. battery charge level is met. The delay is counted from the + power's return. -- + *CS* (aka "force OB hack"):: This is a trick to make UPS power down even if it's running on mains. - Immediately before issuing *S*, "simulate power failure" is issued. The - remaining behaviour is as in *S* case. + Immediately before issuing *S*, "simulate power failure" is issued. + The remaining behaviour is as in *S* case. + -There's a delay between "simulate power failure" and *S* - by default set to -3.5s. You can control it through *cshdelay* option (allowed values are from 0 -to 9.9). +There's a delay between "simulate power failure" and *S* -- by default +set to 3.5s. You can control it through *cshdelay* option (allowed +values are from 0 to 9.9). + The name came from APC CS models, where such trick was used to power down UPSes in consistent fashion using only *S*. It's better to use *@nnn* @@ -158,17 +168,20 @@ command if your UPS supports it (and is not too old, see below). *@nnn* (hard hibernate):: This is basic command used to hibernate UPS regardless if it's - running on batteries or on mains. The option takes 3 digits argument which - can be used to specify additional wake-up delay (in 6 minute units). + running on batteries or on mains. The option takes 3 digits argument + which can be used to specify additional wake-up delay (in 6 minute + units). + -- "old" models: :: - The behaviour is - unfortunately - similarly primitive to *S*. The UPS - unconditionally wakes up after $$nnn*6$$ minutes - *it doesn't care if the - power returned !* If nnn = 000, then UPS will do precisely nothing. On - those models you're better specifying nnn > 0, if you can estimate - the kind of power problems that might be happening in your environment. - Another thing to consider with "old" models - you might lose the + The behaviour is -- unfortunately -- similarly primitive to *S*. + The UPS unconditionally wakes up after $$nnn*6$$ minutes: + *it doesn't care if the power returned !* + If nnn = 000, then UPS will do precisely nothing. + On those models you're better specifying nnn > 0, if you can + estimate the kind of power problems that might be happening + in your environment. + Another thing to consider with "old" models -- you might lose the connection with the UPS, until it wakes up (with *S*, the serial connection is kept alive). @@ -176,8 +189,8 @@ command if your UPS supports it (and is not too old, see below). All the usual variables defined in EEPROM are respected (see *S*). Additionally, if nnn > 0, the $$nnn*6$$ minutes are added to EEPROM defined delay. UPS will not power up if it's running on batteries, - contrary to what "old" models used to do - the combined delay is counted - from the moment of power return. + contrary to what "old" models used to do -- the combined delay is + counted from the moment of power return. -- + Supposedly there exist models that take 2 digits instead of 3. Just in case, @@ -185,11 +198,11 @@ NUT also supports such variation. You have to provide exactly 2 digits to trigger it (*awd* option, or argument to one of the supported instant commands). *K* (delayed poweroff):: - This is permanent poweroff - the UPS will not wake up automatically. On - newer units, it will respect applicable EEPROM variables. + This is permanent poweroff -- the UPS will not wake up automatically. + On newer units, it will respect applicable EEPROM variables. *Z* (instant poweroff):: - This is also permanent poweroff - the UPS will not wake up automatically. + This is also permanent poweroff -- the UPS will not wake up automatically. The poweroff is executed immediately. SHUTDOWN CONTROL BY NUT @@ -202,35 +215,36 @@ There are three options used to control the shutdown behaviour. details. *advorder*=no|[0-4]+:: - This option takes string of digits as an argument. Methods listed are tried - in turn until one of them succeeds. Note that the meaning of digits is - different from *sdtype*. See below for details. + This option takes string of digits as an argument. Methods listed + are tried in turn until one of them succeeds. Note that the meaning + of digits is different from *sdtype*. See below for details. *awd*=[0-9]{1,3}:: - This option lets you specify additional wake-up delay used by *@*. If you - provide exactly 2 digits, the driver will try 2 digits variation (see - previous section for more info). Otherwise standard 3 digits variation is - used. *Note: the time unit is 6 minutes !* + This option lets you specify additional wake-up delay used by *@*. + If you provide exactly 2 digits, the driver will try 2 digits + variation (see previous section for more info). + Otherwise standard 3 digits variation is used. + *Note: the time unit is 6 minutes !* -Keep in mind that *sdtype* and *advorder* are mutually exclusive. If *advorder* -is provided, *sdtype* is ignored. If *advorder* is set to 'no', *sdtype* is -used instead. +Keep in mind that *sdtype* and *advorder* are mutually exclusive. +If *advorder* is provided, *sdtype* is ignored. If *advorder* is +set to 'no', *sdtype* is used instead. -If nothing is provided, *NUT* will assume *sdtype*=0 - which is generally fine -for anything not too ancient or not too quirky. +If nothing is provided, *NUT* will assume *sdtype*=0 -- which is +generally fine for anything not too ancient or not too quirky. SDTYPE ~~~~~~ -The values permitted are from 0 to 5. Only one can be specified. Anything else -will cause apcsmart to exit. +The values permitted are from 0 to 5. Only one can be specified. +Anything else will cause apcsmart to exit. 0:: -issue soft hibernate (*S*) if the UPS is running on batteries, otherwise issue -hard hibernate (*@*) +issue soft hibernate (*S*) if the UPS is running on batteries, +otherwise issue hard hibernate (*@*) 1:: -issue soft hibernate (*S*) (if on batteries), and if it fails (or on mains) - -try hard hibernate (*@*) +issue soft hibernate (*S*) (if on batteries), and if it fails +(or on mains) -- try hard hibernate (*@*) 2:: issue instant poweroff (*Z*) 3:: @@ -245,7 +259,7 @@ NOTE: Hard hibernate's additional wake-up delay can be provided by *awd*. ADVORDER ~~~~~~~~ -The argument is either a word 'no', or a string of 1 - 5 digits in [0 - 4] +The argument is either a word 'no', or a string of 1..5 digits in [0..4] range. Each digit maps to the one of shutdown methods supported by APC UPSes. Methods listed in this way are tried in order, until one of them succeeds. @@ -265,10 +279,10 @@ NOTE: Hard hibernate's additional wake-up delay can be provided by *awd*. IGNORING LB STATE ----------------- -APC units - even if they report LB mode - will not go into shutdown +APC units -- even if they report LB mode -- will not go into shutdown automatically. This gives us even more control with reference to "when to -actually shutdown PSU". Since version 2.6.2, NUT supports *ignorelb* option in -driver's section of linkman:ups.conf[5]. When such option is in effect, +actually shutdown PSU". Since version 2.6.2, NUT supports *ignorelb* option +in driver's section of linkman:ups.conf[5]. When such option is in effect, the core driver will ignore LB state as reported by specific driver and start shutdown basing the decision _only_ on two conditions: @@ -278,21 +292,22 @@ battery.charge < battery.charge.low battery.runtime < battery.runtime.low -Of course - if any of the variables are not available, the appropriate condition -is not checked. If you want to explicitly disable one of the conditions, simply -override the right hand variable causing the condition to always evaluate to -false (you can even provide negative numbers). +Of course -- if any of the variables are not available, the appropriate +condition is not checked. If you want to explicitly disable one of the +conditions, simply override the right hand variable causing the condition +to always evaluate to false (you can even provide negative numbers). -APC UPSes don't have battery.charge.low - you will have to define it if you want -to use such condition (prefix the variable with `override.` or `default.`). +APC UPSes don't have battery.charge.low -- you will have to define it +if you want to use such condition (prefix the variable with `override.` +or `default.`). -"New" units have battery.runtime.low, but depending on battery quality, firmware -version, calibration and UPS load - this variable can be underestimated quite a bit - -especially right after going into OB state. This in turn can cause LB to be -asserted, which under normal conditions will cause *NUT* to initiate the -shutdown. You might want to disable this condition entirely, when relying on -*ignorelb* option (this was actually the main motivation behind introduction of -such feature). +"New" units have battery.runtime.low, but depending on battery quality, +firmware version, calibration and UPS load -- this variable can be +underestimated quite a bit -- especially right after going into OB state. +This in turn can cause LB to be asserted, which under normal conditions +will cause *NUT* to initiate the shutdown. You might want to disable +this condition entirely, when relying on *ignorelb* option (this was +actually the main motivation behind introduction of such feature). Simple example: ---- @@ -302,17 +317,17 @@ Simple example: override.battery.runtime.low = -1 ---- -This would cause apcsmart to go into shutdown _only_ if detected battery charge -< 15%. Runtime condition is always false in this example. +This would cause apcsmart to go into shutdown _only_ if detected +battery charge < 15%. Runtime condition is always false in this example. -You could ask - why bother ? Well, the reason is already hinted above. APC units -can be very picky about the batteries, and their firmware can underestimate the -remaining runtime (especially right after going into OB state). *ignorelb* -option and *$$override.*$$* let you remain in control of the UPS, not UPS in control -of you. +You could ask -- why bother ? Well, the reason is already hinted above. +APC units can be very picky about the batteries, and their firmware can +underestimate the remaining runtime (especially right after going into +OB state). *ignorelb* option and *$$override.*$$* let you remain in +control of the UPS, not UPS in control of you. -Furthermore, this allows to specify conditions similarly to how it's done in -apcupsd daemon, so it should be welcome by people used to that software. +Furthermore, this allows to specify conditions similarly to how it's done +in apcupsd daemon, so it should be welcome by people used to that software. SUPPORTED INSTANT COMMANDS @@ -322,20 +337,24 @@ The apcsmart driver exposes following instant commands: shutdown.return:: executes soft hibernate + shutdown.return cs:: executes "force OB hack" + shutdown.return at::: -executes "hard hibernate" with $$*6$$ minutes additional wake-up delay ( format -is the same as of *awd* option) +executes "hard hibernate" with $$*6$$ minutes additional wake-up +delay ( format is the same as of *awd* option) + shutdown.stayoff:: executes "delayed poweroff" + load.off:: executes "instant poweroff" -All the above commands must be issued 2nd time to have any effect (no less than 3 -seconds, and no more than 15 seconds after the initial call). Those commands are -mostly useful for manual testing, when your machine is not powered by the UPS -you're testing. +All the above commands must be issued 2nd time to have any effect +(no less than 3 seconds, and no more than 15 seconds after the initial +call). Those commands are mostly useful for manual testing, when your +machine is not powered by the UPS you're testing. Other supported commands: @@ -352,10 +371,11 @@ Other supported commands: PREVIOUS DRIVER VERSION ----------------------- -Previous driver is still available as *apcsmart-old*, should there be any need to -use earlier version (bugs, incompatibilities with new functionality, etc.). In -due time, *apcsmart-old* will be phased out completely, but this won't happen until -the new version gets solid exposure with no pending issues. +Previous driver is still available as *apcsmart-old*, should there be +any need to use earlier version (bugs, incompatibilities with new +functionality, etc.). In due time, *apcsmart-old* will be phased out +completely, but this won't happen until the new version gets solid +exposure with no pending issues. BUGS ---- @@ -367,14 +387,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 +408,7 @@ linkman:solis[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://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..710db8e60f 100644 --- a/docs/man/apcupsd-ups.txt +++ b/docs/man/apcupsd-ups.txt @@ -3,17 +3,24 @@ APCUPSD-UPS(8) NAME ---- + apcupsd-ups - Driver for apcupsd client access -NOTE ----- -This man page only documents the specific features of the +SYNOPSIS +-------- + +*apcupsd-ups* -h + +*apcupsd-ups* -a 'UPS_NAME' ['OPTIONS'] + +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 +44,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,13 +94,16 @@ 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* -exposes the UPS to the network). + +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 it is *apcupsd* exposes the UPS to the network and to this NUT +driver as its client). AUTHOR ------ + Andreas Steinmetz SEE ALSO @@ -102,6 +114,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: https://www.networkupstools.org/ +* The apcupsd home page: http://www.apcupsd.org/ diff --git a/docs/man/asciidoc.conf b/docs/man/asciidoc.conf index 9b4533d497..a2761d0db0 100644 --- a/docs/man/asciidoc.conf +++ b/docs/man/asciidoc.conf @@ -1,15 +1,20 @@ ## Borrowed from 'linkgit' in the Git distribution. -## linkman: macro +## linkman, linkman2 macros # # Usage: linkman:command[manpage-section] +# Usage: linkman2:command-page[displayed-command,manpage-section] # -# Note, {0} is the manpage section, while {target} is the command. +# Note: +# - in linkman, {0} is the manpage section, while {target} is the command. +# - in linkman2, {0} is the whole list of attributes, {1} is the command to be +# shown, {2} is the manpage section, while {target} is the command page. # # Show NUT link as: (
); if section is defined, else just show # the command. [macros] (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= +(?su)[\\]?(?Plinkman2):(?P\S*?)\[(?P.*?)\]= ifdef::backend-docbook[] [linkman-inlinemacro] @@ -17,11 +22,18 @@ ifdef::backend-docbook[] {0#} {0#{target}{0}} {0#} +[linkman2-inlinemacro] +{2%{1={target}}} +{2#} +{2#{1={target}}{2}} +{2#} endif::backend-docbook[] ifdef::backend-xhtml11[] [linkman-inlinemacro] {target}{0?({0})} +[linkman2-inlinemacro] +{1={target}}{2?({2})} # Override HTML footer, to include NUT version [footer-text] diff --git a/docs/man/asem.txt b/docs/man/asem.txt index 5501275ad0..ccafdcda71 100644 --- a/docs/man/asem.txt +++ b/docs/man/asem.txt @@ -3,24 +3,31 @@ ASEM(8) NAME ---- + asem - driver for UPS in ASEM PB1300 -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*asem* -h + +*asem* -a 'UPS_NAME' ['OPTIONS'] + +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. + +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. Seems that there are two versions of the charger. Older one is based on Max1667, newer one is a custom solution. Both are on I2C address 0x09. -To be compatible with both versions, the driver just reads bit 15 of address -0x13 which yields online/on battery status. +To be compatible with both versions, the driver just reads bit 15 of +address 0x13 which yields online/on battery status. Battery monitor is a BQ2060 at address 0x0B. EXTRA ARGUMENTS @@ -41,13 +48,14 @@ 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. -Beware that the SystemIO memory used by the I2C controller is reserved by ACPI. -If only a native I2C driver (e.g. i2c_i801, as of 3.5.X Linux kernels) is -available, then you'll need to relax the ACPI resources check. For example, you -can boot with the `acpi_enforce_resources=lax` option. +Beware that the SystemIO memory used by the I2C controller is reserved +by ACPI. If only a native I2C driver (e.g. i2c_i801, as of 3.5.X Linux +kernels) is available, then you'll need to relax the ACPI resources check. +For example, you can boot with the `acpi_enforce_resources=lax` option. ////////////////////////////////////////// Optional: use DIAGNOSTICS to describe troubleshooting techniques that are @@ -60,11 +68,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 -------- +The driver shutdown function is not implemented, so other arrangements +must be made to turn off the UPS. + +AUTHOR +------ + Giuseppe Corbelli SEE ALSO @@ -72,12 +82,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: https://www.networkupstools.org/ diff --git a/docs/man/bcmxcp.txt b/docs/man/bcmxcp.txt index 6ad8555432..cfae0ebe76 100644 --- a/docs/man/bcmxcp.txt +++ b/docs/man/bcmxcp.txt @@ -6,21 +6,28 @@ NAME bcmxcp - Driver for UPSes supporting the serial BCM/XCP protocol -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*bcmxcp* -h + +*bcmxcp* -a 'UPS_NAME' ['OPTIONS'] + +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 -documentation. + +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 documentation. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]. @@ -29,18 +36,21 @@ The number of seconds that the UPS should wait between receiving the shutdown command (`upsdrvctl shutdown`) and actually shutting off. *baud_rate=*'rate':: -Communication speed for the UPS. If this is set to 9600, it tries to connect -to the UPS at 9600bps. If it fails to communicate, it will go into baud-hunting. -It starts at 1200 and goes up to 19200. If it succeeds, it tell you the speed it -connected with. If not included in the config, it defaults to baud-hunting. +Communication speed for the UPS. If this is set to 9600, it tries to +connect to the UPS at 9600bps. If it fails to communicate, it will go +into baud-hunting. It starts at 1200 and goes up to 19200. +If it succeeds, it tell you the speed it 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 +77,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/bcmxcp_usb.txt b/docs/man/bcmxcp_usb.txt index 25bb5cd1ed..bf980a416e 100644 --- a/docs/man/bcmxcp_usb.txt +++ b/docs/man/bcmxcp_usb.txt @@ -3,20 +3,30 @@ 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 - Experimental driver for UPSes supporting the BCM/XCP protocol +over USB + +SYNOPSIS +-------- + +*bcmxcp_usb* -h + +*bcmxcp_usb* -a 'UPS_NAME' ['OPTIONS'] + +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]. -This driver is a variant of the serial driver bcmxcp and uses the same core code. + +NOTE: This driver is a variant of the serial driver linkman:bcmxcp[8] 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. +via USB. It has been developed and tested on Powerware PW3501 hardware. +It also has been tested on PW5110 hardware. EXTRA ARGUMENTS --------------- @@ -24,16 +34,33 @@ EXTRA ARGUMENTS This driver supports the following optional settings in the linkman:ups.conf[5]. -*shutdown_delay=*'delay':: +*shutdown_delay =* 'delay':: The number of seconds that the UPS should wait between receiving the shutdown command and actually shutting off. +NOTE: This driver does not currently support USB-matching settings common +to other drivers, such as *vendor*, *vendorid*, *product*, *productid*, +*serial*, *device* or *bus*. + +//////// +The note above may be addressed by +https://github.com/networkupstools/nut/issues/1764 +When that happens, replace it by the following lines: + +USB INTERFACE ONLY +~~~~~~~~~~~~~~~~~~ + +include::nut_usb_addvars.txt[] +//////// + DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + *shutdown_delay =*'120' INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands: *shutdown.return*:: @@ -56,6 +83,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 +91,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 +103,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 +113,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 +121,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/belkin.txt b/docs/man/belkin.txt index 51760d9907..d12c90ab68 100644 --- a/docs/man/belkin.txt +++ b/docs/man/belkin.txt @@ -6,15 +6,20 @@ NAME belkin - Driver for Belkin serial UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*belkin* -h + +*belkin* -a 'UPS_NAME' ['OPTIONS'] -This man page only documents the hardware-specific features of the +NOTE: This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see 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 +51,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 +64,5 @@ linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/belkinunv.txt b/docs/man/belkinunv.txt index 81517fbd5c..1723acc197 100644 --- a/docs/man/belkinunv.txt +++ b/docs/man/belkinunv.txt @@ -6,18 +6,24 @@ NAME belkinunv - Driver for Belkin "Universal UPS" and compatible -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*belkinunv* -h + +*belkinunv* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see linkman:nutupsdrv[8]. -This driver only supports serial connections. If your UPS has a USB port, -please consult the Hardware Compatibility List (HCL) to see which of the USB -drivers you should use. +NOTE: This driver only supports serial connections. If your UPS has a USB +port, please consult the Hardware Compatibility List (HCL) to see which of +the USB 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 +38,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 @@ -58,28 +65,29 @@ reboot properly when the power returns. In either case, a deadlock is avoided. In addition, if an optional integer argument is given to the *-x wait* -option, this causes *belkinunv* to wait not only for AC power to be present, -but also for the battery charge to reach the given level. I use this as part of -my startup scripts, to ensure that the batteries are sufficiently charged -before the computer continues booting. This should be put very early in the -startup script, before any filesystems are mounted read/write, and before any -filesystem checks are performed. +option, this causes *belkinunv* to wait not only for AC power to be +present, but also for the battery charge to reach the given level. +I use this as part of my startup scripts, to ensure that the batteries +are sufficiently charged before the computer continues booting. +This should be put very early in the startup script, before any +filesystems are mounted read/write, and before any filesystem +checks are performed. Several other *-x* options are provided to fine-tune this -behavior. See the <<_options,options>> below for detailed descriptions. See the -<<_examples,examples>> -below for examples of how to use *belkinunv* in shutdown and -startup scripts. +behavior. See the <<_options,options>> below for detailed descriptions. +See the <<_examples,examples>> below for examples of how to use *belkinunv* +in shutdown and startup scripts. OPTIONS ------- + See also linkman:nutupsdrv[8] for generic options. Never use the *-k* option with this driver; it does not work properly. *-x wait*[='level']:: When this option is used, *belkinunv* does not fork into the -background, but behaves as a standalone program. It connects to the UPS and -waits until AC power is present. If 'level' is specified, it also +background, but behaves as a standalone program. It connects to the UPS +and waits until AC power is present. If 'level' is specified, it also waits until the battery charge reaches at least the given level in percent. Then, and only then, *belkinunv* exits. In addition, while *belkinunv* runs in this mode, it displays a status line @@ -126,6 +134,7 @@ line. VARIABLES --------- + *battery.charge*:: *battery.runtime*:: @@ -286,7 +295,7 @@ readable at that point. # NEAR END OF SHUTDOWN SCRIPT: # if shutdown was caused by UPS, perform Belkin UPS workaround. - if [ -f /etc/killpower ] ; then + if [ -f /etc/killpower ] || /usr/sbin/upsmon -K ; then echo "Waiting for AC power, or for UPS batteries to run out..." /usr/bin/belkinunv -x wait /dev/ttyS1 @@ -304,7 +313,7 @@ file system integrity checks are done. # NEAR BEGINNING OF STARTUP SCRIPT: # if we are recovering from a power failure, wait for the UPS to # charge to a comfortable level before writing anything to disk - if [ -f /etc/killpower ] ; then + if [ -f /etc/killpower ] || /usr/sbin/upsmon -K ; then echo "Waiting for UPS battery charge to reach 60%..." /usr/bin/belkinunv -x wait=60 -x nohang /dev/ttyS1 fi @@ -327,15 +336,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 +359,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: https://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://www.networkupstools.org/protocols/belkin-universal.html[replica + on NUT site]) diff --git a/docs/man/bestfcom.txt b/docs/man/bestfcom.txt index 5213132ec5..567bba2168 100644 --- a/docs/man/bestfcom.txt +++ b/docs/man/bestfcom.txt @@ -6,16 +6,23 @@ NAME bestfcom - Driver for Best Power Fortress/Ferrups -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*bestfcom* -h + +*bestfcom* -a 'UPS_NAME' ['OPTIONS'] + +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). +(f-command set). (For older Fortress units, see +linkman:bestfortress[8].) EXTRA ARGUMENTS --------------- @@ -25,21 +32,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/bestfortress.txt b/docs/man/bestfortress.txt index 8bc969ac94..9c4101ac92 100644 --- a/docs/man/bestfortress.txt +++ b/docs/man/bestfortress.txt @@ -6,19 +6,28 @@ NAME bestfortress - Driver for old Best Fortress UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*bestfortress* -h -This man page only documents the hardware-specific features of the +*bestfortress* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the bestfortress driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver supports old Best Fortress UPS equipment using a serial connection. + +This driver supports old Best Fortress UPS equipment using a serial +connection. + +One example is the "Fortress LI660", sold in (at least) 1995. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -28,22 +37,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/bestuferrups.txt b/docs/man/bestuferrups.txt index a84ebe4666..382e13a29a 100644 --- a/docs/man/bestuferrups.txt +++ b/docs/man/bestuferrups.txt @@ -6,14 +6,20 @@ NAME bestuferrups - Driver for Best Power Micro-Ferrups -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*bestuferrups* -h + +*bestuferrups* -a 'UPS_NAME' ['OPTIONS'] + +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 +30,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/bestups.txt b/docs/man/bestups.txt index 9d80e69d64..90f5960c2b 100644 --- a/docs/man/bestups.txt +++ b/docs/man/bestups.txt @@ -6,15 +6,33 @@ NAME bestups - Driver for Best Power / SOLA (Phoenixtec protocol) UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*bestups* -h -This man page only documents the hardware-specific features of the +*bestups* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: 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. + +If your device works with this driver, but does not work with +linkman:nutdrv_qx[8], please report this via the mailing list or issue +tracker. + 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 @@ -23,7 +41,8 @@ Best 610 is supported using the `ID' option. Other UPS hardware using the Phoenixtec protocol should also work, but they will generate a warning since their battery information is not known. -This driver does not support some older Best/SOLA units. +This driver does not support some older Best/SOLA units. (For older +Fortress units, see linkman:bestfortress[8].) EXTRA ARGUMENTS --------------- @@ -101,17 +120,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/blazer-common.txt b/docs/man/blazer-common.txt index 68d00814a9..d9d6f99096 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 ------------------ @@ -29,27 +42,27 @@ will need changing (see linkman:ups.conf[5]): Maximum battery voltage that is reached after about 12 to 24 hours charging. If you want the driver to report a guesstimated *battery.charge*, you need -to specify this (see <<_battery_charge,BATTERY CHARGE>>). +to specify this (see <<_battery_charge_guesstimation,BATTERY CHARGE GUESSTIMATION>>). *default.battery.voltage.low =* 'value':: Minimum battery voltage just before the UPS automatically shuts down. If you want the driver to report a guesstimated *battery.charge*, you need -to specify this (see <<_battery_charge,BATTERY CHARGE>>). +to specify this (see <<_battery_charge_guesstimation,BATTERY CHARGE GUESSTIMATION>>). *default.battery.voltage.nominal =* 'value':: *override.battery.voltage.nominal =* 'value':: -Some devices show a wrong nominal battery voltage (or none at all), so you may -need to override or set a default value. +Some devices show a wrong nominal battery voltage (or none at all), +so you may need to override or set a default value. *override.battery.packs =* 'value':: Some devices report a part of the total battery voltage. For instance, if *battery.voltage.nominal* is 24 V, but it reports a *battery.voltage* of around 2 V, the number of *battery.packs* to correct this reading would -be 12. The driver will attempt to detect this automatically, but if this fails -somehow, you may want to override this value. +be 12. The driver will attempt to detect this automatically, but if this +fails somehow, you may want to override this value. *ondelay =* 'value':: @@ -90,14 +103,14 @@ seconds at full load and 720 seconds at half load, you would enter The first load should always be higher than the second. If you have values available for loads other than 100 and 50 % respectively, you can use those too, but keep them spaced apart as far as reasonably possible. Just don't -get too close to no load (prediction of runtime depends more on idle load for -the battery then). +get too close to no load (prediction of runtime depends more on idle load +for the battery then). *chargetime =* 'value':: -The time needed to fully recharge the battery after being fully discharged. If -not specified, the driver defaults to 43200 seconds (12 hours). Only used if -*runtimecal* is also specified. +The time needed to fully recharge the battery after being fully discharged. +If not specified, the driver defaults to 43200 seconds (12 hours). +Only used if *runtimecal* is also specified. *idleload =* 'value':: @@ -112,10 +125,10 @@ SERIAL INTERFACE ONLY *cablepower =* 'string':: -By default the driver will set DTR and clear RTS ('normal'). If you find that -your UPS isn't detected or the communication with the UPS is unreliable, you may -try if clear DTR and set RTS ('reverse'), set DTR and RTS ('both') or -clear DTR and RTS ('none') improves this situation. +By default the driver will set DTR and clear RTS ('normal'). If you find +that your UPS isn't detected or the communication with the UPS is unreliable, +you may try if clear DTR and set RTS ('reverse'), set DTR and RTS ('both') +or clear DTR and RTS ('none') improves this situation. endif::blazer_usb[] @@ -123,44 +136,13 @@ ifdef::blazer_usb[] USB INTERFACE ONLY ~~~~~~~~~~~~~~~~~~ -*vendorid =* 'regex':: -*productid =* 'regex':: -*vendor =* 'regex':: -*product =* 'regex':: -*serial =* 'regex':: - -Select a specific UPS, in case there is more than one connected via -USB. Each option specifies an extended regular expression (see -*regex(7)*) that must match the UPS's entire vendor/product/serial -string (minus any surrounding whitespace), or the whole 4-digit -hexadecimal code for vendorid and productid. Try *-DD* for -finding out the strings to match. -+ -Examples: - - - `-x vendor="Foo.Corporation.*"` - - `-x vendorid=051d*` (APC) - - `-x product=".*(Smart|Back)-?UPS.*"` - -*bus =* 'regex':: - -Select a UPS on a specific USB bus or group of buses. The argument is -a regular expression that must match the bus name where the UPS is -connected (e.g. bus="002", bus="00[2-3]"). - -*device =* 'regex':: - -Select a UPS on a specific USB device or group of devices. The argument is -a regular expression that must match the device name where the UPS is -connected (e.g. device="001", device="00[1-2]"). -Note that device numbers are not guaranteed by the OS to be stable across -re-boots or device re-plugging. +include::nut_usb_addvars.txt[] *subdriver =* 'string':: Select a serial-over-USB subdriver to use. You have a choice between *phoenix*, -*ippon*, *cypress*, and *krauler*. When using this option, it is mandatory to also -specify the *vendorid* and *productid*. +*ippon*, *cypress*, and *krauler*. When using this option, it is mandatory +to also specify the *vendorid* and *productid*. *langid_fix =* 'value':: @@ -195,8 +177,8 @@ Turn off the load and return when power is back. Uses the timers defined by *shutdown.stayoff*:: -Turn off the load and remain off (see <<_known_problems,KNOWN PROBLEMS>>). Uses -the timer defined by *offdelay*. +Turn off the load and remain off (see <<_known_problems,KNOWN PROBLEMS>>). +Uses the timer defined by *offdelay*. *shutdown.stop*:: @@ -219,45 +201,48 @@ Perform a battery test for the duration of 'value' minutes. Stop a running battery test (not available on some hardware.) -BATTERY CHARGE --------------- +BATTERY CHARGE GUESSTIMATION +---------------------------- -Due to popular demand, this driver will report a guesstimated *battery.charge* -and optionally *battery.runtime*, provided you specified a couple of the -<<_extra_arguments,EXTRA ARGUMENTS>> listed above. +Due to popular demand, this driver will report a guesstimated +*battery.charge* value and optionally *battery.runtime*, provided you +specified a couple of the <<_extra_arguments,EXTRA ARGUMENTS>> listed above. If you specify both *battery.voltage.high* and *battery.voltage.low* in -linkman:ups.conf[5], but don't enter *runtimecal*, it will guesstimate the state -of charge by looking at the battery voltage alone. This is not reliable under load, -as this only gives reasonably accurate readings if you disconnect the load, let the -battery rest for a couple of minutes and then measure the open cell voltage. This -just isn't practical if the power went out and the UPS is providing power for your +linkman:ups.conf[5], but don't enter *runtimecal*, it will guesstimate +the state of charge by looking at the battery voltage alone. +This is not reliable under load, as this only gives reasonably accurate +readings if you disconnect the load, let the battery rest for a couple +of minutes and then measure the open cell voltage. This just isn't +practical if the power went out and the UPS is providing power for your systems. - battery.voltage - battery.voltage.low -battery.charge = ------------------------------------------ x 100 % - battery.voltage.high - battery.voltage.low - -There is a way to get better readings without disconnecting the load but this -requires one to keep track on how much (and how fast) current is going in- and -out of the battery. If you specified the *runtimecal*, the driver will attempt -to do this. Note however, that this heavily relies on the values you enter and -that the UPS must be able to report the load as well. There are quite a couple -of devices that report 0 % (or any other fixed value) at all times, in which -case this obviously doesn't work. - -The driver also has no way of determining the degradation of the battery capacity -over time, so you'll have to deal with this yourself (by adjusting the values -in *runtimecal*). Also note that the driver guesses the initial state of charge -based on the battery voltage, so this may be less than 100 %, even when you are -certain that they are full. There is just no way to reliably measure this between -0 and 100 % full charge. - -This is better than nothing (but not by much). If any of the above calculations is -giving you incorrect readings, you are the one that put in the values in -linkman:ups.conf[5], so don't complain with the author. If you need something better, -buy a UPS that reports *battery.charge* and *battery.runtime* all by itself -without the help of a NUT driver. + battery.voltage - battery.voltage.low + battery.charge = ------------------------------------------ x 100 % + battery.voltage.high - battery.voltage.low + +There is a way to get better readings without disconnecting the load, +but this requires one to keep track on how much (and how fast) current +is going in- and out of the battery. If you specified the *runtimecal*, +the driver will attempt to do this. Note however, that this heavily +relies on the values you enter and that the UPS must be able to report +the load as well. There are quite a couple of devices that report 0% +(or any other fixed value) at all times, in which case this obviously +doesn't work. + +The driver also has no way of determining the degradation of the battery +capacity over time, so you'll have to deal with this yourself (by adjusting +the values in *runtimecal*). Also note that the driver guesses the initial +state of charge based on the battery voltage, so this may be less than 100%, +even when you are certain that they are full. There is just no way to +reliably measure this between 0 and 100% full charge. + +This is better than nothing (but not by much). If any of the above +calculations are giving you incorrect readings, remember that you are +the one who put in the values in linkman:ups.conf[5], so don't complain +to the author. If you need something better, consider buy an UPS that +reports *battery.charge* and *battery.runtime* all by itself without +the help of a NUT driver. NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS @@ -266,11 +251,13 @@ NOTES FOR THE PREVIOUS USER OF MEGATEC DRIVERS The blazer drivers having replaced the megatec ones, some configuration changes may be required by users switching to blazer. -Part of this, the following megatec options, in ups.conf, have to be changed: +Part of this, the following megatec options, in ups.conf, have to be +changed: *battvolts*:: -You need to use 'default.battery.voltage.high' and 'default.battery.voltage.low' +You need to use 'default.battery.voltage.high' and +'default.battery.voltage.low' *dtr and rts*:: @@ -287,16 +274,17 @@ KNOWN PROBLEMS Some UPS commands aren't supported by all models. In most cases, the driver will send a message to the system log when the user tries to execute an -unsupported command. Unfortunately, some models don't even provide a way for -the driver to check for this, so the unsupported commands will silently +unsupported command. Unfortunately, some models don't even provide a way +for the driver to check for this, so the unsupported commands will silently fail. Both the *load.off* and *shutdown.stayoff* instant commands are meant to turn the load off indefinitely. However, some UPS models don't allow this. -Some models report a bogus value for the beeper status (will always be 'enabled' -or 'disabled'). So, the *beeper.toggle* command may appear to have no effect -in the status reported by the driver when, in fact, it is working fine. +Some models report a bogus value for the beeper status (will always be +'enabled' or 'disabled'). So, the *beeper.toggle* command may appear to +have no effect in the status reported by the driver when, in fact, it is +working fine. The temperature and load value is known to be bogus in some models. @@ -304,8 +292,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 +311,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: https://www.networkupstools.org/ +* The NUT HCL: https://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/blazer_ser.txt b/docs/man/blazer_ser.txt index 344e98ff6c..48f68c362f 100644 --- a/docs/man/blazer_ser.txt +++ b/docs/man/blazer_ser.txt @@ -7,4 +7,11 @@ NAME blazer_ser - Driver for Megatec/Q1 protocol serial based UPS equipment +SYNOPSIS +-------- + +*blazer_ser* -h + +*blazer_ser* -a 'UPS_NAME' ['OPTIONS'] + include::blazer-common.txt[] diff --git a/docs/man/blazer_usb.txt b/docs/man/blazer_usb.txt index d5015429b2..21ec6b4bf7 100644 --- a/docs/man/blazer_usb.txt +++ b/docs/man/blazer_usb.txt @@ -7,5 +7,11 @@ NAME blazer_usb - Driver for Megatec/Q1 protocol USB based UPS equipment -include::blazer-common.txt[] +SYNOPSIS +-------- + +*blazer_usb* -h +*blazer_usb* -a 'UPS_NAME' ['OPTIONS'] + +include::blazer-common.txt[] diff --git a/docs/man/clone.txt b/docs/man/clone.txt index b6be30831e..d5ee2e4a8c 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -3,22 +3,30 @@ CLONE(8) NAME ---- + clone - UPS driver clone -NOTE ----- -This man page only documents the specific features of the +SYNOPSIS +-------- + +*clone* -h + +*clone* -a 'UPS_NAME' ['OPTIONS'] + +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 +66,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 +82,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 +100,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 +111,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 +134,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index bf1ae18da8..b45de7d85d 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -3,18 +3,29 @@ DUMMY-UPS(8) NAME ---- + dummy-ups - Driver for multi-purpose UPS emulation -NOTE ----- -This man page only documents the specific features of the +SYNOPSIS +-------- + +*dummy-ups* -h + +*dummy-ups* -a 'UPS_NAME' ['OPTIONS'] + +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 +38,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 +52,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 +75,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,8 +131,8 @@ 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 -link:https://networkupstools.org/ddl/index.html[DDL (Devices Dumps Library)] +Note that the Network UPS project provides an extensive +link:https://www.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 link:https://raw.githubusercontent.com/networkupstools/nut/master/tools/nut-ddl-dump.sh[`tools/nut-ddl-dump.sh`] @@ -78,13 +144,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 +167,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 @@ -109,7 +178,7 @@ second), independently of (and/or in addition to) any `TIMER` keywords. Repeater Mode ~~~~~~~~~~~~~ -In this context, `port` in the `ups.conf` block is the name of a remote UPS, +In this context, `port` in the `ups.conf` block is the name of the target UPS, using the NUT format, i.e.: @[:] @@ -130,6 +199,11 @@ bit between data-requesting cycles (currently this delay is hardcoded to one second), so propagation of data updates available to a remote `upsd` may lag by this much. +Beware that any error encountered at repeater mode startup (e.g. when not all +target UPS to be repeated or `upsd` instances are connectable yet) will cause +*dummy-ups* driver to terminate prematurely. This behaviour can be changed by +setting the `repeater_disable_strict_start` flag, making such errors non-fatal. + INTERACTION ----------- @@ -140,7 +214,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 +227,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 +278,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/etapro.txt b/docs/man/etapro.txt index 3b827e2a3f..3100fe4b34 100644 --- a/docs/man/etapro.txt +++ b/docs/man/etapro.txt @@ -3,33 +3,45 @@ ETAPRO(8) NAME ---- + etapro - Driver for ETA UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*etapro* -h + +*etapro* -a 'UPS_NAME' ['OPTIONS'] + +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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/everups.txt b/docs/man/everups.txt index ca02d29a92..8945f19aad 100644 --- a/docs/man/everups.txt +++ b/docs/man/everups.txt @@ -3,16 +3,23 @@ EVERUPS(8) NAME ---- + everups - Driver for Ever UPS models -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*everups* -h + +*everups* -a 'UPS_NAME' ['OPTIONS'] + +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 +37,7 @@ don't sleep and force a reboot. AUTHOR ------ + Bartek Szady SEE ALSO @@ -37,8 +45,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/gamatronic.txt b/docs/man/gamatronic.txt index ac9db3fde7..29d071b9fb 100644 --- a/docs/man/gamatronic.txt +++ b/docs/man/gamatronic.txt @@ -3,16 +3,23 @@ GAMATRONIC(8) NAME ---- + gamatronic - Driver for Gamatronic UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*gamatronic* -h + +*gamatronic* -a 'UPS_NAME' ['OPTIONS'] + +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 +31,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Nadav Moskovitch SEE ALSO @@ -31,8 +39,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/generic_gpio.txt b/docs/man/generic_gpio.txt new file mode 100644 index 0000000000..5e859254f4 --- /dev/null +++ b/docs/man/generic_gpio.txt @@ -0,0 +1,127 @@ +GENERIC GPIO(8) +=============== + +NAME +---- + +generic_gpio - Driver for GPIO connected UPSes + +SYNOPSIS +-------- + +*generic_gpio* -h + +*generic_gpio* -a 'gpiochip0' ['OPTIONS'] + +NOTE: This man page only documents the specific features of the *generic_gpio* +driver. For information about the core driver, see linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +This is the driver for GPIO attached UPS devices. + +The driver has been tested against CyberPower CyberShield CSN27U12V attached to +Orange Pi Zero GPIO. + +More information about this UPS can be found here: :: +https://www.cyberpowersystems.com/resources/csn27u12v-um/ + + +EXTRA ARGUMENTS +--------------- + +This driver supports the following optional settings in the +linkman:ups.conf[5] file: + +Driver control: +~~~~~~~~~~~~~~ + +*rules*='value':: +A string consisting of sub-strings. Each sub-string describes GPIO line +states conversion formula to specific NUT state, like +`nut_state=[^]line_num[logical_operation[^]line_num]...;`. Not (^) , and (&) , or +(|)operations are supported for now. nut_state should correspond to NUT +state, line_num to GPIO line number connected to UPS open collector pin. +CyberShield CSN27U12V describes pins as: +|=== +|Battery state|State details|GPIO line +|ON BATTERY|*Low* when operating from utility line + +*Open* when operating from battery|0 +|REPLACE BATTERY|*Low* when battery is charged + +*Open* when battery fails the Self Test|1 +|BATTERY MISSING|*Low* when battery is present + +*Open* when battery is missing|6 +|LOW BATTERY|*Low* when battery is near full charge capacity + +*Open* when operating from a battery with < 20% capacity|3 +|=== +and rules might be defined as + +`rules = "OL=^0;OB=0;LB=3;RB=1;DISCHRG=0&^6;BYPASS=6;"` + +assuming battery pin connection to GPIO lines as listed in table. Expecting simple formula +to be used for each state, extra may increase state reliability and may need to be +checked on each specific UPS. + +Battery Charge: +~~~~~~~~~~~~~~ + +*default.battery.charge.low*='value':: +An integer specifying the battery charge level reported in LB case. + + +CONFIGURATION +------------- + +Here is an example of GPIO driver configuration in *ups.conf* file: +---- +[CyberPower12v] + driver = GENERIC_GPIO + port = gpiochip0 + desc = "Modem and DNS server UPS" + mfr = CyberPower + model = "CyberShield CSN27U12V" + rules = "OL=^0;OB=0;LB=3;RB=1;DISCHRG=0&^6;BYPASS=6;" + default.battery.charge.low = 20 +---- + +SHUTDOWN COMMAND +---------------- + +This driver does not support shutdown command. + +INSTALLATION +------------ + +This driver is not built by default. You can build it by installing +libgpiod and running `configure --with-gpio=yes`. + +You also need to give proper permissions on the local serial device +file (/dev/gpiochip0 for example) to allow the run-time NUT driver user +account to access it, like by adding the following rule to Linux rules.d directory: + + SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\ + chown -R nut:nut /dev/gpiochip0 && chmod -R 700 /dev/gpiochip0\ + +AUTHOR +------ + +Modris Berzonis + +SEE ALSO +-------- + +The core driver: +~~~~~~~~~~~~~~~~ + +linkman:nutupsdrv[8], linkman:ups.conf[5] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +* The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ +* libgpiod home page: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ diff --git a/docs/man/generic_modbus.txt b/docs/man/generic_modbus.txt index 3302b974c0..627474f45c 100644 --- a/docs/man/generic_modbus.txt +++ b/docs/man/generic_modbus.txt @@ -4,7 +4,8 @@ GENERIC_MODBUS(8) NAME ---- -generic_modbus - Driver for contact (direct) signal UPS devices connected via modbus remote I/O gateways +generic_modbus - Driver for contact (direct) signal UPS devices connected +via modbus remote I/O gateways SYNOPSIS -------- @@ -13,8 +14,9 @@ SYNOPSIS *generic_modbus* -a 'DEVICE_NAME' ['OPTIONS'] -NOTE: This man page only documents the specific features of the *generic_modbus* -driver. For information about the core driver, see linkman:nutupsdrv[8]. +NOTE: This man page only documents the specific features of the +*generic_modbus* driver. For information about the core driver, +see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ @@ -229,16 +231,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: https://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/genericups.txt b/docs/man/genericups.txt index b948d0e3dc..31a2b93d94 100644 --- a/docs/man/genericups.txt +++ b/docs/man/genericups.txt @@ -3,15 +3,22 @@ GENERICUPS(8) NAME ---- + genericups - Driver for contact-closure UPS equipment -NOTE ----- -This man page only documents the specific features of the genericups +SYNOPSIS +-------- + +*genericups* -h + +*genericups* -a 'UPS_NAME' ['OPTIONS'] + +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 +30,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 +98,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 +402,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] -Internet resources -~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/hosts.conf.txt b/docs/man/hosts.conf.txt index 273ae98368..1bf23da3c2 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/huawei-ups2000.txt b/docs/man/huawei-ups2000.txt index 9f5d7d473e..0cbe179a9f 100644 --- a/docs/man/huawei-ups2000.txt +++ b/docs/man/huawei-ups2000.txt @@ -1,5 +1,5 @@ HUAWEI_UPS2000(8) -================== +================= NAME ---- @@ -26,33 +26,50 @@ UPS with the following characteristics. 1. Output power: 1 kVA to 3 kVA (higher power models are unsupported). -2. Connection: USB or RS-232 (USB is only supported on Linux 5.12 and -newer kernels, read the section *Cabling* carefully). +2. Connection: USB or RS-232 (for most UPS models, USB is only supported +on Linux 5.12 and newer kernels, but there are exceptions, read the section +*Cabling* carefully). The UPS2000 series has two variants: UPS2000-A with a tower chassis, -and UPS2000-G with a rack-mount chassis. Both should be equally supported, -but more testers are needed. - -Currently, it has been tested on the following models. - -* UPS2000-A-1KTTS (firmware: V2R1C1SPC40, P1.0-D1.0) -* UPS2000-A-2KTTS (firmware: V2R1C1SPC50, P1.0-D1.0) -* UPS2000-G-3KRTS (firmware: V2R1C1SPC40, P1.0-D1.0) +and UPS2000-G with a rack-mount chassis. Within these two variants, +there are also two sub-variants: a standard runtime model powered by +an internal battery pack denoted by an "S" suffix, and a long runtime +model powered by an external battery pack denoted by an "L" suffix. + +All of these models should be equally supported, but more testers are +needed. Currently, it has been tested on the following models. + +* UPS2000-A-1KTTS (firmware: UPS2000A, V2R1C1SPC40, P1.0-D1.0) +* UPS2000-A-1KTTS (firmware: UPS2000A, V2R1C1SPC50, P1.0-D1.0) +* UPS2000-A-2KTTS (firmware: UPS2000A, V2R1C1SPC50, P1.0-D1.0) +* UPS2000-G-1KRTS (firmware: UPS2000A, V2R1C1SPC40, P1.0-D1.0) +* UPS2000-G-1KRTS (firmware: UPS2000G, V2R1C1SPC50, P1.0-D1.0) +* UPS2000-G-3KRTS (firmware: UPS2000A, V2R1C1SPC40, P1.0-D1.0) +* UPS2000-G-3KRTS (firmware: UPS2000G, V2R1C1SPC50, P1.0-D1.0) +* UPS2000-G-3KRTL (firmware: UPS2000A, V2R1C1SPC40, P1.0-D1.0) If your model is not in the list, we encourage you to 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, because the firmware only reports "UPS2000-A" -for all models, including the G series. +in your report, because many units only report themselves as "UPS2000-A" +regardless of their models, including the G series. + +As of 2022, there is also a new hardware variant with a WCH CH341 +USB-to-serial chip instead of a MaxLinear/Exar RX21V1410 chip and +reports itself as "UPS2000G". Driver support has been added since +v0.03. huawei-ups2000 uses the libmodbus project, for Modbus implementation. CABLING ------- -The UPS has a USB port and a RS-232 port. Both are supported, but USB is -only usable on Linux 5.12 and later, via the *xr_serial* kernel module (see -subsection *USB* for details). RS-232 is supported on all operating systems. +The UPS has a USB port and a RS-232 port. Both are supported, but on +most UPS models, USB is only usable on Linux 5.12 and later, via the +*xr_serial* kernel module. But for the newer hardware variant with a +WCH CH341 chip, it should have better compatibility via the *ch341* +kernel module. See subsection *USB* for details. On the other hand, +RS-232 is always supported on all operating systems. Only one port can be used at a time. When USB is used, RS-232 should be unplugged from the UPS, and vice versa. Further, optional adapter cards, @@ -68,12 +85,32 @@ screen to go black. Finally reconnect line power and restart your UPS. USB ~~~~ -The USB port on the UPS2000 is powered by a MaxLinear/Exar RX21V1410 -USB-to-serial converter chip, it's only supported by Linux 5.12 or -newer, via the *xr_serial* kernel module. +The USB port on the UPS2000 is originally powered by a MaxLinear/Exar +RX21V1410 USB-to-serial converter chip, it's only supported by Linux +5.12 or newer, via the *xr_serial* kernel module. Its *lsusb* report +is: + + 04e2:1410 Exar Corp. XR21V1410 USB-UART IC + +However, a recent hardware variant switched to the WCH CH341 serial +chip: + + 1a86:5523 QinHeng Electronics CH341 in serial mode, usb to serial port converter + +If your unit has a WCH CH341 chip (likely only found in units made +after 2022), when the UPS2000 is connected via USB, you should see +the following logs in *dmesg*. + + ch341 2-1.2:1.0: ch341-uart converter detected + usb 2-1.2: ch341-uart converter now attached to ttyUSB0 + +If so, you should be able to proceed without worrying about kernel +compatibility. This CH341 chip has been around for a decade and should be +compatible with your system. -When the UPS2000 is connected via USB to a supported Linux system, -you should see the following logs in *dmesg*. +On the other hand, if your unit has a MaxLinear/Exar XR21V1410 chip, like +most users do, when the UPS2000 is connected via USB to a supported Linux +system, you should see the following logs in *dmesg*. xr_serial 1-1.2:1.1: xr_serial converter detected usb 1-1.2: xr_serial converter now attached to ttyUSB0 @@ -86,8 +123,8 @@ necessary device driver, you will get this message instead: The generic driver *cdc_acm* is incompatible and cannot be used. You should upgrade your Linux kernel to Linux 5.12 or newer. -WARNING: On an unsupported system, the USB device can still be -recognized as a USB ACM device, but communication is impossible, +WARNING: On an unsupported system, the XR21V1410 USB device can still +be recognized as a USB ACM device, but communication is impossible, please don't waste your time on *cdc_acm*. If you're already running on Linux 5.12 or newer kernels, but still @@ -97,7 +134,8 @@ report to your Linux distro maintainers and ask them to enable *xr_serial* (kernel option `CONFIG_USB_SERIAL_XR`). When upgrading the Linux kernel isn't an option, or when you are using -another operating system (e.g. FreeBSD), RS-232 must be used. +another operating system (e.g. FreeBSD), RS-232 must be used. Even for +CH341 users, one can try this option if USB somehow refuses to work. RS-232 ~~~~~~ @@ -323,17 +361,23 @@ UPS selects the correct communication interface. Also, if you have discovered a reproducible serial port lockup problem, it can be an previously unknown bug, make sure to file a bug report. -USB is unsupported -~~~~~~~~~~~~~~~~~~~ +USB chip (MaxLinear/Exar RX21V1410) is unsupported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As previously stated, only RS-232 is supported on all systems. USB -requires a device-specific driver, which is only available on Linux +As previously stated, only RS-232 is supported on all systems. On +most UPS units, the USB chip RX21V1410 is used, and it requires a +device-specific driver *xr_serial*, which is only available on Linux 5.12 and newer kernels. On an unsupported system, the USB device can still be recognized as a USB ACM device, but in reality, communication is impossible. It can only be fixed by implementing a driver for your system, nothing can -be done within NUT. +be done within NUT. Please use the RS-232 port instead. + +Alternatively, if your unit has a WCH CH341 chip (likely only found in +units made after 2022), it should have better compatibility. + +See the previous section *Cabling* for more information. Finally, in the unlike scenario that you are using NUT on Microsoft Windows, you should be able to install the USB device driver following @@ -342,20 +386,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: https://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..9bc37ab8f7 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 @@ -114,6 +65,7 @@ Developer manual pages - linkman:libupsclient-config[1] - linkman:nut-recorder[8] - linkman:skel[8] +- linkman:sockdebug[8] [[devclient]] Client library @@ -149,7 +101,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..f8638e3b6f 100644 --- a/docs/man/isbmex.txt +++ b/docs/man/isbmex.txt @@ -6,15 +6,20 @@ NAME isbmex - Driver for ISBMEX UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*isbmex* -h + +*isbmex* -a 'UPS_NAME' ['OPTIONS'] -This man page only documents the hardware-specific features of the +NOTE: This man page only documents the hardware-specific features of the isbmex driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports SOLA/BASIC Mexico ISBMEX protocol UPS equipment. EXTRA ARGUMENTS @@ -25,6 +30,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Edscott Wilson Garcia SEE ALSO @@ -32,8 +38,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/ivtscd.txt b/docs/man/ivtscd.txt index 6a2c7c4beb..5d6da16972 100644 --- a/docs/man/ivtscd.txt +++ b/docs/man/ivtscd.txt @@ -3,32 +3,44 @@ IVTSCD(8) NAME ---- + ivtscd - driver for the IVT Solar Controller Device -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*ivtscd* -h + +*ivtscd* -a 'UPS_NAME' ['OPTIONS'] + +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/ + +The NUT (Network UPS Tools) home page: https://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..1e496c7645 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: https://www.networkupstools.org/ diff --git a/docs/man/liebert-esp2.txt b/docs/man/liebert-esp2.txt index 7b71425131..95b364878b 100644 --- a/docs/man/liebert-esp2.txt +++ b/docs/man/liebert-esp2.txt @@ -1,35 +1,47 @@ LIEBERT-ESP2(8) -============== +=============== NAME ---- liebert-esp2 - Driver for Liebert UPS, using the ESP-II serial protocol -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*liebert-esp2* -h + +*liebert-esp2* -a 'UPS_NAME' ['OPTIONS'] + +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. +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. + Use of a standard RS-232 cable with full handshaking may result in undesired operation and/or shutdown. It is therefore advisable to confirm -the proper cable/wiring with the diagram provided in the manual with your UPS. +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. -This driver currently does not support modification of configuration parameters, -such as the low battery warning time and automatic restart policy. +This driver currently does not support modification of configuration +parameters, such as the low battery warning time and automatic restart +policy. EXTRA ARGUMENTS --------------- @@ -37,19 +49,25 @@ EXTRA ARGUMENTS 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. +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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/liebert.txt b/docs/man/liebert.txt index dfaa795838..ce8bb6c9e3 100644 --- a/docs/man/liebert.txt +++ b/docs/man/liebert.txt @@ -6,14 +6,20 @@ NAME liebert - Driver for Liebert contact-closure UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*liebert* -h + +*liebert* -a 'UPS_NAME' ['OPTIONS'] + +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 +48,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/macosx-ups.txt b/docs/man/macosx-ups.txt index ee30671fd1..bd57e38b7b 100644 --- a/docs/man/macosx-ups.txt +++ b/docs/man/macosx-ups.txt @@ -3,16 +3,23 @@ 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 +SYNOPSIS +-------- + +*macosx-ups* -h + +*macosx-ups* -a 'UPS_NAME' ['OPTIONS'] + +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,10 +30,11 @@ 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, -and our traditional "don't care" value is `auto`. +parameter no longer has any effect. The rest of NUT still requires a value +here, and our traditional "don't care" value is `auto`. *model*='regex':: Likewise, if you have more than one UPS, it may be necessary to specify a @@ -37,8 +45,8 @@ DIAGNOSTICS ----------- If the driver cannot find an UPS, first open System Preferences and see if -there is an "UPS" tab on the Energy Saver panel. If so, re-run the driver with -the *-D* flag to list the names of the power sources found. +there is an "UPS" tab on the Energy Saver panel. If so, re-run the driver +with the *-D* flag to list the names of the power sources found. KNOWN ISSUES AND BUGS --------------------- @@ -60,8 +68,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 +80,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: https://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..9a2a06f50c 100644 --- a/docs/man/masterguard.txt +++ b/docs/man/masterguard.txt @@ -3,19 +3,36 @@ MASTERGUARD(8) NAME ---- + masterguard - Driver for Masterguard UPS equipment -NOTES ------ -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*masterguard* -h + +*masterguard* -a 'UPS_NAME' ['OPTIONS'] + +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 +43,7 @@ Cancel the shutdown procedure. AUTHOR ------ + Michael Spanier SEE ALSO @@ -33,12 +51,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/metasys.txt b/docs/man/metasys.txt index 77f9b36e1c..519c294700 100644 --- a/docs/man/metasys.txt +++ b/docs/man/metasys.txt @@ -6,10 +6,14 @@ NAME metasys - Driver for Meta System UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*metasys* -h -This man page only documents the hardware-specific features of the +*metasys* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the metasys driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -32,15 +36,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 +56,7 @@ UPS are really welcome. AUTHOR ------ + Fabio Di Niro SEE ALSO @@ -56,8 +64,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/mge-shut.txt b/docs/man/mge-shut.txt index 097d4cce98..d558613f2e 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/mge-utalk.txt b/docs/man/mge-utalk.txt index 049e4c7582..b287f56ba2 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/microdowell.txt b/docs/man/microdowell.txt index 9277379486..a2dc8ea32d 100644 --- a/docs/man/microdowell.txt +++ b/docs/man/microdowell.txt @@ -6,17 +6,22 @@ NAME microdowell - Driver for Microdowell Enterprise UPS series -NOTE ----- +SYNOPSIS +-------- + +*microdowell* -h + +*microdowell* -a 'UPS_NAME' ['OPTIONS'] -This man page only documents the hardware-specific features of the +NOTE: This man page only documents the hardware-specific features of the Microdowell driver. For information about the core driver, see 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 +31,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Elio Corbolante SEE ALSO @@ -33,8 +39,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/microsol-apc.txt b/docs/man/microsol-apc.txt index 9bc3756199..096602f0c6 100644 --- a/docs/man/microsol-apc.txt +++ b/docs/man/microsol-apc.txt @@ -6,15 +6,20 @@ NAME microsol-apc - Driver for APC Back-UPS BR UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*microsol-apc* -h -This man page only documents the hardware-specific features of the +*microsol-apc* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the microsol-apc driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports the following UPS models: * APC Back-UPS BZ1500-BR @@ -51,19 +56,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/netxml-ups.txt b/docs/man/netxml-ups.txt index 2517cd7a83..658168cb9b 100644 --- a/docs/man/netxml-ups.txt +++ b/docs/man/netxml-ups.txt @@ -3,18 +3,25 @@ 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 +SYNOPSIS +-------- + +*netxml-ups* -h + +*netxml-ups* -a 'UPS_NAME' ['OPTIONS'] + +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 +39,7 @@ parameter. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -77,6 +85,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 +103,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 +112,7 @@ whatever reason), the driver will no longer be able to reach the UPS. AUTHORS ------- + Arjen de Korte SEE ALSO @@ -109,8 +120,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt index 39fef960fa..51b8b6f66f 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) @@ -51,6 +52,12 @@ Update wrapping of devices into services Update wrapping of devices into services in an infinite loop; Default freq is 60 sec. +*nut-driver-enumerator.sh --daemon-after(=freq)*:: +Update wrapping of devices into services in an infinite loop; +first do one run of the loop though, then daemonize (this way +service unit is deemed started only when NUT config and driver +instances are in sync). Default freq is 60 sec. + *nut-driver-enumerator.sh --reconfigure*:: Stop and un-register all service instances and recreate them (e.g. if new dependency template was defined in a new @@ -133,10 +140,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nut-ipmipsu.txt b/docs/man/nut-ipmipsu.txt index b5f3ba40e6..8cda9c28ea 100644 --- a/docs/man/nut-ipmipsu.txt +++ b/docs/man/nut-ipmipsu.txt @@ -70,8 +70,10 @@ This driver will report various information related to a PSU, including: - status of the PSU: * 'OL' means that the PSU is present and providing power, - * 'OFF' means that the PSU is present but not providing power (power cable removed), - * 'stale' (no data) means that the PSU is not present (ie physically removed). + * 'OFF' means that the PSU is present but not providing power + (power cable removed), + * 'stale' (no data) means that the PSU is not present (i.e. + physically removed). Here is an example output for a Dell r610 server: @@ -100,16 +102,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: https://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..2758844d6d 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/ +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index 1a8796d096..dca9b946e3 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'] @@ -18,6 +19,10 @@ DESCRIPTION *nut-scanner* scans available communication buses and displays any NUT-compatible devices it has found. +*nut-scanner* can also display the detected devices in various formats, +including ups.conf, and ensures that the generated devices name are unique +across buses. + INSTALLATION ------------ @@ -29,12 +34,17 @@ both during compilation and runtime, then SNMP discovery will be available. OPTIONS ------- + *-h*:: Display the help text. DISPLAY OPTIONS --------------- +*-Q* | *--disp_nut_conf_with_sanity_check*:: +Display result in the 'ups.conf' format with sanity-check warnings (if any) +as comments (default). + *-N* | *--disp_nut_conf*:: Display result in the 'ups.conf' format. @@ -49,21 +59,40 @@ Scan all available communication buses (default behavior) *-U* | *--usb_scan*:: List all NUT-compatible USB devices currently plugged in. ++ +This option can be specified several times, for more hardware link-specific +details; these can be counter-productive in case of USB enumeration changes +over time: ++ +| `-U` | do not report any bus/device/busport details | +| `-UU` | report bus and busport, if available | +| `-UUU` | report bus/device/busport details | +| `-UUUU` | report bus/device/busport details, and bcdDevice (limited use and benefit) | ++ +NOTE: For reliability, it is preferable to match just by vendor and product +identification, and a serial number if available and unique. *-S* | *--snmp_scan*:: -Scan SNMP devices. Requires at least a 'start IP', and optionally, an 'end IP'. See specific SNMP OPTIONS for community and security settings. +Scan SNMP devices. Requires at least a 'start IP', and optionally, +an 'end IP'. See specific SNMP OPTIONS for community and security settings. *-M* | *--xml_scan*:: -Scan XML/HTTP devices. Broadcast a network message on the current network interfaces to retrieve XML/HTTP capable devices. No IP required. +Scan XML/HTTP devices. Can broadcast a network message on the current network +interfaces to retrieve XML/HTTP capable devices. No IP required in this mode. *-O* | *--oldnut_scan*:: Scan NUT devices (i.e. upsd daemon) on IP ranging from 'start IP' to 'end IP'. +*-n* | *--nut_simulation_scan*:: +Scan NUT simulated devices (.dev files in $CONFPATH). + *-A* | *--avahi_scan*:: -Scan NUT servers using Avahi request on the current network interfaces. No IP required. +Scan NUT servers using Avahi request on the current network interfaces. +No IP required. *-I* | *--ipmi_scan*:: -Scan NUT compatible power supplies available via IPMI on the current host, or over the network. +Scan NUT compatible power supplies available via IPMI on the current host, +or over the network. *-E* | *--eaton_serial* 'serial ports':: Scan Eaton devices (XCP and SHUT) available via serial bus on the current host. @@ -78,7 +107,6 @@ ports using 'X-Y', where X and Y are characters referring to the port number. - a single port name. - a list of ports name, coma separated, like '/dev/ttyS1,/dev/ttyS4'. - NETWORK OPTIONS --------------- @@ -86,10 +114,14 @@ NETWORK OPTIONS Set the network timeout in seconds. Default timeout is 5 seconds. *-s* | *--start_ip* 'start IP':: -Set the first IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut). +Set the first IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut) +or optional (XML/HTTP). *-e* | *--end_ip* 'end IP':: -Set the last IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut). If this parameter is omitted, only the 'start IP' is scanned. If 'end IP' is less than 'start IP', both parameters are internally permuted. +Set the last IP (IPv4 or IPv6) when a range of IP is required (SNMP, old_nut) +or optional (XML/HTTP). +If this parameter is omitted, only the 'start IP' is scanned. If 'end IP' is +less than 'start IP', both parameters are internally permuted. *-m* | *--mask_cidr* 'IP address/mask':: Set a range of IP using CIDR notation. @@ -110,43 +142,63 @@ SNMP V3 OPTIONS --------------- *-l* | *--secLevel* 'security level':: -Set the 'security level' used for SNMPv3 messages. Allowed values are: noAuthNoPriv, authNoPriv and authPriv. +Set the 'security level' used for SNMPv3 messages. +Allowed values are: noAuthNoPriv, authNoPriv and authPriv. +This parameter is mandatory if you use non-trivial authentication. *-u* | *--secName* 'security name':: -Set the 'security name' used for authenticated SNMPv3 messages. This parameter is mandatory if you set 'security level'. +Set the 'security name' used for authenticated SNMPv3 messages. +This parameter is mandatory if you set 'security level'. *-w* | *--authProtocol* 'authentication protocol':: -Set the 'authentication protocol' used for authenticated SNMPv3 messages. Allowed values are MD5, SHA, SHA256, SHA384 or SHA512 (depending on Net-SNMP library capabilities; check help of the `nut-scanner` binary program for the run-time supported list). Default value is MD5. +Set the 'authentication protocol' used for authenticated SNMPv3 messages. +Allowed values are MD5, SHA, SHA256, SHA384 or SHA512 (depending on +Net-SNMP library capabilities; check help of the `nut-scanner` binary +program for the run-time supported list). Default value is MD5. *-W* | *--authPassword* 'authentication pass phrase':: -Set the 'authentication pass phrase' used for authenticated SNMPv3 messages. This parameter is mandatory if you set 'security level' to authNoPriv or authPriv. +Set the 'authentication pass phrase' used for authenticated SNMPv3 messages. +This parameter is mandatory if you set 'security level' to authNoPriv +or authPriv. *-x* | *--privProtocol* 'privacy protocol':: -Set the 'privacy protocol' used for encrypted SNMPv3 messages. Allowed values are DES, AES, AES192 or AES256 (depending on Net-SNMP library capabilities; check help of the `nut-scanner` binary program for the run-time supported list). Default value is DES. +Set the 'privacy protocol' used for encrypted SNMPv3 messages. +Allowed values are DES, AES, AES192 or AES256 (depending on Net-SNMP +library capabilities; check help of the `nut-scanner` binary program +for the run-time supported list). Default value is DES. *-X* | *--privPassword* 'privacy pass phrase':: -Set the 'privacy pass phrase' used for encrypted SNMPv3 messages. This parameter is mandatory if you set 'security level' to authPriv. +Set the 'privacy pass phrase' used for encrypted SNMPv3 messages. +This parameter is mandatory if you set 'security level' to authPriv. IPMI OPTIONS ------------ *-b* | *--username* 'username':: -Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN. No default). +Set the username used for authenticating IPMI over LAN connections +(mandatory for IPMI over LAN. No default). *-B* | *--password* 'password':: -Specify the password to use when authenticating with the remote host (mandatory for IPMI over LAN. No default). +Specify the password to use when authenticating with the remote host +(mandatory for IPMI over LAN. No default). *-d* | *--authType* 'authentication type':: -Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5). +Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, +MD2, and MD5) with the remote host (default=MD5). This forces connection through the 'lan' IPMI interface , thus in IPMI 1.5 mode. *-L* | *--cipher_suite_id* 'cipher suite identifier':: -Specify the IPMI 2.0 cipher suite ID to use. The Cipher Suite ID identifies a set of authentication, integrity, and -confidentiality algorithms to use for IPMI 2.0 communication. The authentication algorithm identifies the algorithm -to use for session setup, the integrity algorithm identifies the algorithm to use for session packet signatures, and the -confidentiality algorithm identifies the algorithm to use for payload encryption (default=3). +Specify the IPMI 2.0 cipher suite ID to use. The Cipher Suite ID identifies +a set of authentication, integrity, and confidentiality algorithms to use +for IPMI 2.0 communication. ++ +The authentication algorithm identifies the algorithm to use for session +setup, the integrity algorithm identifies the algorithm to use for session +packet signatures, and the confidentiality algorithm identifies the algorithm +to use for payload encryption (default=3). + -The following cipher suite ids are currently supported (Authentication; Integrity; Confidentiality): +The following cipher suite ids are currently supported +(Authentication; Integrity; Confidentiality): - *0*: None; None; None - *1*: HMAC-SHA1; None; None @@ -168,8 +220,8 @@ MISCELLANEOUS OPTIONS Display NUT version. *-a* | *--available*:: -Display available buses that can be scanned, depending on how the binary has -been compiled. (e.g. OLDNUT, USB, SNMP, XML, AVAHI, IPMI). +Display available buses that can be scanned, depending on how the nut-scanner +binary program has been compiled. (e.g. OLDNUT, USB, SNMP, XML, AVAHI, IPMI). *-q* | *--quiet*:: Display only scan result. No information on currently scanned bus is displayed. @@ -188,39 +240,72 @@ EXAMPLES To scan USB devices only: -*nut-scanner -U* +---- +:; nut-scanner -U + +[nutdev-usb1] + driver = "snmp-ups" + port = "192.168.0.42" +---- + +To scan SNMP v1 device with public community on address range 192.168.0.0 +to 192.168.0.255: -To scan SNMP v1 device with public community on address range 192.168.0.0 to 192.168.0.255: +---- +:; nut-scanner -S -s 192.168.0.0 -e 192.168.0.255 -*nut-scanner -S -s 192.168.0.0 -e 192.168.0.255* +[nutdev-snmp1] + driver = "snmp-ups" + port = "192.168.0.42" +---- The same using CIDR notation: -*nut-scanner -S -m 192.168.0.0/24* +---- +:; nut-scanner -S -m 192.168.0.0/24 -To scan NUT servers with a timeout of 10 seconds on IP range 192.168.0.0 to 192.168.0.127 using CIDR notation: +[nutdev-snmp1] + driver = "snmp-ups" + port = "192.168.0.42" +---- -*nut-scanner -O -t 10 -m 192.168.0.0/25* +To scan NUT servers with a timeout of 10 seconds on IP range 192.168.0.0 +to 192.168.0.127 using CIDR notation: -To scan for power supplies, through IPMI (1.5 mode) over the network, on address range 192.168.0.0 to 192.168.0.255: +---- +:; nut-scanner -O -t 10 -m 192.168.0.0/25 -*nut-scanner -I -m 192.168.0.0/24 -b username -B password* +[nutdev-nut1] + driver = "dummy-ups" + port = "dummy-test@192.168.1.28" +---- -To scan for Eaton serial devices on ports 0 and 1 (/dev/ttyS0, -/dev/ttyUSB0, /dev/ttyS1 and /dev/ttyUSB1 on Linux): +To scan for power supplies, through IPMI (1.5 mode) over the network, +on address range 192.168.0.0 to 192.168.0.255 using CIDR notation: -*nut-scanner --eaton_serial 0-1* +---- +:; nut-scanner -I -m 192.168.0.0/24 -b username -B password +---- + +To scan for Eaton serial devices on ports 0 and 1 (`/dev/ttyS0`, +`/dev/ttyUSB0`, `/dev/ttyS1` and `/dev/ttyUSB1` on Linux): -To scan for Eaton serial devices on ports 1 and 2 (COM1 and COM2 on Windows): +---- +:; nut-scanner --eaton_serial 0-1 +---- -*nut-scanner --eaton_serial 1-2* +To scan for Eaton serial devices on ports 1 and 2 (`COM1` and `COM2` on Windows): + +---- +:; nut-scanner --eaton_serial 1-2 +---- SEE ALSO -------- linkman:ups.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index 8a0bf7ad68..2df3552645 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 @@ -53,6 +54,31 @@ netclient;; When only upsmon is required, possibly because there are other hosts that are more closely attached to the UPS, the MODE should be set to netclient. +*ALLOW_NO_DEVICE*:: +Optional, defaults to `false`. Set this to `true` to allow starting the `upsd` +NUT data server service even if `ups.conf` has no device sections configured +at the moment. This environment variable overrides the built-in "false" flag +value in the `upsd` program, and an optional same-named default flag that +can be set in `upsd.conf`. ++ +If you want a data server always running and responding on the network, even +if it initially has nothing to serve (may be live-reloaded later, when devices +become configured), this option is for you. + +*ALLOW_NOT_ALL_LISTENERS*:: +Optional, defaults to `false`. Set this to `true` to allow starting the `upsd` +NUT data server even if not all `LISTEN` directives can be honoured at the +moment. This environment variable overrides the built-in "false" flag in the +`upsd` program, and an optional same-named default flag that can be set in +`upsd.conf`. ++ +If you want a data server always running, even if it would potentially not +serve all clients on every uptime, this option is for you (note you would +have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +later). ++ +Probably configuring `LISTEN *` is a better choice in such cases. + *UPSD_OPTIONS*:: Optional. Set upsd specific options. See linkman:upsd[8] for more details. It is ignored when 'MODE' above indicates that no upsd @@ -75,13 +101,21 @@ unavailable. On the other hand, it should not be so long that the system remains offline for an unreasonable amount of time if line power has returned. See sleep(1) for compatible time syntax. If you specify the time in seconds, use the "s" suffix. - ++ WARNING: this workaround might be dangerous under some circumstances. Please read http://bugs.debian.org/358696 for more details. +*POWEROFF_QUIET*:: +Optional, defaults to `false`. This setting controls if the NUT shutdown +integration scripts or service units would emit messages about their activity +(or lack thereof). By default they may be verbose, to aid in post-mortem +troubleshooting via logs or console captures. Set to `true` to avoid that +trove of information, if you consider it noise. + EXAMPLE ------- +------ # /etc/nut/nut.conf. See nut.conf(5) MODE=none @@ -91,12 +125,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 +139,7 @@ SEE ALSO linkman:ups.conf[5], linkman:upsd.conf[5], linkman:upsd.users[5], linkman:upsmon.conf[5] -INTERNET RESOURCES ------------------- -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nut_usb_addvars.txt b/docs/man/nut_usb_addvars.txt new file mode 100644 index 0000000000..bc776e241a --- /dev/null +++ b/docs/man/nut_usb_addvars.txt @@ -0,0 +1,126 @@ +//////// +This file is included into other man pages, where appropriate, +by their choice of markup and sectioning (usually lands into +"USB INTERFACE ONLY" for serial/usb or "EXTRA ARGUMENTS"). + +Keep in sync with nut_usb_addvars() method provided by +* drivers/libusb0.c +* drivers/libusb1.c +* drivers/usb-common.h + +and matching code in tools/nut-scanner/scan_usb.c +//////// + +*port =* 'string':: + +Some 'value' must be set, typically *auto*. ++ +NOTE: This could be a device filesystem path like `/dev/usb/hiddev0` +but current use of libusb API precludes knowing and matching by such +identifiers. They may also be inherently unreliable (dependent on +re-plugging and enumeration order). At this time the actual 'value' +is ignored, but syntactically some 'port' configuration must still +be there. + +It is possible to control multiple UPS units simultaneously by running +several instances of this driver, provided they can be uniquely +distinguished by setting some combination of the *vendor*, *product*, +*vendorid*, *productid*, *serial*, *bus* and/or *device* options detailed +below. For devices or operating systems that do not provide sufficient +information, the *allow_duplicates* option can be of use (limited and +risky!) + +*vendorid =* 'regex':: +*productid =* 'regex':: +*vendor =* 'regex':: +*product =* 'regex':: +*serial =* 'regex':: + +Select a specific UPS, in case there is more than one connected via +USB. Each option specifies an extended regular expression (see +*regex(7)* for more information on regular expressions), which +must match the UPS's entire respective vendor/product/serial string +(minus any surrounding whitespace), or the whole 4-digit +hexadecimal code for `vendorid` and `productid`. ++ +Try *lsusb(8)* or running this NUT driver with *-DD* command-line +argument for finding out the strings to match. ++ +Examples: + +- `-x vendor="Foo.Corporation.*"` +- `-x vendorid="051d*"` (APC) +- `-x product=".*(Smart|Back)-?UPS.*"` + +*bus =* 'regex':: + +Select a UPS on a specific USB bus or group of buses. The argument is +a regular expression that must match the bus name where the UPS is +connected (e.g. `bus="002"` or `bus="00[2-3]"`) as seen on Linux in +`/sys/bus/usb/devices` or *lsusb(8)*; including leading zeroes. + +*device =* 'regex':: + +Select a UPS on a specific USB device or group of devices. The argument is +a regular expression that must match the device name where the UPS is +connected (e.g. `device="001"` or `device="00[1-2]"`) as seen on Linux +in `/sys/bus/usb/devices` or *lsusb(8)*; including leading zeroes. ++ +NOTE: device numbers are not guaranteed by the OS to be stable across +re-boots or device re-plugging. + +*busport =* 'regex':: + +If supported by the hardware, OS and libusb on the particular deployment, +this option should allow to specify physical port numbers on an USB hub, +rather than logical `device` enumeration values, and in turn -- this should +be less volatile across reboots or re-plugging. The value may be seen in +the USB topology output of `lsusb -tv` on systems with that tool, for example. ++ +NOTE: this option is not practically supported by some NUT builds +(it should be ignored with a warning then), and not by all systems +that NUT can run on. + +*allow_duplicates*:: + +If you have several UPS devices which may not be uniquely identified by +the options above (e.g. only VID:PID can be discovered there), this flag +allows each driver instance where it is set to take the first match if +available, or proceed to try another. ++ +Normally the driver initialization would abort at this point claiming +"Resource busy" or similar error, assuming that the otherwise properly +matched device is unique -- and some other process already handles it. ++ +[WARNING] +========= +This feature is inherently non-deterministic! +The association of driver instance name to actual device +may vary between runs! + +If you only care to know that *at least* one of your no-name UPSes +is online, this option can help. + +If you must really know *which* one, it will not! +========= + +*usb_set_altinterface =* 'bAlternateSetting':: + +Force redundant call to `usb_set_altinterface()`, especially if needed +for devices serving multiple USB roles where the UPS is not represented +by the interface number `0` (default). + +*usb_config_index*:: +*usb_hid_rep_index*:: +*usb_hid_desc_index*:: +*usb_hid_ep_in*:: +*usb_hid_ep_out*:: + +Force use of specific interface, endpoint, descriptor index etc. numbers, +rather than defaulting to 0 (rarely other values in certain drivers for +some devices known to use non-zero numbers). Specified as a hexadecimal +number. ++ +As a rule of thumb for `usb_hid_desc_index` discovery, you can see larger +`wDescriptorLength` values (roughly 600+ bytes) in reports of `lsusb` or +similar tools. diff --git a/docs/man/nutdrv_atcl_usb.txt b/docs/man/nutdrv_atcl_usb.txt index cd99e2b77f..3f8e12105c 100644 --- a/docs/man/nutdrv_atcl_usb.txt +++ b/docs/man/nutdrv_atcl_usb.txt @@ -3,38 +3,57 @@ 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 +SYNOPSIS +-------- + +*nutdrv_atcl_usb* -h + +*nutdrv_atcl_usb* -a 'UPS_NAME' ['OPTIONS'] + +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 --------------- This driver supports the following optional setting: -*vendor*='name':: +*vendor =* 'name':: + In case your iManufacturer (Vendor) string does not exactly match -+ATCL FOR UPS+, you may provide an alternate string here. Note that a more -likely case is that your device is handled by another driver for +0001:0000+ -devices, such as linkman:nutdrv_qx[8]. ++ATCL FOR UPS+, you may provide an alternate string here (or specify "NULL" if +the device does not provide a vendor string but you want this driver to match). ++ +Note that a more likely case for mismatch is that your device is handled by +another driver for +0001:0000+ devices, such as linkman:nutdrv_qx[8]. ++ +NOTE: This driver does not intend to support USB-matching settings common to +other drivers, such as *vendorid*, *product*, *productid*, *serial*, *device* +or *bus*; also the *vendor* setting supported here is not a regular expression. 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 +70,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 +80,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nutdrv_qx.txt b/docs/man/nutdrv_qx.txt index b2b8a6da1f..0af25b016b 100644 --- a/docs/man/nutdrv_qx.txt +++ b/docs/man/nutdrv_qx.txt @@ -7,11 +7,16 @@ NAME nutdrv_qx - Driver for Q* protocol serial and USB based UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*nutdrv_qx* -h -This man page only documents the hardware-specific features of the *nutdrv_qx* driver. -For information about the core driver, see linkman:nutupsdrv[8]. +*nutdrv_qx* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the +*nutdrv_qx* driver. For information about the core driver, see +linkman:nutupsdrv[8]. SUPPORTED HARDWARE @@ -19,8 +24,9 @@ SUPPORTED HARDWARE 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' +'Hunnox', 'Masterguard', 'Mustek', 'Powercool', 'Voltronic Power', 'SKE' (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>>), @@ -63,6 +69,9 @@ If you set stayoff in linkman:ups.conf[5] when FSD arises the UPS will call a *s Skip autodetection of the protocol to use and only use the one specified. Supported values: 'bestups', 'hunnox', 'masterguard', 'mecer', 'megatec', 'megatec/old', 'mustek', 'q1', 'voltronic', 'voltronic-qs', 'voltronic-qs-hex' and 'zinto'. + +Run the driver program with the `--help` option to see the exact list of +`protocol` values it would currently recognize. ++ Note that if you end up using the 'q1' protocol, you may want to give a try to the 'mecer', 'megatec' and 'zinto' ones setting the <> (only one, or both). *pollfreq =* 'num':: @@ -76,20 +85,44 @@ If your UPS doesn't report either *battery.charge* or *battery.runtime* you may *default.battery.voltage.high =* 'value':: Maximum battery voltage that is reached after about 12 to 24 hours charging. -If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). +If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge_guesstimation,BATTERY CHARGE GUESSTIMATION>>). *default.battery.voltage.low =* 'value':: Minimum battery voltage just before the UPS automatically shuts down. -If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge,BATTERY CHARGE>>). +If you want the driver to report a guesstimated *battery.charge*, you need to specify this (see <<_battery_charge_guesstimation,BATTERY CHARGE GUESSTIMATION>>). *default.battery.voltage.nominal =* 'value':: *override.battery.voltage.nominal =* 'value':: Some devices show a wrong nominal battery voltage (or none at all), so you may need to override or set a default value. *override.battery.packs =* 'value':: -Some devices report a part of the total battery voltage. -For instance, if *battery.voltage.nominal* is 24 V, but it reports a *battery.voltage* of around 2 V, the number of *battery.packs* to correct this reading would be 12. -The driver will attempt to detect this automatically, but if this fails somehow, you may want to override this value. +Some devices "natively" report just a part of the total battery voltage +(see also *battery_voltage_reports_one_pack* below). ++ +For instance, if *battery.voltage.nominal* is 24 V, but it reports +a *battery.voltage* of around 2 V, the number of *battery.packs* to +correct this reading would be 12. ++ +The driver will attempt to detect this number automatically, but if +this fails somehow, you may want to override this value. ++ +Note that this is primarily useful for "guesstimation" of `battery.charge` +and/or `battery.runtime` (with `runtimecal` setting), if those readings are +not provided by the device directly. + +*battery_voltage_reports_one_pack*:: +Some devices "natively" report just report a part of the total battery voltage +(see also *override.battery.packs* above, if that value is not reported by the +device or properly guessed by the driver otherwise). ++ +If this flag is set, most of the subdrivers (except those which know about +more complicated device-specific nuances, currently: *ablerex*, *masterguard* +and *voltronic-qs-hex*) adjust their ultimately reported *battery.voltage* +value as a multiple of *battery.packs* and "native" *battery.voltage*). ++ +Note this is primarily useful for consistent diagnostics and graphing of the +numbers, and should not impact the "guesstimation" of `battery.charge` and/or +`battery.runtime` - so rather a cosmetic adjustment, than critical. *runtimecal =* 'value,value,value,value':: Parameter used in the (optional) runtime estimation. @@ -151,7 +184,7 @@ The acceptable range is +60..599940+ seconds. The acceptable range is +12..5940+ seconds. *pins_shutdown_mode =* 'value':: -Set http://www.networkupstools.org/protocols/sola.html#_shutdown_set_command[shutdown mode functionality of Pin 1 and Pin 7] on the UPS DB9 communication port (Per Best Power's EPS-0059) to 'value' [+0..6+]. +Set https://www.networkupstools.org/protocols/sola.html#_shutdown_set_command[shutdown mode functionality of Pin 1 and Pin 7] on the UPS DB9 communication port (Per Best Power's EPS-0059) to 'value' [+0..6+]. MASTERGUARD PROTOCOL @@ -318,37 +351,17 @@ If you find that your UPS isn't detected or the communication with the UPS is un USB INTERFACE ONLY ~~~~~~~~~~~~~~~~~~ -*port =* 'string':: -You must set 'value' to *auto*. - -*vendorid =* 'regex':: -*productid =* 'regex':: -*vendor =* 'regex':: -*product =* 'regex':: -*serial =* 'regex':: -Select a specific UPS, in case there is more than one connected via USB. -Each option specifies an extended regular expression (see *regex(7)*) that must match the UPS's entire vendor/product/serial string (minus any surrounding whitespace), or the whole 4-digit hexadecimal code for vendorid and productid. -Try *-DD* for finding out the strings to match. -+ -Examples: - - - `-x vendor="Foo.Corporation.*"` - - `-x vendorid=051d*` (APC) - - `-x product=".*(Smart|Back)-?UPS.*"` - -*bus =* 'regex':: -Select a UPS on a specific USB bus or group of buses. -The argument is a regular expression that must match the bus name where the UPS is connected (e.g. +bus="002"+, +bus="00[2-3]"+). - -*device =* 'regex':: -Select a UPS on a specific USB device or group of devices. -The argument is a regular expression that must match the device name where the UPS is connected (e.g. +device="001"+, +device="00[1-2]"+). -Note that device numbers are not guaranteed by the OS to be stable across re-boots or device re-plugging. +include::nut_usb_addvars.txt[] *subdriver =* 'string':: Select a serial-over-USB subdriver to use. You have a choice between *cypress*, *fabula*, *fuji*, *hunnox*, *ippon*, *krauler*, *phoenix*, *phoenixtec*, *sgs*, *snr*, *armac* and *ablerex*. -When using this option, it is mandatory to also specify the *vendorid* and *productid*. ++ +Run the driver program with the `--help` option to see the exact list of +`subdriver` values it would currently recognize. ++ +NOTE: When using this option, it is mandatory to also specify the *vendorid* +and *productid* matching parameters. *langid_fix =* 'value':: Apply the language ID workaround to the *krauler* subdriver. @@ -508,8 +521,8 @@ Put the UPS in ECO Mode. Take the UPS out of ECO Mode. -BATTERY CHARGE --------------- +BATTERY CHARGE GUESSTIMATION +---------------------------- Due to popular demand, this driver will report a guesstimated *battery.charge* and optionally *battery.runtime*, provided you specified a couple of the <<_extra_arguments,EXTRA ARGUMENTS>> listed above. @@ -517,9 +530,9 @@ If you specify both *battery.voltage.high* and *battery.voltage.low* in linkman: This is not reliable under load, as this only gives reasonably accurate readings if you disconnect the load, let the battery rest for a couple of minutes and then measure the open cell voltage. This just isn't practical if the power went out and the UPS is providing power for your systems. - battery.voltage - battery.voltage.low -battery.charge = ------------------------------------------ x 100 % - battery.voltage.high - battery.voltage.low + battery.voltage - battery.voltage.low + battery.charge = ------------------------------------------ x 100 % + battery.voltage.high - battery.voltage.low There is a way to get better readings without disconnecting the load but this requires one to keep track on how much (and how fast) current is going in and out of the battery. If you specified the *runtimecal*, the driver will attempt to do this. @@ -577,7 +590,7 @@ Part of this, the following bestups options, in linkman:ups.conf[5], are no long *nombattvolt*:: *battvoltmult*:: -See <<_battery_charge,BATTERY CHARGE>>. +See <<_battery_charge_guesstimation,BATTERY CHARGE GUESSTIMATION>>. *ID*:: Discarded. @@ -794,13 +807,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 +833,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: https://www.networkupstools.org/ +* The NUT HCL: https://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/nutdrv_siemens_sitop.txt b/docs/man/nutdrv_siemens_sitop.txt index 291096130b..87bf629a16 100644 --- a/docs/man/nutdrv_siemens_sitop.txt +++ b/docs/man/nutdrv_siemens_sitop.txt @@ -3,17 +3,25 @@ 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 +SYNOPSIS +-------- + +*nutdrv_siemens_sitop* -h + +*nutdrv_siemens_sitop* -a 'UPS_NAME' ['OPTIONS'] + +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. + +*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, so as far as this driver is concerned, all models are serial models. @@ -26,6 +34,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 +63,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,23 +95,26 @@ 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. -In order to react quickly on status changes from the UPS, and to prevent the serial buffer -from overflowing, *pollinterval* should be set to a relatively low value. The recommended -value is 1 (second). + +In order to react quickly on status changes from the UPS, and to prevent +the serial buffer from overflowing, *pollinterval* should be set to a +relatively low value. The recommended value is 1 (second). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings: *max_polls_without_data*='num':: The serial port is polled periodically for new data (see *Polling*). -If there is no valid new data after 'num' polls, it is assumed that communication -with the UPS is lost. -The default value is 2. Lower values may cause spurious 'Data stale' messages, -especially at startup. +If there is no valid new data after 'num' polls, it is assumed that +communication with the UPS is lost. +The default value is 2. Lower values may cause spurious 'Data stale' +messages, especially at startup. INSTANT COMMANDS ---------------- @@ -126,6 +139,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 +150,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 +171,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 +188,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 +198,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index 90ac366813..bfef09f52b 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,34 +28,49 @@ 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: - linkman:nutscan_display_parsable[3] for parsable output, -- linkman:nutscan_display_ups_conf[3] for ups.conf style. +- linkman:nutscan_display_ups_conf[3] for ups.conf style, +- linkman:nutscan_display_ups_conf_with_sanity_check[3] for ups.conf style + with comments for warnings about possible configuration problems (if any). 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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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..2cde5523f8 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,37 @@ 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 * alt_driver_names; + 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..b24ff8e3a7 100644 --- a/docs/man/nutscan_add_option_to_device.txt +++ b/docs/man/nutscan_add_option_to_device.txt @@ -4,14 +4,29 @@ NUTSCAN_ADD_OPTION_TO_DEVICE(3) NAME ---- -nutscan_add_option_to_device - Add option data to the specified device. +nutscan_add_option_to_device, nutscan_add_commented_option_to_device - Add +option data to the specified device. + SYNOPSIS -------- #include - void nutscan_add_option_to_device(nutscan_device_t * device,char * option_name, char * value); + /* Add enabled option data to the specified device. */ + void nutscan_add_option_to_device( + nutscan_device_t * device, + char * option_name, + char * value); + + /* Since libnutscan version 2.5.0: + * Add option data to the specified device with an optional comment tag + * for options suggested, but not currently enabled for actual use. */ + void nutscan_add_commented_option_to_device( + nutscan_device_t * device, + char * option_name, + char * value, + char * comment_tag); DESCRIPTION @@ -20,21 +35,48 @@ 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 * alt_driver_names; + 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. + +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', and optionally a 'comment_tag'. Copies of the +'option_name', 'value' and 'comment_tag' are stored in the device, +so the caller can safely free all of the original strings used as +arguments. -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. +NOTE: A non-`NULL` value of the 'comment_tag' makes the option not-enabled +for current use. Depending on the output format, it may be either completely +ignored, or rendered as a comment with this value as a prefix (a zero-length +string `"\0"` may be passed to have no prefix). -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. +Such options and their values may be further sanity-checked and reported +as warnings by *nutscan_display_sanity_check()* dispatcher and its related +methods which implement the logic of particular checks. This is used for +example when generating 'ups.conf' file content suggestions with +*nutscan_display_ups_conf_with_sanity_check()* method. + +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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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_device_to_device[3] diff --git a/docs/man/nutscan_cidr_to_ip.txt b/docs/man/nutscan_cidr_to_ip.txt index 99d83327d1..ef7c9a9c1a 100644 --- a/docs/man/nutscan_cidr_to_ip.txt +++ b/docs/man/nutscan_cidr_to_ip.txt @@ -16,16 +16,29 @@ 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_ups_conf_with_sanity_check[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..aa2cc76e4b 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,19 +17,31 @@ 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. + +Note that this format is for machine consumption, so is not associated +with sanity checks that may be used along with display method for the +'ups.conf' file format. 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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[3], linkman:nutscan_display_ups_conf[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] diff --git a/docs/man/nutscan_display_sanity_check.txt b/docs/man/nutscan_display_sanity_check.txt new file mode 100644 index 0000000000..f0e6ce4eb0 --- /dev/null +++ b/docs/man/nutscan_display_sanity_check.txt @@ -0,0 +1,39 @@ +NUTSCAN_DISPLAY_SANITY_CHECK(3) +=============================== + +NAME +---- + +nutscan_display_sanity_check - Display sanity check warnings about +the specified `nutscan_device_t` structure on stdout. + +SYNOPSIS +-------- + + #include + + void nutscan_display_sanity_check(nutscan_device_t * device); + +DESCRIPTION +----------- + +The *nutscan_display_sanity_check()* function calls all sanity-check +analyzers against displays all NUT devices in 'device', and they may +print comments to stdout. It displays them in a way that it can be +directly copied into the 'ups.conf' file. + +It is called from *nutscan_display_ups_conf_with_sanity_check()* to +provide an aggregate content for 'ups.conf' file in one shot. + +SEE ALSO +-------- + +linkman:nutscan_display_ups_conf_with_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[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], 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] diff --git a/docs/man/nutscan_display_sanity_check_serial.txt b/docs/man/nutscan_display_sanity_check_serial.txt new file mode 100644 index 0000000000..243b15a341 --- /dev/null +++ b/docs/man/nutscan_display_sanity_check_serial.txt @@ -0,0 +1,43 @@ +NUTSCAN_DISPLAY_SANITY_CHECK_SERIAL(3) +====================================== + +NAME +---- + +nutscan_display_sanity_check_serial - Display sanity check warnings +about "serial" (serial number/code string) optional values in the +specified `nutscan_device_t` structure on stdout. + +SYNOPSIS +-------- + + #include + + void nutscan_display_sanity_check_serial(nutscan_device_t * device); + +DESCRIPTION +----------- + +The *nutscan_display_sanity_check_serial()* function analyzes "serial" +optional field in all NUT devices in 'device', and in case of duplicate +or otherwise seemingly invalid values, prints comments to stdout. +It displays them in a way that it can be directly copied into the +'ups.conf' file. + +It is called from *nutscan_display_ups_conf_with_sanity_check()* to +provide an aggregate content for 'ups.conf' file in one shot. + +SEE ALSO +-------- + +linkman:nutscan_display_ups_conf_with_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[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_sanity_check[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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] diff --git a/docs/man/nutscan_display_ups_conf.txt b/docs/man/nutscan_display_ups_conf.txt index 4d714c9309..3b6233ac9d 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,13 +17,22 @@ 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. + +It is called from *nutscan_display_ups_conf_with_sanity_check()* to +provide an aggregate content for 'ups.conf' file in one shot. 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_with_sanity_check[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[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] diff --git a/docs/man/nutscan_display_ups_conf_with_sanity_check.txt b/docs/man/nutscan_display_ups_conf_with_sanity_check.txt new file mode 100644 index 0000000000..838375eb36 --- /dev/null +++ b/docs/man/nutscan_display_ups_conf_with_sanity_check.txt @@ -0,0 +1,36 @@ +NUTSCAN_DISPLAY_UPS_CONF_WITH_SANITY_CHECK(3) +============================================= + +NAME +---- + +nutscan_display_ups_conf_with_sanity_check - Display the specified +`nutscan_device_t` structure and sanity-check warnings on stdout. + +SYNOPSIS +-------- + + #include + + void nutscan_display_ups_conf_with_sanity_check(nutscan_device_t * device); + +DESCRIPTION +----------- + +The *nutscan_display_ups_conf_with_sanity_check()* function displays +all NUT devices in 'device' to stdout, and follows up with comments +about sanity-check violations (if any). 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_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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[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] diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index 1479f385a0..7ef10cc3ad 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,13 +17,22 @@ 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_with_sanity_check[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3] diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index d13923d34f..bf8aa26d89 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -16,25 +16,45 @@ 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], +linkman:nutscan_display_ups_conf_with_sanity_check[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3] diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index f23fe0b9ce..27768d270b 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -11,30 +11,45 @@ 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 nutscan_avail_nut = 1 : Old NUT method is available + nutscan_avail_nut_simulation = 1 : NUT simulation devices method is available nutscan_avail_snmp = 1 : SNMP method is available 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_nut[3], linkman:nutscan_scan_avahi[3], -linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http_range[3], +linkman:nutscan_scan_nut[3], linkman:nutscan_scan_nut_simulation[3], +linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], +linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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] diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index 81ab4dbf18..43581599ec 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -11,23 +11,34 @@ 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] +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3] linkman:nutscan_add_device_to_device[3] diff --git a/docs/man/nutscan_scan_avahi.txt b/docs/man/nutscan_scan_avahi.txt index 2c830981a7..5c407d3d7b 100644 --- a/docs/man/nutscan_scan_avahi.txt +++ b/docs/man/nutscan_scan_avahi.txt @@ -10,35 +10,46 @@ 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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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..42e2a826ec 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,14 +29,20 @@ 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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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], diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index 5999152fbb..a1fddcfce8 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,10 +46,15 @@ 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_scan_snmp[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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_nut.txt b/docs/man/nutscan_scan_nut.txt index d0b93b700d..08c6520c88 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -10,31 +10,47 @@ 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_scan_ipmi[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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_nut_simulation.txt b/docs/man/nutscan_scan_nut_simulation.txt new file mode 100644 index 0000000000..8e7ba1999c --- /dev/null +++ b/docs/man/nutscan_scan_nut_simulation.txt @@ -0,0 +1,47 @@ +NUTSCAN_SCAN_NUT_SIMULATION(3) +============================== + +NAME +---- + +nutscan_scan_nut_simulation - Scan your sysconfig directory for NUT simulation devices. + +SYNOPSIS +-------- + + #include + #include /* useconds_t */ + + nutscan_device_t * nutscan_scan_nut_simulation(); + +DESCRIPTION +----------- + +The *nutscan_scan_nut_simulation()* function try to detect available NUT +simulation devices, by finding .dev and .seq files in your sysconfig +directory. + +You MUST call linkman:nutscan_init[3] before using this function. + +RETURN VALUE +------------ + +The *nutscan_scan_nut_simulation()* 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_range[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], +linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_nut[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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], +linkman:nutscan_cidr_to_ip[3] diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index 8f91f85911..3ae72ccfd1 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,31 +46,47 @@ 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_scan_ipmi[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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_usb.txt b/docs/man/nutscan_scan_usb.txt index dfeb0d4468..f9632bdd00 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -11,26 +11,64 @@ SYNOPSIS #include - nutscan_device_t * nutscan_scan_usb(); + nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts); + +NOTE +---- + +Before `libnutscan` version 2.5.0 there was no argument: + + nutscan_device_t * nutscan_scan_usb(void); + +After the API update to have an argument, equivalent default activity can +be achieved by passing `NULL` value for the argument. DESCRIPTION ----------- -The *nutscan_scan_usb()* function try to detect NUT compatible USB devices. +The *nutscan_scan_usb()* function tries to detect NUT compatible USB devices. + +The `scanopts` argument contains toggles about values that would be reported +into the generated device section. Currently they regard physical link details +which can change over time (e.g. USB re-enumeration due to software or hardware +re-connections); see `nut-scan.h` for current details: + +---- +/* USB scan options structure */ +typedef struct nutscan_usb { + /* Hardware link related values below are not reliable for run-time + * matching (they can change over time) but can be useful if e.g. + * "serial" is not available or unique */ + int report_bus; + int report_busport; + int report_device; + + /* The value is not currently used for device matching, but might be + * used later, and it is available from discovery */ + int report_bcdDevice; +} nutscan_usb_t; +---- 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_scan_ipmi[3], +linkman:nutscan_display_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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.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..70e0a86b23 --- /dev/null +++ b/docs/man/nutscan_scan_xml_http_range.txt @@ -0,0 +1,58 @@ +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_sanity_check[3], +linkman:nutscan_display_sanity_check_serial[3], +linkman:nutscan_display_ups_conf_with_sanity_check[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..e1cc17722a 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,44 @@ Raise log level threshold. Use this multiple times to log more details. + The debugging comment above also applies here. +*-c* 'command':: +Send 'command' to the background process as a signal. Valid commands +are: + + *reload*;; reread configuration files, ignoring modified settings + which can not be applied "on the fly" + *reload-or-error*;; reread configuration files, ignoring but counting + changed values which require a driver restart (can not be + changed on the fly), and return a success/fail code based + on that count, so the caller can decide the fate of the + currently running driver instance + *reload-or-exit*;; reread configuration files, exiting the old + driver process if it encounters modified settings + which can not be applied "on the fly" (so caller like + systemd can launch another copy of the driver) +///////// + *reload-or-restart*;; reread configuration files, causing the + old driver process to close the device connection + and re-exec itself if it encounters modified settings + which can not be applied "on the fly" (may fail for + critical changes like run-time user/group accounts) +///////// + +*-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 +each NUT driver as a foreground process so it does not create a PID file. +See also `-FF` option as an alternative. + +*-F*:: +Enforce running the driver as a foreground process, regardless of debugging +or data-dumping settings. +Specify twice (`-FF` or `-F -F`) to save the PID file even in this mode. + +*-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 +172,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 +202,7 @@ production use. FILES ----- + ups.conf:: Required configuration file. This contains all details on which drivers to start and where the hardware is attached. @@ -164,13 +210,18 @@ to start and where the hardware is attached. ENVIRONMENT VARIABLES --------------------- +*NUT_DEBUG_LEVEL* sets default debug verbosity if no *-D* arguments +were provided on command line, but does not request that the daemon +runs in foreground mode. + *NUT_CONFPATH* is the path name of the directory that contains -`upsd.conf` and other configuration files. If this variable is not set, -*upsd* uses a built-in default, which is often `/usr/local/ups/etc`. +`ups.conf` and other configuration files. If this variable is not set, +drivers use a built-in default, which is often `/usr/local/ups/etc`. *NUT_STATEPATH* is the path name of the directory in which -*upsd* keeps state information. If this variable is not set, -*upsd* uses a built-in default, which is often `/var/state/ups`. +*upsd* and drivers keep shared state information. If this variable +is not set, *upsd* and drivers use a built-in default, which is often +`/var/state/ups`. The *STATEPATH* directive in linkman:upsd.conf[5] overrides this variable. *NUT_ALTPIDPATH* is the path name of the directory in which @@ -188,61 +239,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/ +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/oneac.txt b/docs/man/oneac.txt index dfa5b0852d..b6fda081fc 100644 --- a/docs/man/oneac.txt +++ b/docs/man/oneac.txt @@ -6,10 +6,14 @@ NAME oneac - Driver for Oneac UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*oneac* -h -This man page only documents the hardware-specific features of the +*oneac* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the oneac driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -28,6 +32,7 @@ linkman:genericups[8] driver. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -39,6 +44,7 @@ Change shutdown delay time from 0 second default. INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands. (See linkman:upscmd[8]) @@ -97,6 +103,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 +115,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/optiups.txt b/docs/man/optiups.txt index 055d26ad1d..d3bdf6ec0a 100644 --- a/docs/man/optiups.txt +++ b/docs/man/optiups.txt @@ -6,10 +6,14 @@ NAME optiups - Driver for Opti-UPS (Viewsonic) UPS and Zinto D (ONLINE-USV) equipment -NOTE ----- +SYNOPSIS +-------- + +*optiups* -h + +*optiups* -a 'UPS_NAME' ['OPTIONS'] -This man page only documents the hardware-specific features of the +NOTE: This man page only documents the hardware-specific features of the optiups driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -56,13 +60,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 +78,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/phoenixcontact_modbus.txt b/docs/man/phoenixcontact_modbus.txt index 9711937d46..a9f22cdc05 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: https://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/pijuice.txt b/docs/man/pijuice.txt index 1643208f34..f97bce4bb0 100644 --- a/docs/man/pijuice.txt +++ b/docs/man/pijuice.txt @@ -3,11 +3,17 @@ PIJUICE(8) NAME ---- + pijuice - driver for UPS in PiJuice HAT -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*pijuice* -h + +*pijuice* -a 'UPS_NAME' ['OPTIONS'] + +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 +23,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 +37,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 +60,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 +69,7 @@ made to turn off the UPS. AUTHORS ------- + Andrew Anderson SEE ALSO @@ -67,15 +77,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: https://www.networkupstools.org/ diff --git a/docs/man/powercom.txt b/docs/man/powercom.txt index ce4dbd73d0..62b7a6c223 100644 --- a/docs/man/powercom.txt +++ b/docs/man/powercom.txt @@ -6,14 +6,20 @@ NAME powercom - UPS driver for serial Powercom/Trust/Advice UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*powercom* -h + +*powercom* -a 'UPS_NAME' ['OPTIONS'] + +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 +30,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 +123,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 +136,7 @@ values for ALL models. Trust ~~~~~ + numOfBytesFromUPS = 11 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0},{7,0},{8,0}} @@ -139,6 +148,7 @@ Trust KP625AP ~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0x80},{7,0},{8,0}} @@ -150,6 +160,7 @@ KP625AP Egys ~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0x80},{7,0},{8,0}} @@ -161,6 +172,7 @@ Egys IMP ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} @@ -168,6 +180,7 @@ IMP KIN ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x4b},{8,0},{8,0}} @@ -175,6 +188,7 @@ KIN BNT ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x42},{8,0},{8,0}} @@ -182,6 +196,7 @@ BNT BNT-other ~~~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{8,0},{8,0},{8,0}} @@ -193,25 +208,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/powerman-pdu.txt b/docs/man/powerman-pdu.txt index 0feec852e7..30d2fcda97 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. + +In the current NUT version as of this writing (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: https://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..8e86742545 100644 --- a/docs/man/powerpanel.txt +++ b/docs/man/powerpanel.txt @@ -6,14 +6,20 @@ NAME powerpanel - Driver for serial PowerPanel Plus compatible UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*powerpanel* -h + +*powerpanel* -a 'UPS_NAME' ['OPTIONS'] + +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 +30,7 @@ network cards via SNMP. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in linkman:ups.conf[5]: *protocol=*['text,binary']:: @@ -51,6 +58,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 +80,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 +97,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 +108,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 +125,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 +140,7 @@ instrument. AUTHORS ------- + Arjen de Korte , Doug Reynolds SEE ALSO @@ -135,13 +148,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/rhino.txt b/docs/man/rhino.txt index 443dec85e7..ed97f717ec 100644 --- a/docs/man/rhino.txt +++ b/docs/man/rhino.txt @@ -3,17 +3,24 @@ RHINO(8) NAME ---- + rhino - Driver for Brazilian Microsol RHINO UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*rhino* -h + +*rhino* -a 'UPS_NAME' ['OPTIONS'] + +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 +52,7 @@ COMMANDS AUTHOR ------ + Silvino B. MagalhĆ£es SEE ALSO @@ -52,8 +60,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/richcomm_usb.txt b/docs/man/richcomm_usb.txt index 69578e74d5..fd37343400 100644 --- a/docs/man/richcomm_usb.txt +++ b/docs/man/richcomm_usb.txt @@ -3,24 +3,41 @@ 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 +SYNOPSIS +-------- + +*richcomm_usb* -h + +*richcomm_usb* -a 'UPS_NAME' ['OPTIONS'] + +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 apply. This means that you will only get the essentials in ups.status: OL, OB, and LB. See also linkman:genericups[8]. +//////// +TODO: Uncomment after solving https://github.com/networkupstools/nut/issues/1768 + +EXTRA ARGUMENTS +--------------- + +include::nut_usb_addvars.txt[] +//////// + 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 +48,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/riello_ser.txt b/docs/man/riello_ser.txt index 45a399b902..a9c684f51f 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/riello_usb.txt b/docs/man/riello_usb.txt index d4afbfedb7..737aa499f7 100644 --- a/docs/man/riello_usb.txt +++ b/docs/man/riello_usb.txt @@ -1,5 +1,5 @@ RIELLO_USB(8) -=========== +============= NAME ---- @@ -24,6 +24,10 @@ riello_usb supports all recent Riello UPS with USB. Older Riello UPS products are not supported. +EXTRA ARGUMENTS +--------------- + +include::nut_usb_addvars.txt[] AUTHOR ------ @@ -35,8 +39,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/safenet.txt b/docs/man/safenet.txt index f6f5a5a34c..62ca249eb6 100644 --- a/docs/man/safenet.txt +++ b/docs/man/safenet.txt @@ -3,21 +3,29 @@ SAFENET(8) NAME ---- + safenet - Driver for SafeNet compatible UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*safenet* -h + +*safenet* -a 'UPS_NAME' ['OPTIONS'] + +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 +46,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 +82,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 +97,7 @@ return when the power comes back. AUTHOR ------ + Arjen de Korte SEE ALSO @@ -94,8 +105,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/skel.txt b/docs/man/skel.txt index 7cae6669c9..034e3034af 100644 --- a/docs/man/skel.txt +++ b/docs/man/skel.txt @@ -3,11 +3,17 @@ SKEL(8) NAME ---- + skel - skeleton driver man page -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*skel* -h + +*skel* -a 'UPS_NAME' ['OPTIONS'] + +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 +37,7 @@ apropos(8) database is properly rebuilt. SUPPORTED HARDWARE ------------------ + *skel* supports ... ////////////////////////////////////////// @@ -44,6 +51,7 @@ CABLING EXTRA ARGUMENTS --------------- + This driver also supports the following optional settings: *option1*='num':: @@ -86,6 +94,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 +109,7 @@ some form of contact information so that users can report bugs. AUTHORS ------- + J Random User ////////////////////////////////////////// @@ -114,8 +124,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/sms_ser.txt b/docs/man/sms_ser.txt new file mode 100644 index 0000000000..c5676729f1 --- /dev/null +++ b/docs/man/sms_ser.txt @@ -0,0 +1,50 @@ +SMS_SER(8) +========== + +NAME +---- + +sms_ser - Driver for SMS UPS Protocol 1Phase. + +SYNOPSIS +-------- + +*sms_ser* -h + +*sms_ser* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features +of the sms_ser driver. For information about the core driver, see +linkman:nutupsdrv[8], and for the technical background check in +doc/sms-brazil-protocol.txt. + +NOTE: Given the proximity of this protocol to Megatec Qx family, +this driver may later become part of `nutdrv_qx` collection. + + +SUPPORTED HARDWARE +------------------ + +sms_ser supports only the "sms 1phase" SMS Product, as described in +the `monofasico.xml` file delivered with the device. + +Other SMS protocols for their other products are not supported. + + +AUTHOR +------ + +Alex W. BaulĆ© + +SEE ALSO +-------- + +The core driver +~~~~~~~~~~~~~~~~ + +linkman:nutupsdrv[8] + +Internet resources +~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 23781be617..221e081ab1 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -6,34 +6,44 @@ NAME snmp-ups - Multi-MIB Driver for SNMP UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*snmp-ups* -h + +*snmp-ups* -a 'UPS_NAME' ['OPTIONS'] + +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]. 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 -*pw*:: -Powerware devices with ConnectUPS SNMP cards +*eaton_pw_nm2*:: +Powerware devices with ConnectUPS SNMP cards, as well as UPSes with +Eaton Gigabit Network Cards (Network-M2) (renamed from pw) -*pxgx_ups*:: -Eaton devices with Power Xpert Gateway UPS Card +*eaton_pxg_ups*:: +Eaton devices with Power Xpert Gateway UPS Card (renamed from pxgx_ups) *aphel_genesisII*:: Eaton Powerware ePDU Monitored @@ -54,7 +64,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 +96,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 +128,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 @@ -124,6 +140,7 @@ equipment which has strangeness in the three-phase power reporting. *secLevel*='value':: Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv) +This parameter is mandatory if you use non-trivial authentication. *secName*='value':: Set the securityName used for authenticated SNMPv3 messages (no default) @@ -149,6 +166,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 +174,7 @@ SNMP v3 also requires OpenSSL support from http://www.openssl.org. LIMITATIONS ----------- + Shutdown ~~~~~~~~ @@ -168,15 +187,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 +216,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 + +Available at: https://www.networkupstools.org/ups-protocols.html#_snmp Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/sockdebug.txt b/docs/man/sockdebug.txt new file mode 100644 index 0000000000..f79bde71be --- /dev/null +++ b/docs/man/sockdebug.txt @@ -0,0 +1,83 @@ +SOCKDEBUG(8) +============ + +NAME +---- + +sockdebug - simple developer/troubleshooting aid utility to communicate +with a NUT driver using the socket protocol + +SYNOPSIS +-------- + +*sockdebug* socketname + +For example (WIN32 and POSIX builds): + + sockdebug.exe dummy-ups-UPS1 + +For example (POSIX-compliant systems using an alternate state path): + + sockdebug /var/state/ups/dummy-ups-UPS1 + +DESCRIPTION +----------- + +*sockdebug* is a tool built when NUT `configure --with-dev` is enabled. +It may alternatively be built by calling `make sockdebug` in the root +of the build workspace. Actual source files used depend on the platform. + +It is used to connect to a NUT driver using the socket protocol on an +Unix socket or Windows pipe, similarly to how the linkman:upsd[8] data +server talks to the locally running drivers in order to represent them +on the network further using the common NUT protocol of the Network UPS +Tools project, or how driver programs can communicate to their already +running instances to implement commands like live `reload-or-error`. + +This tool allows a developer or troubleshooter to watch the broadcast +updates emitted by the driver, as well as to issue unicast commands and +receive replies (during an interactive investigation session, you may +want to command `NOBROADCAST` first). + +For more details see the `docs/sock-protocol.txt` file in NUT sources. + +OPTIONS +------- + +*sockdebug* accepts (and requires) the following option: + +*socketname*:: +Either a full path (in POSIX builds) or the base name of device socket/pipe +(on all platforms), comprised of a `drivername-devicename` tuple, e.g. some +`dummy-ups-UPS1` for a `dummy-ups` driver instance handling an `UPS1` device +configuration (which in turn may originate in the `ups.conf` file or be +dynamically constructed for tests by calling the driver program with a +`-s TMP` CLI option). ++ +On POSIX systems, if this argument only represents a base name and not a +full path to the Unix socket file, the tool should first look for the file +in the current directory, and then fall back to configured (built-in) state +path or the value of `NUT_STATEPATH` environment variable, if set; e.g.: ++ +------ + NUT_STATEPATH=/tmp ./server/sockdebug dummy-ups-UPS1 +------ + +AUTHORS +------- + +This manual page was written by Jim Klimov . + +The program was originally written by +Russell Kroll (POSIX version), and by +Frederic Bohe (Windows version). + +SEE ALSO +-------- + +linkman:upsd[8] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt new file mode 100644 index 0000000000..8d0fecd552 --- /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: https://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..7c1bf016e8 100644 --- a/docs/man/solis.txt +++ b/docs/man/solis.txt @@ -6,16 +6,21 @@ NAME solis - Driver for Brazilian Microsol SOLIS UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*solis* -h + +*solis* -a 'UPS_NAME' ['OPTIONS'] -This man page only documents the hardware-specific features of the +NOTE: This man page only documents the hardware-specific features of the solis 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: * Solis 1000 VA * Solis 1500 VA @@ -55,6 +60,7 @@ connect to the UPS, but some values are read incorrectly. AUTHOR ------ + Silvino B. MagalhĆ£es SEE ALSO @@ -62,8 +68,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/tripplite.txt b/docs/man/tripplite.txt index 9305551eae..986afc5a75 100644 --- a/docs/man/tripplite.txt +++ b/docs/man/tripplite.txt @@ -3,22 +3,30 @@ TRIPPLITE(8) NAME ---- + tripplite - Driver for Tripp-Lite SmartPro UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*tripplite* -h + +*tripplite* -a 'UPS_NAME' ['OPTIONS'] + +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 +44,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/tripplite_usb.txt b/docs/man/tripplite_usb.txt index d71537f676..087a310aa4 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: @@ -35,8 +40,8 @@ This driver has been tested with the following models: * SMART2200RMXL2U * SMART3000RM2U -If you have used Tripp Lite's PowerAlert software to connect to your UPS, there -is a good chance that 'tripplite_usb' will work if it uses one of the +If you have used Tripp Lite's PowerAlert software to connect to your UPS, +there is a good chance that 'tripplite_usb' will work if it uses one of the following protocols: * Protocol 0004 @@ -45,10 +50,10 @@ following protocols: * Protocol 3003 * Protocol 3005 -On the other hand, if the web page for your UPS on the Tripp-Lite website says -"HID-compliant USB port also enables direct integration with built-in power -management and auto-shutdown features of Windows and MAC OS X", then you should -use the linkman:usbhid-ups[8] driver instead. +On the other hand, if the web page for your UPS on the Tripp-Lite website +says "HID-compliant USB port also enables direct integration with built-in +power management and auto-shutdown features of Windows and MAC OS X", then +you should use the linkman:usbhid-ups[8] driver instead. EXTRA ARGUMENTS --------------- @@ -56,6 +61,39 @@ EXTRA ARGUMENTS This driver supports the following optional settings in the linkman:ups.conf[5] file (or with '-x' on the command line): +include::nut_usb_addvars.txt[] + +- `-x upsid="12345"` + +Select a specific UPS by its unique UPS ID. The argument is a regular expression +that must match the UPS ID string. This allows for precise identification of UPS +devices when multiple devices of the same make and model are connected. +See below regarding how to read and write the ups id (unit id) using linkman:upsrw[8]. + + +[NOTE] +.Notes for `tripplite_usb` driver handling of common USB matching settings: +====== +* *product* is a regular expression to match the product string for the UPS. +This would be useful if you have two different Tripp Lite UPS models connected +to the same monitoring system, and you want to be sure that you shut them down +in the correct order. ++ +This regex is matched against the full USB product string as seen in +lsusb(8). The `ups.model` in the linkman:upsc[1] output only lists the name +after `TRIPP LITE`, so to match a SMART2200RMXL2U, you could use the regex +`.*SMART2200.*`. + +* The *productid* is a regular expression which matches the UPS PID as four +hexadecimal digits. So far, the only known devices that work with this driver +have PID `0001`. + +* The *serial* option may be or not be helpful: it does not appear that these +particular Tripp Lite UPSes supported by this driver use the `iSerial` +descriptor field to return a serial number. However, in case your unit does, +you may specify it here. +====== + *offdelay*:: This setting controls the delay between receiving the "kill" command ('-k') @@ -70,64 +108,30 @@ range of 10% through 100%, so the resting voltage of the charged battery can be used for *battery_max*, and the higher float charge voltage should not cause problems. -*bus*:: - -This regular expression is used to match the USB bus (as seen in -`/proc/bus/usb/devices` or lsusb(8); including leading zeroes). - -*device*:: - -This regular expression is used to match the USB device (as seen in -`/proc/bus/usb/devices` or lsusb(8); including leading zeroes). -Note that device numbers are not guaranteed by the OS to be stable across -re-boots or device re-plugging. - -*product*:: - -A regular expression to match the product string for the UPS. This would be -useful if you have two different Tripp Lite UPS models connected to the -system, and you want to be sure that you shut them down in the correct order. - -NOTE: This regex is matched against the full USB product string as seen in -lsusb(8). The `ups.model` in the linkman:upsc[1] output only lists the name after -`TRIPP LITE`, so to match a SMART2200RMXL2U, you could use the regex -`.*SMART2200.*`. - -*productid*:: - -The productid is a regular expression which matches the UPS PID as four -hexadecimal digits. So far, the only devices that work with this driver have -PID `0001`. - -*serial*:: - -It does not appear that these particular Tripp Lite UPSes use the `iSerial` -descriptor field to return a serial number. However, in case your unit does, -you may specify it here. - -For more information on regular expressions, see regex(7) - RUNTIME VARIABLES ----------------- *ups.delay.shutdown*:: -This variable is the same as the 'offdelay' setting, but it can be changed at -runtime by linkman:upsrw[8]. +This variable is the same as the 'offdelay' setting, but it can be +changed at runtime by linkman:upsrw[8]. *ups.id*:: -Some SMARTPRO models feature an ID that can be set and retrieved. If your UPS -supports this feature, this variable will be listed in the output of linkman:upsrw[8]. +Some SMARTPRO models feature an Unit ID (ups.id) that can be set and retrieved. +If your UPS supports this feature, this variable will be listed in +the output of linkman:upsrw[8]. *outlet.1.switch*:: -Some Tripp Lite units have a switchable outlet (usually outlet #1) which can be -turned on and off by writing '1' or '0', respectively, to `outlet.1.switch` -with linkman:upsrw[8]. -If your unit has multiple switchable outlets, substitute the outlet number for -'1' in the variable name. Be sure to test this first - there is no other way to -be certain that the number used by the driver matches the label on the unit. +Some Tripp Lite units have a switchable outlet (usually outlet #1) +which can be turned on and off by writing '1' or '0', respectively, +to `outlet.1.switch` with linkman:upsrw[8]. ++ +If your unit has multiple switchable outlets, substitute the outlet +number for '1' in the variable name. Be sure to test this first -- +there is no other way to be certain that the number used by the driver +matches the label on the unit. KNOWN ISSUES AND BUGS --------------------- @@ -147,16 +151,18 @@ same time, since they have different USB Product ID strings. If you have two SMART1500RM2U units, you would have to find which USB bus and device number each unit is on (via lsusb(8)). -Some of the SMART*2U models have an ID number, but because this ID is not -exposed as a USB string descriptor, there is no easy way to use this ID to -distinguish between multiple UPS units on a single machine. The UPS would need -to be claimed by the driver in order to read this ID. +Some of the SMART*2U models have a configurable Unit ID number, and you can now use the `upsid` +config argument to uniquely specify which UPS a given driver instance should control. +This allows for precise identification of UPS devices when multiple devices are connected. +To retrieve or set the upsid use the linkman:upsrw[8] utility. + +AUTHORS +------- + +Written by Charles Lepple, based on the linkman:tripplite[8] driver +by Rickard E. (Rik) Faith and Nicholas Kain. -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. +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 +172,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) - -INTERNET RESOURCES ------------------- +regex(7), lsusb(8) -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/tripplitesu.txt b/docs/man/tripplitesu.txt index 6416699a1c..a279a25827 100644 --- a/docs/man/tripplitesu.txt +++ b/docs/man/tripplitesu.txt @@ -3,20 +3,28 @@ TRIPPLITESU(8) NAME ---- + tripplitesu - Driver for Tripp-Lite SmartOnline (SU) UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*tripplitesu* -h + +*tripplitesu* -a 'UPS_NAME' ['OPTIONS'] + +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 +35,7 @@ until there are only a few seconds left. Common values are around 25--30. AUTHOR ------ + Allan N. Hessenflow SEE ALSO @@ -34,12 +43,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 41a194c306..2d81d0bd57 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -3,15 +3,17 @@ UPS.CONF(5) NAME ---- + ups.conf - UPS definitions for Network UPS Tools DESCRIPTION ----------- -This file is read by the driver controller linkman:upsdrvctl[8], the UPS drivers -that use the common core (see linkman:nutupsdrv[8], and linkman:upsd[8]). The -file begins with global directives, and then each UPS has a section which -contains a number of directives that set parameters for that UPS. +This file is read by the driver controller linkman:upsdrvctl[8], +the UPS drivers which use the common core (see linkman:nutupsdrv[8]), +linkman:nut-driver-enumerator[8], and the NUT data server linkman:upsd[8]. +The file begins with global directives, and then each UPS has a section +which contains a number of directives that set parameters for that UPS. A UPS section begins with the name of the UPS in brackets, and continues until the next UPS name in brackets or until EOF. The name "default" is @@ -39,6 +41,14 @@ In this case, the linkman:apcsmart[8] driver will receive variables called "cable" and "sdtype" which have special meanings. See the man pages of your driver(s) to learn which variables are supported and what they do. +Here is another example, when connecting a serial UPS on Windows: + + [windows-ups] + driver = mge-shut + port = "\\\\.\\COM10" + desc = "UPS on a Windows machine" + + GLOBAL DIRECTIVES ----------------- @@ -71,6 +81,9 @@ Optional. Specify to upsdrvctl to not wait at all for the driver(s) to execute the request command. + The default (omission) is to wait. ++ +It can be overridden by `NUT_IGNORE_NOWAIT` environment variable +(e.g. used to work around certain issues with systemd otherwise). *retrydelay*:: Optional. Specify the delay between each restart attempt of the driver(s), @@ -82,19 +95,20 @@ The default is 5 seconds. *pollinterval*:: Optional. The status of the UPS will be refreshed after a maximum -delay which is controlled by this setting. This is normally 2 seconds. This -may be useful if the driver is creating too much of a load on your system or -network. +delay which is controlled by this setting. This is normally 2 seconds. +This setting may be useful if the driver is creating too much of a load +on your monitoring system or network. + -Note that some drivers (such as linkman:usbhid-ups[8], linkman:snmp-ups[8] and -linkman:nutdrv_qx[8]) also have an option called *pollfreq* which controls how -frequently some of the less critical parameters are polled. Details are -provided in the respective driver man pages. +Note that some drivers (such as linkman:usbhid-ups[8], linkman:snmp-ups[8] +and linkman:nutdrv_qx[8]) also have an option called *pollfreq* which +controls how frequently some of the less critical parameters are polled. +Details are 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,28 +122,71 @@ 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 -need to specify the one that is compatible with your hardware. See -linkman:nutupsdrv[8] for more information on drivers in general and pointers to the -man pages of specific drivers. +Required. This specifies which program will be monitoring this UPS. +You need to specify the one that is compatible with your hardware. +See linkman:nutupsdrv[8] for more information on drivers in general +and pointers to the man pages of specific drivers. *port*:: -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'. +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'. +On Windows, the first serial port will be "\\\\.\\COM1" (note the +escaped slashes). + +*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*:: @@ -140,6 +197,13 @@ set this to -1. + The default value for this parameter is 0. +*allow_killpower*:: +Optional. This allows you to request `driver.killpower` instant command, +to immediately call the driver-specific default implementation of +`upsdrv_shutdown()` method, for same effect as when a NUT driver is +started with `-k` command-line flag. This option can be toggled with +linkman:upsrw[8] as `driver.flag.allow_killpower` during run-time. + *desc*:: Optional. This allows you to set a brief description that upsd will provide @@ -183,6 +247,14 @@ 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. + +Note that after this time `upsdrvctl` would just move along with its +business (whether retrying the same driver if `maxretry>1`, or trying +another driver if starting them all, or just eventually exit); however, +each such most recently started "stuck" driver process may be further +initializing in the background, and might even succeed eventually. ++ +They would not be actively killed by `upsdrvctl` after this timeout expires. ++ The default is 45 seconds. *synchronous*:: @@ -200,6 +272,21 @@ certain UPSes from working on Mac OS X. If your UPS requires explicitly setting the alternate interface, include this flag, and email the nut-upsdev list with details about your UPS and operating system. +*usb_config_index*:: +*usb_hid_rep_index*:: +*usb_hid_desc_index*:: +*usb_hid_ep_in*:: +*usb_hid_ep_out*:: + +Optional. Force use of specific interface, endpoint, descriptor index etc. +numbers, rather than defaulting to 0 (rarely other values in certain drivers +for some devices known to use non-zero numbers). Specified as a hexadecimal +number. ++ +As a rule of thumb for `usb_hid_desc_index` discovery, you can see larger +`wDescriptorLength` values (roughly 600+ bytes) in reports of `lsusb` or +similar tools. + *default.*:: Optional. Set a default value for which is used in case the UPS @@ -225,6 +312,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 +335,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsc.txt b/docs/man/upsc.txt index 11628867a9..1d4c8e4bda 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/ +The NUT (Network UPS Tools) home page: https://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..e9b17404d4 100644 --- a/docs/man/upscli_init.txt +++ b/docs/man/upscli_init.txt @@ -16,8 +16,9 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_init()* function initialize upsclient module and set many -SSL-related properties: 'certverify' to 1 makes certificate verification +TLS/SSL-related properties: 'certverify' to 1 makes certificate verification required for all SSL connections and 'certpath' is the location of certificate database. @@ -28,19 +29,28 @@ 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 to decrypt certificate private key. +If compiled with NSS, it would normally log either the infamous message +"Init SSL without certificate database" if no 'certpath' was provided, +or "Init SSL with certificate database located at %s" otherwise. +Since some programmatic consumers become confused by such extra text on +the `stderr` of tools they call (such as monitoring systems doing `upsc` +queries), you can export an environment variable `NUT_QUIET_INIT_SSL` +with string values "true", "TRUE" or "1", to avoid logging these messages +and just emit them as debug stream (at verbosity 1 or higher). + You can call linkman:upscli_add_host_cert[3] to register specific host security policy before initialize connections to them. @@ -53,6 +63,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..2f78415c84 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,9 +68,12 @@ 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], +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], linkman:upscli_getvar[3], linkman:upscli_list_next[3], linkman:upscli_list_start[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index c6c48992c8..b60cab9843 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 ------------------- -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upscode2.txt b/docs/man/upscode2.txt index b03763a84b..07618f2406 100644 --- a/docs/man/upscode2.txt +++ b/docs/man/upscode2.txt @@ -3,22 +3,30 @@ UPSCODE2(8) NAME ---- + upscode2 - Driver for UPScode II compatible UPS equipment -NOTE ----- -This man page only documents the hardware-specific features of the +SYNOPSIS +-------- + +*upscode2* -h + +*upscode2* -a 'UPS_NAME' ['OPTIONS'] + +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]: @@ -79,21 +87,24 @@ NOTES The Powerware UPS models that this driver has been tested against until now 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 +guesstimate 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 412d21ea1f..5597f483c5 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -48,6 +48,26 @@ unless the calling environment sets a same-named variable to enforce a value for the current run. One way this can happen is somebody un-commenting it in the 'nut.conf' file used by init-scripts and service unit method scripts. +"ALLOW_NOT_ALL_LISTENERS 'Boolean'":: + +Normally upsd requires that all `LISTEN` directives can be honoured at the +moment the daemon starts. If your LAN IP address (or host name) used in one +of the `LISTEN` directives may be not always accessible, and for some reason +do not want to just `LISTEN *` on the wildcard interface, but e.g. you still +want to use `upsmon` on `localhost`, this option can help. Note you would +have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +later. ++ +Boolean values 'true', 'yes', 'on' and '1' mean that the server would not +refuse to start if it can listen on at least one interface. ++ +Boolean values 'false', 'no', 'off' and '0' mean that the server should +refuse to start if it can not LISTEN on each and every (non-localhost) +interface found in upsd.conf. This is the default, unless the calling +environment sets a same-named variable to enforce a value for the current run. +One way this can happen is somebody un-commenting it in the 'nut.conf' file +used by init-scripts and service unit method scripts. + "STATEPATH 'path'":: Tell upsd to look for the driver state sockets in 'path' rather @@ -66,12 +86,18 @@ compiled into the code. This overrides any value you may have set with upsd will listen on port 3493 for this interface. + Multiple LISTEN addresses may be specified. The default is to bind to -127.0.0.1 if no LISTEN addresses are specified (and ::1 if IPv6 support is -compiled in). +`127.0.0.1` if no LISTEN addresses are specified (and also `::1` if IPv6 +support is compiled in). ++ +To listen on all available interfaces and configured IP addresses of your +system, you may also use `::` for IPv6 and `0.0.0.0` for IPv4, respectively. +As a special case, a single `LISTEN * ` directive (with an asterisk) will +try to listen on both IPv6 (`::0`) and IPv4 (`0.0.0.0`) wild-card IP addresses, +subject to `upsd` command-line arguments or system configuration. +Note that if the system supports IPv4-mapped IPv6 addressing per RFC-3493, +and does not allow to disable this mode, then there may be one listening +socket to handle both address families. + -To listen on all available interfaces, you may also use '0.0.0.0' for IPv4 and -and '::' for IPv6. - LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN myhostname.mydomain @@ -80,6 +106,12 @@ and '::' for IPv6. + This parameter will only be read at startup. You'll need to restart (rather than reload) upsd to apply any changes made here. ++ +Please note that older NUT releases could have been using the IPv4-mapped +IPv6 addressing (sometimes also known as "dual-stack") mode, if provided +by the system. Current versions (since NUT v2.8.1 release) explicitly try +to restrict their listening sockets to only support one address family on +each socket, and so avoid IPv4-mapped mode where possible. "MAXCONN 'connections'":: @@ -92,6 +124,7 @@ connections. Only set this if you know exactly what you're doing. When compiled with SSL support with OpenSSL backend, you can enter the certificate file here. ++ The certificates must be in PEM format and must be sorted starting with the subject's certificate (server certificate), followed by intermediate CA certificates (if applicable_ and the highest level (root) CA. It should @@ -102,6 +135,7 @@ NUT user manual for more information on the SSL support in NUT. When compiled with SSL support with NSS backend, you can enter the certificate path here. ++ Certificates are stored in a dedicated database (data split in 3 files). Specify the path of the database directory. @@ -110,22 +144,54 @@ Specify the path of the database directory. When compiled with SSL support with NSS backend, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key. ++ +NOTE: Be sure to enclose "certificate name" in double-quotes if you +are using a value with spaces in it. "CERTREQUEST 'certificate request level'":: When compiled with SSL support with NSS backend and client certificate validation (disabled by default, see 'docs/security.txt'), you can specify if upsd requests or requires client's' certificates. ++ Possible values are : - '0' to not request to clients to provide any certificate - '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 ------------------- -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 331a2fd066..d781bb6d7b 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. @@ -71,7 +87,19 @@ RELOADING upsd can reload its configuration files without shutting down the process if you send it a SIGHUP or start it again with `-c reload`. This only works -if the background process is able to read those files. +if the background process is able to read those files, and if the daemon did +save a PID file when it started. + +[NOTE] +====== +Service instances wrapped by systemd or SMF might not save them by default -- +use respective `reload`/`refresh` framework actions instead then), e.g. +`systemctl reload nut-server` + +NUT releases after 2.8.0 define aliases for these units, so if your Linux +distribution uses NUT-provided unit definitions, `systemctl reload upsd` +may also work. +====== If you think that upsd can't reload, check your syslog for error messages. If it's complaining about not being able to read the files, then you need @@ -122,6 +150,10 @@ linkman:ups.conf[5]. ENVIRONMENT VARIABLES --------------------- +*NUT_DEBUG_LEVEL* sets default debug verbosity if no *-D* arguments +were provided on command line, but does not request that the daemon +runs in foreground mode. + *NUT_CONFPATH* is the path name of the directory that contains `upsd.conf` and other configuration files. If this variable is not set, *upsd* uses a built-in default, which is often `/usr/local/ups/etc`. @@ -141,25 +173,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsd.users.txt b/docs/man/upsd.users.txt index 10bbdba262..2a0889f2db 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 ------------------- -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index da0593142d..446ed2d411 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -8,10 +8,13 @@ upsdrvctl - UPS driver controller SYNOPSIS -------- + *upsdrvctl* -h *upsdrvctl* ['OPTIONS'] {start | stop | shutdown} ['ups'] +*upsdrvctl* ['OPTIONS'] -c COMMAND ['ups'] + DESCRIPTION ----------- @@ -56,40 +59,110 @@ 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. +Note that this does not preclude the `upsdrvctl` tool from exiting after +its job is done (however an explicit *-F* option does). + +*-d*:: +Pass the selected debug level from `upsdrvctl` to launched drivers. +Note that by default for NUT daemons, enabled debugging means running +in foreground mode; you can specify *-B* additionally to avoid that. + +*-F*:: +Driver will run in the foreground (not fork away from the `upsdrvctl` +process), regardless of debugging settings. It would also keep the tool +program itself foregrounded with driver daemons running as its children +(in case of a single driver startup, it would not even fork). It would +also not wait for drivers to complete initialization, so `upsdrvctl` +will warn about such situations. +Specify twice (`-FF` or `-F -F`) to save the driver PID file even in +this mode (not saved by default when staying in foreground). + +*-B*:: +Drivers will run in the background, regardless of debugging settings, +as set by *-D* and passed-through by *-d* options. COMMANDS -------- -upsdrvctl supports three commands - start, stop and shutdown. They take -an optional argument which is a UPS name from linkman:ups.conf[5]. -Without that argument, they operate on every UPS that is currently -configured. +upsdrvctl supports three commands - start, stop and shutdown. +It also supports passing requests to running drivers using +`-c COMMAND` syntax, similar to that in some other daemons. + +They all can take an optional argument which is a UPS name from +linkman:ups.conf[5]. Without that argument, they operate on every +UPS that is currently configured. + +NOTE: `upsdrvctl` can not manage devices not listed in `ups.conf` +(such as test drivers started with `-s TMP` option). *start*:: Start the UPS driver(s). In case of failure, further attempts may be executed by using the 'maxretry' and 'retrydelay' options - see linkman:ups.conf[5]. *stop*:: -Stop the UPS driver(s). +Stop the UPS driver(s). This does not send commands to the UPS. *shutdown*:: -Command the UPS driver(s) to run their shutdown sequence. Drivers are -stopped according to their sdorder value - see linkman:ups.conf[5]. +Command the UPS driver(s) to run their shutdown sequence. This +assumes that the driver is no longer running, and starts a fresh +instance via "driver -k". It is intended to be used as the last step +in system shutdown, after the filesystems are no longer mounted rw. +Drivers are stopped according to their sdorder value - see +linkman:ups.conf[5]. WARNING: this will probably power off your computers, so don't play around with this option. Only use it when your systems are prepared to lose power. NOTE: refer to linkman:ups.conf[5] for using the *nowait* parameter. +It can be overridden by `NUT_IGNORE_NOWAIT` environment variable +(e.g. used to work around certain issues with systemd otherwise). + +*-c* 'command':: +Send 'command' to the background process as a signal. Valid commands +are: + + *dump*;; tell the driver(s) to dump currently known state + information to their `stdout` (if attached anywhere) + *reload*;; reread configuration files, ignoring modified settings + which can not be applied "on the fly" + *reload-or-error*;; reread configuration files, ignoring but counting + changed values which require a driver restart (can not be + changed on the fly), and return a success/fail code based + on that count, so the caller can decide the fate of the + currently running driver instance + *reload-or-exit*;; reread configuration files, exiting the old + driver process if it encounters modified settings + which can not be applied "on the fly" (so caller like + systemd can launch another copy of the driver) +///////// + *reload-or-restart*;; reread configuration files, causing the + old driver process to close the device connection + and re-exec itself if it encounters modified settings + which can not be applied "on the fly" (may fail for + critical changes like run-time user/group accounts) +///////// + +If the `upsdrvctl` was launched to remain in memory and manage NUT driver +processes, it can receive supported signals and pass them to those drivers. ENVIRONMENT VARIABLES --------------------- +*NUT_DEBUG_LEVEL* sets default debug verbosity if no *-D* arguments +were provided on command line, but does not request that the daemon +runs in foreground mode. + *NUT_CONFPATH* is the path name of the directory that contains -`upsd.conf` and other configuration files. If this variable is not set, -*upsdrvctl* the driver use a built-in default, which is often +`ups.conf` and other configuration files. If this variable is not set, +*upsdrvctl* (and the drivers) use a built-in default, which is often `/usr/local/ups/etc`. +*NUT_ALTPIDPATH* is the path name of the directory in which +*upsd* and drivers store .pid files. If this variable is not set, +*upsd* and drivers use either *NUT_STATEPATH* if set, or ALTPIDPATH if set, +or otherwise the built-in default *STATEPATH*. + DIAGNOSTICS ----------- @@ -100,8 +173,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index 9ad3eeaf94..e6e00de697 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'] @@ -150,6 +151,8 @@ play around with this option. Only use it when your systems are prepared to lose power. NOTE: refer to linkman:ups.conf[5] for using the *nowait* parameter. +It can be overridden by `NUT_IGNORE_NOWAIT` environment variable +(e.g. used to work around certain issues with systemd otherwise). ENVIRONMENT VARIABLES --------------------- @@ -193,11 +196,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsimage.cgi.txt b/docs/man/upsimage.cgi.txt index 9ede74a723..d9f6b636f7 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: https://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..1f0b64efc0 100644 --- a/docs/man/upslog.txt +++ b/docs/man/upslog.txt @@ -47,6 +47,9 @@ within this string are: %PID%;; insert the pid of upslog %VAR varname%;; insert the value of variable varname +(see NUT developer documentation chapter "Variables" on-line or in +the `docs/nut-names.txt` file in sources of the NUT version you have +installed for more details) The default format string is: @@ -65,19 +68,31 @@ 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 +upsname[@hostname[:port]]+. The default hostname is "localhost". +*-m* 'tuple':: +Monitor multiple UPSs. The format for this option is a tuple of +ups and logfile separated by commas. An example would be: +`upsname@hostname:9999,/var/log/nut/cps.log` + *-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 -program. This defaults to "nobody", which is less than ideal. +If 'username' is not defined, it will use the value that was compiled into +the program. This defaults to "nobody", which is less than ideal. SERVICE DELAYS -------------- @@ -108,13 +123,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 4551c95628..a50edb86f7 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -103,55 +103,63 @@ Each UPS that you need to be monitor should have a MONITOR line. Not all of these need supply power to the system that is running upsmon. You may monitor other systems if you want to be able to send notifications about status changes on them. - ++ You must have at least one MONITOR directive in `upsmon.conf`. - -'system' is a UPS identifier. It is in this form: - ++ +* 'system' is a UPS identifier. It is in this form: ++ +[@[:]]+ - ++ The default hostname is "localhost". Some examples: - - - "su700@mybox" means a UPS called "su700" on a system called "mybox". ++ + - "su700@mybox" means a UPS called "su700" on a system called "mybox". This is the normal form. - - "fenton@bigbox:5678" is a UPS called "fenton" on a system called + - "fenton@bigbox:5678" is a UPS called "fenton" on a system called "bigbox" which runs linkman:upsd[8] on port "5678". -'powervalue' is an integer representing the number of power supplies ++ +* 'powervalue' is an integer representing the number of power supplies that the UPS feeds on this system. Most normal computers have one power supply, and the UPS feeds it, so this value will be 1. You need a very large or special system to have anything higher here. - ++ You can set the 'powervalue' to 0 if you want to monitor a UPS that doesn't actually supply power to this system. This is useful when you want to have upsmon do notifications about status changes on a UPS without shutting down when it goes critical. -The 'username' and 'password' on this line must match an entry in ++ +* The 'username' and 'password' on this line must match an entry in the `upsd` server system's linkman:upsd.users[5] file. - ++ If your username is "observer" and your password is "abcd", the MONITOR line might look like this (likely on a remote secondary system): - -+MONITOR myups@bigserver 1 observer abcd secondary+ - ++ +---- +MONITOR myups@bigserver 1 observer abcd secondary +---- ++ Meanwhile, the `upsd.users` on `bigserver` would look like this: ++ +---- +[observer] + password = abcd + upsmon secondary - [observer] - password = abcd - upsmon secondary - - [upswired] - password = blah - upsmon primary - +[upswired] + password = blah + upsmon primary +---- ++ And the copy of upsmon on that bigserver would run with the primary configuration: ++ +---- +MONITOR myups@bigserver 1 upswired blah primary +---- -+MONITOR myups@bigserver 1 upswired blah primary+ - - -The 'type' refers to the relationship with linkman:upsd[8]. It can ++ +* The 'type' refers to the relationship with linkman:upsd[8]. It can be either "primary" or "secondary". See linkman:upsmon[8] for more information on the meaning of these modes. The mode you pick here also goes in the `upsd.users` file, as seen in the example above. @@ -163,6 +171,22 @@ reach any of the UPS entries in this configuration file. It keeps warning you until the situation is fixed. By default this is 300 seconds. +*POLLFAIL_LOG_THROTTLE_MAX* 'count':: + +upsmon normally reports polling failures for each device that are in place +for each POLLFREQ loop (e.g. "Data stale" or "Driver not connected") to +system log as configured. If your devices are expected to be AWOL for an +extended timeframe, you can use this throttle to reduce the stress on +syslog traffic and storage, by posting these messages only once in every +several loop cycles, and when the error condition has changed or cleared. ++ +A negative value means standard behavior (log on every loop, effectively +same as when `max=1`), and a zero value means to never repeat the message +(log only on start and end/change of the failure state). ++ +Note that this throttle only applies to one latest-active error state per +monitored device. + *NOTIFYCMD* 'command':: upsmon calls this to send messages when things happen. @@ -226,11 +250,26 @@ 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']...:: +NOPARENT;; `upsmon` parent process died - shutdown impossible + +CAL;; UPS calibration in progress + +NOTCAL;; UPS calibration finished + +OFF;; UPS administratively OFF or asleep + +NOTOFF;; UPS no longer administratively OFF or asleep + +BYPASS;; UPS on bypass (powered, not protecting) + +NOTBYPASS;; UPS no longer on bypass + +*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 -change this. +via `/bin/wall` and writes to the syslog when things happen. +Except for Windows where upsmon only writes to the syslog by default. +You can change this. + Examples: + @@ -293,6 +332,27 @@ Refer to the section: [[UPS_shutdown]] "Configuring automatic shutdowns for low battery events", or refer to the online version. +*OFFDURATION* 'seconds':: + +NUT supports an "administrative OFF" for power devices which can be managed to +turn off their application workload, while the UPS or ePDU remains accessible +for monitoring and management. This toggle allows to delay propagation of such +state into a known loss of a feed (possibly triggering FSD on `upsmon` clients +which `MONITOR` the device and are in fact still alive -- e.g. with multiple +power sources or because they as the load are not really turned off), because +when some devices begin battery calibration, they report "OFF" for a few seconds +and only then they might report "CAL" after switching all the power relays -- +thus causing false-positives for `upsmon` FSD trigger. ++ +A negative value means to disable decreasing the counter of working power +supplies in such cases, and a zero makes the effect of detected "OFF" state +immediate. Built-in default value is 30 (seconds), to put an "OFF" state into +effect (decrease known-fed supplies count) if it persists for this many seconds. ++ +NOTE: so far we support the device reporting an "OFF" state which usually +means completely un-powering the load; a bug-tracker issue was logged to +design similar support for just some manageable outlets or outlet groups. + *RBWARNTIME* 'seconds':: When a UPS says that it needs to have its battery replaced, upsmon will @@ -338,6 +398,36 @@ together, i.e.: SHUTDOWNCMD "/sbin/shutdown -h +0" +*SHUTDOWNEXIT* 'boolean|number':: + +After initiating shutdown, should this upsmon daemon itself exit? +By doing so NUT secondary systems can tell the NUT primary that +it can proceed with its own shutdown and eventually tell the UPS +to cut power for the load. ("Yes" by default) ++ +Some "secondary" systems with workloads that take considerable time +to stop (e.g. virtual machines or large databases) can benefit from +reporting (by virtue of logging off the data server) that they are +ready for the "primary" system to begin its own shutdown and eventually +to tell the UPS to cut the power - not as soon as they have triggered +their own shutdown, but at a later point (e.g. when the upsmon service +is stopped AFTER the heavier workloads). ++ +Note that the actual ability to complete such shutdown depends on the +remaining battery run-time at the moment when UPS power state becomes +considered critical and the shutdowns begin. You may also have to tune +`HOSTSYNC` on the NUT primary to be long enough for those secondaries to +stop their services. In practice, it may be worthwhile to investigate +ways to trigger shutdowns earlier on these systems, e.g. by setting up +`upssched` integration, or `dummy-ups` driver with overrides for stricter +`battery.charge` or `battery.runtime` triggers than used by the rest of +your servers. ++ +This option supports Boolean-style strings (yes/on/true or no/off/false) +or numbers to define a delay (in seconds) between calling `SHUTDOWNCMD` +and exiting the daemon. Zero means immediate exit (default), negative +values mean never exiting on its own accord. + *CERTPATH* 'certificate file or database':: When compiled with SSL support, you can enter the certificate path here. @@ -356,6 +446,9 @@ When compiled with SSL support, you can enter the certificate path here. When compiled with SSL support with NSS, you can specify the certificate name to retrieve from database to authenticate itself and the password required to access certificate related private key. ++ +NOTE: Be sure to enclose "certificate name" in double-quotes if you +are using a value with spaces in it. *CERTHOST* 'hostname' 'certificate name' 'certverify' 'forcessl':: @@ -365,6 +458,9 @@ for each server you can contact. Each entry maps server name with the expected certificate name and flags indicating if the server certificate is verified and if the connection must be secure. ++ +NOTE: Be sure to enclose "certificate name" in double-quotes if you +are using a value with spaces in it. *CERTVERIFY* '0 | 1':: @@ -393,10 +489,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 1478275fdd..afe4ad6692 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 @@ -145,6 +157,28 @@ The UPS needs to have its battery replaced. *NOCOMM*:: The UPS can't be contacted for monitoring. +*NOPARENT*:: +`upsmon` parent process died - shutdown impossible. + +*CAL*:: +UPS calibration in progress. + +*NOTCAL*:: +UPS calibration finished. + +*OFF*:: +UPS administratively OFF or asleep. + +*NOTOFF*:: +UPS no longer administratively OFF or asleep. + +*BYPASS*:: +UPS on bypass (powered, not protecting). + +*NOTBYPASS*:: +UPS no longer on bypass. + + NOTIFY COMMAND -------------- @@ -163,14 +197,15 @@ 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 ------------ By default, all notify events (see above) generate a global message -(wall) to all users, plus they are logged via the syslog. You can change -this with the NOTIFYFLAG directive in the configuration file: +(wall) to all users, plus they are logged via the syslog. +Except for Windows where upsmon only writes to the syslog by default. +You can change this with the NOTIFYFLAG directive in the configuration file: +NOTIFYFLAG+ 'notifytype' 'flags' @@ -377,6 +412,53 @@ by starting another copy of the program with `-c fsd` command line argument. This is useful when you want to initiate a shutdown before the critical stage through some external means, such as linkman:upssched[8]. +WARNING: Please note that 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 normally 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 also latch +or otherwise resurface the "FSD" state 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. + +When it is time to shut down, upsmon creates POWERDOWNFLAG to +communicate to the operating system that the UPS should be commanded +off late in the shutdown sequence. This file is removed if present +when upsmon starts, so that the next normal shutdown does not cause +the UPS to be turned off. (The file can't in general be removed +during shutdown because the filesystem might be read only. If the +file is in a RAM-backed filesystem, the it won't be present and the +check to remove it won't fire.) + +SIMULATING POWER FAILURES +------------------------- + +To test a synchronized shutdown without pulling the plug on your UPS(es), +you need only set the forced shutdown (FSD) flag on them. You can do this +by calling upsmon again to set the flag, i.e.: + ++upsmon -c fsd+ + +After that, the primary and the secondary will do their usual shutdown +sequence as if the battery had gone critical, while you can time how long +it takes for them. This is much easier on your UPS equipment, and it beats +crawling under a desk to find the plug. + +Note you can also use a dummy SHUTDOWNCMD setting to just report that the +systems would shut down at this point, without actually disrupting their work. + +WARNING: after such "dummy" experiments you may have to restart the NUT data +server `upsd` to clear its "FSD" flag for the devices and clients involved, +and make sure no files named by `POWERDOWNFLAG` option (e.g. `/etc/killpower`) +remain on the `upsmon primary` systems under test. + DEAD UPSES ---------- @@ -390,6 +472,19 @@ upsmon will alert you to a UPS that can't be contacted for monitoring with a "NOCOMM" notifier by default every 300 seconds. This can be changed with the NOCOMMWARNTIME setting. +Also upsmon normally reports polling failures for each device that are in place +for each POLLFREQ loop (e.g. "Data stale" or "Driver not connected") to +system log as configured. If your devices are expected to be AWOL for an +extended timeframe, you can use POLLFAIL_LOG_THROTTLE_MAX to reduce the +stress on syslog traffic and storage, by posting these messages only once +in every several loop cycles, and when the error condition has changed or +cleared. A negative value means standard behavior (log on every loop, +effectively same as when `max=1`), and a zero value means to never repeat +the message (log only on start and end/change of the failure state). + +Note that this throttle only applies to one latest-active error state per +monitored device. + RELOADING NUANCES ----------------- @@ -411,22 +506,16 @@ those values, you *must* stop upsmon and start it back up. upsmon will warn you in the syslog if you make changes to either of those values during a reload. -SIMULATING POWER FAILURES -------------------------- +ENVIRONMENT VARIABLES +--------------------- -To test a synchronized shutdown without pulling the plug on your UPS(es), -you need only set the forced shutdown (FSD) flag on them. You can do this -by calling upsmon again to set the flag, i.e.: +*NUT_DEBUG_LEVEL* sets default debug verbosity if no *-D* arguments +were provided on command line, but does not request that the daemon +runs in foreground mode. -+upsmon -c fsd+ - -After that, the primary and the secondary will do their usual shutdown -sequence as if the battery had gone critical, while you can time how long -it takes for them. This is much easier on your UPS equipment, and it beats -crawling under a desk to find the plug. - -Note you can also use a dummy SHUTDOWNCMD setting to just report that the -systems would shut down at this point, without actually disrupting their work. +*NUT_CONFPATH* is the path name of the directory that contains +`upsmon.conf` and other configuration files. If this variable is not set, +*upsmon* uses a built-in default, which is often `/usr/local/ups/etc`. FILES ----- @@ -438,17 +527,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 54a79f4e92..7e93d02d1f 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upssched.conf.txt b/docs/man/upssched.conf.txt index baa7f4f43b..f6ea00e457 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upssched.txt b/docs/man/upssched.txt index f15581e9ab..44cdb3d9ae 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. @@ -100,6 +101,25 @@ when there are none running. If the timer daemon isn't running, there are no timers to cancel, and furthermore there is no need to start a clock-watcher. So, it skips that step and exits sooner. +ENVIRONMENT VARIABLES +--------------------- + +*NUT_DEBUG_LEVEL* sets default debug verbosity if no *-D* arguments +were provided on command line, but does not request that the daemon +runs in foreground mode. + +NOTE: Unlike some other NUT daemons, `upssched` with enabled debug +does not stop reporting on `stderr`! It forks a background process +with the first call as an event handler, which exits soon after all +tracked timers have elapsed and were handled (if needed). + +*UPSNAME* and *NOTIFYTYPE* are required, as detailed above. They are +set by `upsmon` when it calls `upssched` as its choice of `NOTIFYCMD`. + +*NUT_CONFPATH* is the path name of the directory that contains +`upssched.conf` and other configuration files. If this variable is not set, +*upssched* uses a built-in default, which is often `/usr/local/ups/etc`. + FILES ----- @@ -112,4 +132,5 @@ linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsset.cgi.txt b/docs/man/upsset.cgi.txt index 79f5786d15..3bae6a1d5c 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsset.conf.txt b/docs/man/upsset.conf.txt index 2058a24907..cfb422928d 100644 --- a/docs/man/upsset.conf.txt +++ b/docs/man/upsset.conf.txt @@ -32,7 +32,7 @@ secure it. On Apache, you can use the .htaccess file or put the directives in your httpd.conf. It looks something like this, assuming the .htaccess -method: +method for older Apache releases: deny from all @@ -42,6 +42,19 @@ method: You will probably have to set "AllowOverride Limit" for this directory in your server-level configuration file as well. +Modern Apache enjoys a more detailed syntax, like this: + + ScriptAlias /upsstats.cgi /usr/share/nut/cgi/upsstats.cgi + ScriptAlias /upsset.cgi /usr/share/nut/cgi/upsset.cgi + + Options +Includes +ExecCGI + AllowOverride Limit + + Require local + Require ip aa.bb.cc.dd/nn + + + If this doesn't make sense, then stop reading and leave this program alone. It's not something you absolutely need to have anyway. @@ -55,8 +68,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsstats.cgi.txt b/docs/man/upsstats.cgi.txt index 141cfefb24..0c6435b539 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/upsstats.html.txt b/docs/man/upsstats.html.txt index 9d30841f10..91c4e7bf6e 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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 0677b6784a..323fe53deb 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -6,10 +6,14 @@ NAME usbhid-ups - Driver for USB/HID UPS equipment -NOTE ----- +SYNOPSIS +-------- + +*usbhid-ups* -h -This man page only documents the hardware-specific features of the +*usbhid-ups* -a 'UPS_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the usbhid-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -31,6 +35,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, @@ -44,13 +49,33 @@ This driver is known to work on: - most Linux systems, - FreeBSD (beta stage) and maybe other *BSD, - Darwin / Mac OS X, - - Solaris 10. + - Solaris 10 and illumos-based distributions. EXTRA ARGUMENTS --------------- This driver also supports the following optional settings: +include::nut_usb_addvars.txt[] + +*subdriver*='regex':: +Select the USB HID subdriver for the device manually, where automatic match +by device attributes alone does not suffice (e.g. new devices for which no +`vendorid`/`productid` pair was built into any driver -- but common USB HID +support is anticipated, or for different-capability devices with same +interface chips, notably "phoenixtec/liebert" and "mge"). ++ +Run the driver program with the `--help` option to see the exact list of +`subdriver` values it would currently recognize. ++ +NOTE: this option first checks for exact matches to subdriver identification +strings, such as `"TrippLite HID 0.85"` (which are prone to bit-rot), and if +there was no exact match -- retries with a case-insensitive extended regular +expression. ++ +NOTE: When using this option, it is mandatory to also specify the *vendorid* +and *productid* matching parameters. + *offdelay*='num':: Set the timer before the UPS is turned off after the kill power command is sent (via the *-k* switch). @@ -89,38 +114,65 @@ 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). -*vendor*='regex':: -*product*='regex':: -*serial*='regex':: -*vendorid*='regex':: -*productid*='regex':: - -Select a specific UPS, in case there is more than one connected via -USB. Each option specifies an extended regular expression (see -regex(7)) that must match the UPS's entire vendor/product/serial -string (minus any surrounding whitespace), or the whole 4-digit -hexadecimal code for vendorid and productid. Try *-DD* for -finding out the strings to match. +*onlinedischarge_battery*:: +If this flag is set, the driver will treat `OL+DISCHRG` status as +offline/on-battery. + -Examples: - - - `-x vendor="Foo.Corporation.*"` - - `-x vendorid=051d*` (APC) - - `-x product=".*(Smart|Back)-?UPS.*"` - -*bus*='regex':: - -Select a UPS on a specific USB bus or group of buses. The argument is -a regular expression that must match the bus name where the UPS is -connected (e.g. bus="002", bus="00[2-3]"). - -*device =* 'regex':: - -Select a UPS on a specific USB device or group of devices. The argument is -a regular expression that must match the device name where the UPS is -connected (e.g. device="001", device="00[1-2]"). -Note that device numbers are not guaranteed by the OS to be stable across -re-boots or device re-plugging. +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. + +*onlinedischarge*:: +DEPRECATED, old name for `onlinedischarge_battery` described above. + +*onlinedischarge_calibration*:: +If this flag is set, the driver will treat `OL+DISCHRG` status as calibration. +Some UPS models (e.g. APC were seen to do so) report `OL+DISCHRG` when they +are in calibration mode. This usually happens after a few seconds reporting +an `OFF` state as well, while the hardware is switching to on-battery mode. ++ +NOTE: If it takes so long on your device that a shutdown gets issued, you may +want to look at `upsmon` option `OFFDURATION` used to filter out temporary +values of "administrative OFF" as not a loss of a feed for the powered load. + +*onlinedischarge_log_throttle_sec*='num':: +Set the minimum frequency (in seconds) at which warnings would be emitted +for an otherwise not handled `OL+DISCHRG` device status combination. +Negative values disable sequentially repeated messages (when this state +appears and persists). ++ +If the device does not report `battery.charge`, the default value is 30 seconds +(fairly frequent, in case the UPS-reported state combination does reflect a +bad power condition and so the situation is urgent). ++ +If it does report `battery.charge`, by default the repeated notifications +would only be logged if this charge is different from when the message was +emitted previously (e.g. when the battery is really discharging). ++ +If both this option is set, and `battery.charge` is correctly reported, +either of these rules allow the notification to be logged. + +*onlinedischarge_log_throttle_hovercharge*='num':: +See details in `onlinedischarge_log_throttle_sec` and `battery.charge` based +log message throttling description above. This option adds a concept of UPS +"hovering" a battery charge at some level deemed safe for its chemistry, and +not forcing it to be fully charged all the time. As long as the current value +of `battery.charge` remains at or above this threshold percentage (default 100), +the `OL+DISCHRG` message logging is not triggered by variations of the charge. + +*disable_fix_report_desc*:: +Set to disable fix-ups for broken USB encoding, etc. which we apply by default +on certain models (vendors/products) which were reported as not following the +protocol strictly. This flag allows to disable the feature in particular device +configurations. ++ +It is always possible that the vendors eventually release fixed firmware, or +re-use identifiers by which we match suspected broken devices for unrelated +products, so processing these fix-ups would be a waste of time there. ++ +It is also always possible that NUT fix-ups cause issues on some devices, +whether due to NUT bugs or because the vendor protocol implementation is +broken in more than one place. *explore*:: With this option, the driver will connect to any device, including @@ -142,6 +194,18 @@ INPUT flagged objects. Some Powercom units need this option. Limit the number of bytes to read from interrupt pipe. For some Powercom units this option should be equal to 8. +*waitbeforereconnect*='num':: +The driver automatically tries to reconnect to the UPS on unexpected error. +This parameter (in seconds) allows it to wait before attempting the reconnection. +The default value is 0. ++ +NOTE: for instance, it was found that Eaton MGE Ellipse Max 1500 FR UPS firmware +stops responding every few hours, which causes usbhid-ups driver to detect an +libusb insufficient memory error; in this case, when the usbhid-ups driver tries +to reconnect too early, the activity sometimes led the UPS firmware to crash and +turn off the load immediately! Setting this parameter to 30 seconds solved this +problem (while 20 seconds were not enough). + INSTALLATION ------------ @@ -161,11 +225,11 @@ IMPLEMENTATION Selecting a specific UPS ~~~~~~~~~~~~~~~~~~~~~~~~ -The driver ignores the "port" value in *ups.conf*. Unlike previous -versions of this driver, it is now possible to control multiple UPS -units simultaneously with this driver, provided they can be distinguished -by setting some combination of the "vendor", "product", "serial", -"vendorid", and "productid" options. For instance: +As mentioned above, the driver ignores the "port" value in *ups.conf*. +Unlike previous versions of this driver, it is now possible to control +multiple UPS units simultaneously with this driver, provided they can +be distinguished by setting some combination of the device-matching options. +For instance: [mge] driver = usbhid-ups @@ -236,6 +300,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 +337,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/man/victronups.txt b/docs/man/victronups.txt index 05be10c1b6..c9a113ffe2 100644 --- a/docs/man/victronups.txt +++ b/docs/man/victronups.txt @@ -3,16 +3,23 @@ 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 +SYNOPSIS +-------- + +*victronups* -h + +*victronups* -a 'UPS_NAME' ['OPTIONS'] + +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 +32,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 +50,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/ + +The NUT (Network UPS Tools) home page: https://www.networkupstools.org/ diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 6186b6e1bb..222180f106 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -42,15 +42,22 @@ NUT network protocol, over the time: |Protocol version |NUT version |Description |1.0 |< 1.5.0 |Original protocol (legacy version) |1.1 |>= 1.5.0 |Original protocol (without old commands) -.2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands +.2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENT" 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. +ERRATA: Earlier revisions of this table mistakenly mentioned `LIST CLIENTS` +as added since 2.6.4. The actual added command was `LIST CLIENT` (no `S`) +as documented in its section below. GET --- @@ -489,8 +496,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 +508,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 +631,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..48cff0660e 100644 --- a/docs/new-clients.txt +++ b/docs/new-clients.txt @@ -40,7 +40,7 @@ functions documentation referenced in the same file. Clients like upsc are provided as examples of how to retrieve data using the upsclient functions. -link:http://www.networkupstools.org/projects.html[Other programs] not included +link:https://www.networkupstools.org/projects.html[Other programs] not included in this package may also use this library, such as wmnut. High level library: libnutclient @@ -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/new-drivers.txt b/docs/new-drivers.txt index 7e173720e4..8503a38287 100644 --- a/docs/new-drivers.txt +++ b/docs/new-drivers.txt @@ -95,7 +95,7 @@ This structure tracks several description information about the driver: - DRV_COMPLETE: this is the gold level! It implies that 100 % of the protocol is implemented, and a full QA pass. * *subdrv_info*: array of upsdrv_info_t for sub driver(s) information. For - example, this is used by usbhid-ups. + example, this is used by usbhid-ups. This information is currently used for the startup banner printing and tests. @@ -159,6 +159,13 @@ fails. If the UPS does not shut down the load, then the user is vulnerable to a race if the power comes back on during the shutdown process. +This method should not directly `exit()` the driver program (neither +should it call `fatalx()` nor `fatal_with_errno()` methods). It can +`upslogx(LOG_ERR, ...)` or `upslog_with_errno(LOG_ERR, ...)`, and then +`set_exit_flag(N)` if required (`-1` for `EXIT_FAILURE` and `-2` for +`EXIT_SUCCESS` which would be handled in the standard driver loop or +`forceshutdown()` method of `main.c`). + Data types ---------- @@ -312,21 +319,41 @@ Serial port handling -------------------- Drivers which use serial port functions should include serial.h and use -these functions whenever possible: +these functions (and cross-platform data types) whenever possible: + + - TYPE_FD + +Cross-platform data type to represent a serial-port connection. + + - ERROR_FD_SER + +Macro value representing an invalid serial-port connection. + + - VALID_FD_SER(TYPE_FD_SER fd) + +This macro evaluates to `true` if `fd` currently has a "valid" value +(e.g. represents a connected device). You should invalidate the `fd` +when you initialize the variable or close the connection, by assigning +`fd = ERROR_FD`. + + - INVALID_FD_SER(TYPE_FD_SER fd) + +This macro evaluates to `true` if `fd` does not currently have a +"valid" value. - - int ser_open(const char *port) + - TYPE_FD_SER ser_open(const char *port) This opens the port and locks it if possible, using one of fcntl, lockf, or uu_lock depending on what may be available. If something fails, it calls fatal for you. If it succeeds, it always returns the fd that was opened. - - int ser_open_nf(const char *port) + - TYPE_FD_SER ser_open_nf(const char *port) This is a non-fatal version of ser_open(), that does not call fatal if something fails. - - int ser_set_speed(int fd, const char *port, speed_t speed) + - int ser_set_speed(TYPE_FD_SER fd, const char *port, speed_t speed) This sets the speed of the port and also does some basic configuring with tcgetattr and tcsetattr. If you have a special serial @@ -337,38 +364,38 @@ a useful error message. This is the only place that will generate a message if someone passes a non-serial port /dev entry to your driver, so it needs the extra detail. - - int ser_set_speed_nf(int fd, const char *port, speed_t speed) + - int ser_set_speed_nf(TYPE_FD_SER fd, const char *port, speed_t speed) This is a non-fatal version of ser_set_speed(), that does not call fatal if something fails. - - int ser_set_dtr(int fd, int state) - - int ser_set_rts(int fd, int state) + - int ser_set_dtr(TYPE_FD_SER fd, int state) + - int ser_set_rts(TYPE_FD_SER fd, int state) These functions can be used to set the modem control lines to provide cable power on the RS232 interface. Use state = 0 to set the line to 0 and any other value to set it to 1. - - int ser_get_dsr(int fd) - - int ser_get_cts(int fd) - - int ser_get_dcd(int fd) + - int ser_get_dsr(TYPE_FD_SER fd) + - int ser_get_cts(TYPE_FD_SER fd) + - int ser_get_dcd(TYPE_FD_SER fd) These functions read the state of the modem control lines. They will return 0 if the line is logic 0 and a non-zero value if the line is logic 1. - - int ser_close(int fd, const char *port) + - int ser_close(TYPE_FD_SER fd, const char *port) This function unlocks the port if possible and closes the fd. You should call this in your upsdrv_cleanup handler. - - ssize_t ser_send_char(int fd, unsigned char ch) + - ssize_t ser_send_char(TYPE_FD_SER fd, unsigned char ch) This attempts to write one character and returns the return value from write. You could call write directly, but using this function allows for future error handling in one place. - - ssize_t ser_send_pace(int fd, useconds_t d_usec, + - ssize_t ser_send_pace(TYPE_FD_SER fd, useconds_t d_usec, const char *fmt, ...) If you need to send a formatted buffer with an intercharacter delay, use @@ -380,22 +407,22 @@ one. The return value is the number of characters that was sent to the port, or -1 if something failed. - - ssize_t ser_send(int fd, const char *fmt, ...) + - ssize_t ser_send(TYPE_FD_SER fd, const char *fmt, ...) Like ser_send_pace, but without a delay. Only use this if you're sure that your UPS can handle characters at the full line rate. - - ssize_t ser_send_buf(int fd, const void *buf, size_t buflen) + - ssize_t ser_send_buf(TYPE_FD_SER fd, const void *buf, size_t buflen) This sends a raw buffer to the fd. It is typically used for binary transmissions. It returns the results of the call to write. - - ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, + - ssize_t ser_send_buf_pace(TYPE_FD_SER fd, useconds_t d_usec, const void *buf, size_t buflen) This is just ser_send_buf with an intercharacter delay. - - ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec) + - ssize_t ser_get_char(TYPE_FD_SER fd, void *ch, time_t d_sec, useconds_t d_usec) This will wait up to d_sec seconds + d_usec microseconds for one character to arrive, storing it at ch. It returns 1 on success, -1 @@ -406,7 +433,7 @@ back to the usual idle loop in main in time to answer the PINGs from upsd. That will cause an oscillation between staleness and normal behavior. - - ssize_t ser_get_buf(int fd, void *buf, size_t buflen, + - ssize_t ser_get_buf(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) Like ser_get_char, but this one reads up to buflen bytes storing all of @@ -415,7 +442,7 @@ returns the number of bytes read, -1 on failure and 0 on a timeout. This is essentially a single read() function with a timeout. - - ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, + - ssize_t ser_get_buf_len(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) Like ser_get_buf, but this one waits for buflen bytes to arrive, @@ -426,7 +453,7 @@ and 0 on a timeout. This should only be used for binary reads. See ser_get_line for protocols that are terminated by characters like CR or LF. - - ssize_t ser_get_line(int fd, void *buf, size_t buflen, + - ssize_t ser_get_line(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, const char *ignset, time_t d_sec, useconds_t d_usec) @@ -456,7 +483,7 @@ there. Your driver will get `"OK"`, and the rest is gone forever. This also means that you should not "pipeline" commands to the UPS. Send a query, then read the response, then send the next query. - - ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, + - ssize_t ser_get_line_alert(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler(char ch), @@ -471,7 +498,7 @@ Implementation note: this function actually does all of the work, and ser_get_line is just a wrapper that sets an empty alertset and a NULL handler. - - ssize_t ser_flush_in(int fd, const char *ignset, int verbose) + - ssize_t ser_flush_in(TYPE_FD_SER fd, const char *ignset, int verbose) This function will drain the input buffer. If verbose is set to a positive number, then it will announce the characters which have been @@ -482,7 +509,7 @@ This function returns the number of characters which were read, so you can check for extra bytes by looking for a nonzero return value. Zero will also be returned if the read fails for some reason. - - int ser_flush_io(int fd) + - int ser_flush_io(TYPE_FD_SER fd) This function drains both the in- and output buffers. Return zero on success. diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 8b4bd252d4..2149fccdbc 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -4,9 +4,10 @@ NUT variable names and instant commands endif::external_title[] [NOTE] -.RFC xxxx Recording Document +.RFC 9271 Recording Document ==== -This document is defined by (pending) RFC xxxx and is referenced as the +This document is defined by RFC 9271 published by IETF at +https://www.rfc-editor.org/info/rfc9271 and is referenced as the document of record for the variable names and the instant commands used in the protocol described by the RFC. @@ -17,7 +18,15 @@ as used in commands and messages between the Attachment Daemon (the `upsd` in case of NUT implementation of the standard) and the clients. ==== -This document defines the standard names of NUT commands and variables. +This document defines the standard names of NUT commands and variables +(not to be confused with <<_status_data,device status data>> described in +ifdef::website[] +another chapter). +endif::website[] +ifndef::website[] +the `docs/new-drivers.txt` in NUT source codebase). +endif::website[] + Developers should use the names recorded here, with dstate functions and data mappings provided in NUT drivers for interactions with power devices. @@ -34,11 +43,49 @@ a better name later, clients that already use the undocumented variable will break when it is eventually changed. An explicitly "experimental" data point is less surprising in this regard. +Similarly, some source files (`drivers/*-mib.c` and `drivers/*-hid.c`) +may mention data point names following the pattern of `unmapped.x.y`. +These are generated by helper scripts which walk the reports from SNMP +and USB HID devices, respectively `scripts/subdriver/gen-snmp-subdriver.sh` +and `scripts/subdriver/gen-usbhid-subdriver.sh`, and assign names based on +strings in those reports. The `unmapped` entries should not be exposed in +"production" builds of the NUT drivers. They are an aid for developers to +know that such entries are served by their device, so an existing standard +NUT name can be assigned for the concept (or new name negotiated with the +community), but are normally hidden with `#if WITH_UNMAPPED_DATA_POINTS` +clauses which can be enabled in custom NUT builds by use of +`./configure --with-unmapped-data-points` option. + NOTE: In the descriptions, "opaque" means programs should not attempt to parse the value for that variable as it may vary greatly from one UPS (or similar device) to the next. These strings are best handled directly by the user. +Structured naming +----------------- + +All standard NUT names of variables and commands are structured, with +a certain domain-specific prefix and purpose-specific suffix parts. +NUT tools provide and interpret them as dot-separated strings (although +third-party tools might restructure them by cutting and pasting at the +dot separation location, e.g. to represent as a JSON data tree or as +data model classes for specific programming languages). + +If you would be making a parser of this information, please do also note +that in some *but not all* cases there is a defined data point for some +reading or command at the "root level" of what evolved to be a collection +of further structured related information (and there are no guarantees +for future evolution in this regard), for example: + +* an `input.voltage` reports the momentary voltage level value and + there is a `input.voltage.maximum` for a certain related detail; +* conversely, there are several items like `input.transfer.reason` + but there is no actual `input.transfer` report. + +There may be more layers than two (e.g. `input.voltage.low.warning`), +and in certain cases detailed below there may be a variable component +in the practical values (e.g. the `n` in `ambient.n.temperature.alarm` +variable or `outlet.n.load.off` command names). Time and Date format -------------------- @@ -87,11 +134,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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -99,7 +149,7 @@ ups: General unit information [options="header"] |=============================================================================== | Name | Description | Example value -| ups.status | UPS status | linkdoc:developer-guide[OL,_status_data] +| ups.status | UPS status | <<_status_data,OL>> | ups.alarm | UPS alarms | OVERHEAT | ups.time | Internal UPS clock time (opaque string) | 12:34 @@ -172,10 +222,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 +295,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 +328,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 +354,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 +365,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 @@ -330,6 +386,10 @@ Valid CONTEXTs Valid SPECs ^^^^^^^^^^^ +NOTE: For cursory readers -- the following couple of tables lists just the +short `SPEC` component of the larger `DOMAIN.CONTEXT.SPEC` naming scheme +for phase-aware values, as discussed in other sections of this chapter. + Valid with/without context (i.e. per phase or aggregated/averaged) [options="header"] @@ -470,15 +530,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 +718,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 @@ -704,6 +766,18 @@ driver: Internal driver information cmdline -x) setting | (varies) | driver.flag.xxx | Flag xxx (ups.conf or cmdline -x) status | enabled (or absent) +| driver.state | Current state in driver's + lifecycle, primarily to help + readers discern long-running + init (with full device walk) + or cleanup stages from + the stable working loop | init.starting, init.quiet, + init.device, init.info, + init.updateinfo (first walk), + reconnect.trying, + reconnect.updateinfo, + updateinfo, quiet, dumping, + cleanup.upsdrv, cleanup.exit |=============================================================================== server: Internal server information @@ -713,7 +787,7 @@ server: Internal server information |=============================================================================== | Name | Description | Example value | server.info | Server information | Network UPS Tools upsd vX.Y.Z - - http://www.networkupstools.org/ + https://www.networkupstools.org/ | server.version | Server version | X.Y.Z |=============================================================================== 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..95cc070421 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,5 +1,7 @@ -personal_ws-1.1 en 2918 utf-8 +personal_ws-1.1 en 3508 utf-8 +AAC AAS +ABI ACFAIL ACFREQ ACK @@ -10,6 +12,7 @@ ACrms ADDR ADDRCONFIG ADDRINFO +ADELSYSTEM ADK ADKK AEC @@ -35,6 +38,8 @@ ATT ATTRS ATX ATs +AUTOCOMMIT +AUTOPUSH AVL AVR AVRLCD @@ -56,7 +61,6 @@ AlmCPol AlmEnbl Amplon AmpĆØre -Amplon Andreas Andreassen Andrzej @@ -64,6 +68,7 @@ Angelone Antonino Apodaca AppData +AppVeyor Arjen Arkadiusz Armin @@ -74,6 +79,7 @@ AsciiDoc Asium Ates AudibleAlarmControl +AuthConfig AutoFrq AutoMsg AutoRst @@ -81,6 +87,7 @@ Autobook Autoconfigure Autostartup Avocent +Axel Axxium BATGNn BATNn @@ -123,11 +130,13 @@ BattTstFail BatteryA BatteryB BaudRt +BaulĆ© BayTech BeepTone Belkin's Benedikt Berge +Berzonis BestPort BiWeekly Bieringer @@ -154,6 +163,7 @@ ByPass CA's CABAC CAs +CBI CBLimit CCC CCCC @@ -166,7 +176,9 @@ CERTIDENT CERTREQUEST CERTVERIF CEST +CHOST CHRG +CL CLANGVER CLI CLOCAL @@ -183,11 +195,13 @@ CPAN CPE CPM CPP +CPPDBG CPPFLAGS CPUs CRC CREAD CROSSTALK +CSN CSS CSUM CTB @@ -204,10 +218,12 @@ ChangeLog ChargdV Chatziathanassiou CheckUPS +CheckUPSAvailable Checksum Chiou Chu Cichowski +CircleCI Claesson CodingStyle Collver @@ -225,12 +241,17 @@ Ctrl Cuvellard Cyber CyberPower +CyberShield +CygWin Cygwin DATACABLE DATAPATH DCE +DCF +DCO DDD DDDDD +DDDDDD DDF DDThh DEADTIME @@ -238,47 +259,70 @@ DEBUGOUT DELCMD DELENUM DELINFO +DELPHYS DELRANGE DES DESTDIR +DEVNAME DF DHEA +DIGYS DISCHRG +DLDIR +DLLs DMF DMFs DN +DNS DOCTYPE DOMAINs DPC +DPURIFY DRIVERLIST DS DSA DSHUTD DSL DTE +DTrace DUMPALL DUMPDONE DWAKE DWITH DX Daniele +Dashjr DataRoom Dbnc +Ddtrace DeepTstFail Defensor DeltaUPSv +DesktopFileName DeviceID DeviceKit +DeviceLogin +DeviceLogout +Dgtk Dharm DiSplay Diehl Dietze +DigitalOcean Digitus +Digys Dimitris +Dinstalled +Disassembly +Dlibelf Dly +Dman Dmitry DocBook +DocumentRoot Doxygen +DriverInstaller +Dsystemtap Dynamix Dynex EAGAIN @@ -288,9 +332,11 @@ EG EL ELCD EMI +EMP EMPDT ENDFOR ENV +EOC EOF EOLed EPEL @@ -305,6 +351,7 @@ ETIME EUROCASE EXtreme Economou +EditorConfig Edlman Edmundsson Edscott @@ -327,15 +374,21 @@ Erikson Eriksson Evgeny Exar +ExecCGI +ExecStartPre +FD FEMEA FFF FH FHS +FILEPATH FINALDELAY FIPS FIXME +FMRI FO FOREACHUPS +FOSS FPT FREEADDRINFO FREHn @@ -346,6 +399,7 @@ FTS FTTx FTUPS FV +FWIW Faber Fabio Fabrice @@ -369,15 +423,23 @@ FreqSens Frolov FullLoad FuƟ +GC GCCVER GES GETADDRINFO +GID GKrellM GND +GNUmakefile +GObject +GPIO +GPIOCHIP GPL GPSER GRs GTK +GUESSTIMATION +GUID GUIs GWD GXE @@ -388,8 +450,13 @@ Gammons Gandi Gaspar Gathman +Gembe GenTestFail Gert +GetRWVars +GetUPSCommands +GetUPSList +GetUPSNames GetUPSVars Ghali Giese @@ -398,12 +465,14 @@ GitHub GitHub's Gnd Gnomovision +GnuTLS Goebl Golang Gomes Goncalves Gordeev Gough +Grafana Grafenthal Gtec GuardEnd @@ -412,14 +481,18 @@ GuideBook Guillen HB HC +HDD HEADs HEHn HELn HFILE HIDIOCINITREPORT +HIDRDD HITRANS +HL HMAC HNX +HOMEBREW HOSTLINK HOSTSYNC HOWTO @@ -440,18 +513,27 @@ Hessenflow HiBox HiFreq HighBatt +Hirschler Hlavinka Holger +HomeKit +Homebrew +Homebridge Hoogervorst Hough +Hrusecky Hunnox Hurd HĆ„vard IANA +IC ID's +IDE IDEN +IDEs IDentifiers IFBETWEEN +IFF IFSUPP IGN IMG @@ -468,6 +550,7 @@ INVOLT IPAR IPBX IPC +IPM IPP IPSS IPs @@ -478,10 +561,12 @@ ITEMP ITy IVT IZ +ImageFiles Infosec Innova Integrators IntelCC +IntelliJ InvCDly InvCPol InvMin @@ -491,10 +576,13 @@ Invter IoT Ioannou JAWAN +JBUS JBus +JK JKL JRE JSON +JVM JW Jageson Jarosch @@ -504,12 +592,14 @@ Javadoc Javascript Jenkinsfile JoinControllers +Jong Joon Jumpered KNutClient KNutSetting KOLFF KRT +KRTL KRTS KTTS Kain @@ -543,6 +633,8 @@ LDADD LDFLAGS LDLC LDRIVER +LDSHARED +LDSHAREDLIBC LEDs LGTM LH @@ -559,8 +651,11 @@ LINEV LISTINSTCMD LISTRW LISTVARS +LLDB +LLNC LOADPCT LOCKFN +LOCKNAME LOTRANS LUA LVM @@ -574,13 +669,18 @@ Laventhol Legrand Lepple Levente +LibGD LibLTDL LibNEON +LibUSB LineA LineB LineSens Lintian +ListClients Lite's +LogMax +LogMin LowBatt LowFreq LowRntm @@ -600,12 +700,16 @@ MAXPARMAKES MBATTCHG MCOL MCU +MDigest +MEC MEGATAEC MH MIBs +MIMode MINLINEV MINSUPPLIES MINTIMEL +MKDIRPROG MLH MMM MMMMMMMMMMMMMMM @@ -614,9 +718,13 @@ MONITORed MOXA MPSU MQ +MQTT MSI MSII MSIII +MSVCRT +MSYS +MX MacKenzie's MacOS Maccelari @@ -656,11 +764,14 @@ Milkov MinACVI MinACVO MinDCV +MinGW MiniCOL MiniGuard Minislot Moar Modbus +ModemManager +Modris MonAMI MonUPS Monett @@ -672,6 +783,7 @@ Mozilla Msg MultiLink Multiplug +Munin MyCompany MyPasSw MySQL @@ -680,6 +792,7 @@ NAK NAS NBF NConfigs +NDE NETVER NETVERSION NFS @@ -693,9 +806,11 @@ NNNNNNNN NNNNNNNNNN NNNNNNNNNNNNNNNNNNN NOAUTH +NOBROADCAST NOCOMM NOCOMMWARNTIME NOCONF +NOGET NOMBATTV NOMINV NOMOUTV @@ -704,15 +819,20 @@ NONBLOCK NONUT NOP NOPARENT +NOTBYPASS +NOTCAL NOTIFYCMD NOTIFYFLAG NOTIFYFLAGS NOTIFYMSG +NOTOFF NQA NTP NUT's NUTCONF +NUTClient NUTSRC +NUTServer NVA NX Nadav @@ -721,21 +841,27 @@ Nash NaturalDocs Necedah NetBSD +NetBeans +NetInvent NetPro NetServer NetUps Netman +NetworkUPSTools Neus Niels Niklas Niro Nobreaks +NodeJS Nom NomDCV NomVIn NomVOut +NotePad Novell NrLoBatt +NuGet NutException Nxx OAH @@ -744,6 +870,7 @@ OC ODH OEM OEM'ed +OFFDURATION OID OIDs OLHVT @@ -752,8 +879,11 @@ OMNIVSINT ONF ONV OOM +OSABI +OSC OSF OSs +OUTDIR OUTPUTV OUTVOLT OV @@ -773,6 +903,7 @@ OnTmDays OneAC OpenBSD OpenIndiana +OpenPGP OpenSSL OpenSolaris OpenSource @@ -782,8 +913,10 @@ Opengear's Opensource Opti OptiUPS +Orsiris Orvaldi Orzechowski +OutletSystem OutputOverload OvrLds PBT @@ -791,6 +924,7 @@ PBTn PBTnn PC PC's +PCI PDC PDUs PDX @@ -807,12 +941,15 @@ PIPEFN PLD PLL PLVn +POLLFAIL POLLFREQALERT POMode POSIX POWERDOWNFLAG POWEREX POWERLINE +POWERSTATE +PPA PPD PPDn PPDnnn @@ -820,7 +957,9 @@ PPP PPPPPPPPPP PR PR'ed +PROGRA PROGS +PROTVER PRs PSA PSD @@ -831,12 +970,16 @@ PSSENTR PSUs PSW PSX +PThreads PULS PV PWLv PWR +PXG +PYTHONPATH PaaS Pac +PackageRequired Parisi Patrik Pavel @@ -896,11 +1039,18 @@ PresentStatus Priv Procomm ProductID +Progra +Proxmox Prynych +Pulizzi PwrOut +PyDOC PyGTK PyNUT PyNUTClient +PyNUTError +PyPI +PyQt QBDR QBT QBV @@ -927,6 +1077,7 @@ QSKTn QSKn QVFW QWS +QinHeng Quette RAIDiator RBWARNTIME @@ -935,9 +1086,12 @@ RDNT RDWR README REDi +REFREPO REPLBATT REQSSL +RESPIN RETPCT +REXX RK RMCARD RMCPplus @@ -973,6 +1127,7 @@ Rene RenĆ© Repotec's Repoteck +RequireAny Richthof Rickard Ridgway @@ -985,22 +1140,27 @@ RodrĆ­guez Rouben Rozman Rucelf +RunUPSCommand +RuntimeWarning RxD RxHs Ryabov SAI +SASU SCHEMADIR SCM SCO SCR SDA SDE +SDFLAG SDR SDRnnnnn SDT SELFTEST SELinux SENTR +SER SERIALNO SERVER's SETFL @@ -1012,13 +1172,18 @@ SG SGI SHA SHUTDOWNCMD +SHUTDOWNEXIT +SHUTDOWNSCRIPT SIG SIGHUP SIGINT +SIGKILL SIGPIPE SIGPWR SIGTERM +SIGURG SIGUSR +SIGWINCH SKOFFn SKONn SKP @@ -1033,11 +1198,14 @@ SMTP SMX SNMPv SNR +SOCK SOCKADDR SOCKLEN SOFF SOLA SOLA's +SOMECO +SOURCEMODE SOV SPARC SPC @@ -1048,8 +1216,10 @@ SRC SSSS STARTTLS STB +STDCALL STESTI STI +STIME STO STP SUNWlibusbugen @@ -1057,9 +1227,11 @@ SUNWugen SUNWusb SURTD SUSE +SVR SX SXI SXL +SYMLINKDIR SafeNet Salicru Salvia @@ -1070,30 +1242,39 @@ Schmier Schoch Schonefeld Schroder +ScriptAlias Sekury Selinger SendEnv Senoidal Sep Sequentializing +SerialNumber Serv +Serveur +SetRWVar Shara +ShareAlike Shaul ShdnDbnc ShdnDly ShdnPol +ShutDown Shutdn Sibbald Sicon Sidorov +SigLevel Signetic Silvino Sinline Sistem Sistemas +SlackPack Slackware SmartBoost SmartCell +SmartNUT SmartOnline SmartPro SmartSlot @@ -1108,6 +1289,8 @@ Solaris Soltec Soltys SomeVendor +Sotirov's +SourceForge Soyntec Spanier Spiros @@ -1123,6 +1306,7 @@ Sublicensing SunOS SuperPower Sweex +Sy Sycon Symmetra Symmetras @@ -1132,6 +1316,7 @@ Sysgration SyslogIdentifier SystemIO Systeme +Syu Szady TBD TBR @@ -1142,6 +1327,7 @@ TEMPC TEMPF TESTEDFILE TESTEDINDEX +TGS TIMELEFT TIOCM TIOCMBIC @@ -1157,6 +1343,7 @@ TST TT TTT TXF +TXG TXV TXVxx TapSwDly @@ -1178,9 +1365,12 @@ Theodor Thierry Tigra Tnn +ToddGreenfield Tomek +Toolset TopGuard Toth +Traceback TrackingID TrackingResult Tripp @@ -1192,6 +1382,7 @@ TxHS UB UBD UBR +UCRT UDP UHV UI @@ -1199,6 +1390,7 @@ UID UIDA UINT UNKCOMMAND +UNSTASH UNV UPGUARDS UPM @@ -1210,11 +1402,18 @@ UPSDESC UPSHOST UPSIMAGEPATH UPSLC +UPSNOTIFY +UPSName +UPSOutletSystemOutletDelayBeforeReboot +UPSOutletSystemOutletDelayBeforeShutdown +UPSOutletSystemOutletDelayBeforeStartup +UPSOutletSystemOutletSwitchable UPSSTATSPATH UPSTEMP UPScode UPSes UPSilon +UPSmart UPSmon UPSonic UPSs @@ -1229,6 +1428,7 @@ USVs UTC UTalk UUU +UUUU UX Ubuntu Ulf @@ -1246,11 +1446,13 @@ VER VERFW VFI VIB +VM VMIN VMM VMware VNC VPATH +VSCode VSN VTIME VV @@ -1265,26 +1467,38 @@ Viewsonic Viktor VirCIO Vout +Vultech Vyskocil VĆ”clav WALKMODE WARNFATAL WARNOPT +WCH WELI WHAD +WIP WIPO WMNut WS +WSDIR WSE +WSL WTU Waldie +WantedBy +WatchdogSec +WebFreak Werror +Weverything Wextra WhizBang +WiX Wikipedia +WinMerge WinNUT WinPower Wireshark +Wl WordFmt Wrede XAU @@ -1295,15 +1509,18 @@ XOFF XON XOPEN XP +XPG XPPC XSL XT XUPS XXXX +XXXXXXXXXXXX XYZ Xfer XferDly XfmrRes +Xlinker Xpert Xups Xymon @@ -1325,6 +1542,7 @@ Zaika Zampieri Zawadzki ZeroMQ +abandonware abcd ablerex abuild @@ -1339,16 +1557,21 @@ acpi acquisited acvoltsin acvoltsout +acx adb addcmd addenum addinfo +additionalSOLibSearchPath addr addrange +addvars +adelsystem adkorte adm admin's adminbox +adoc advorder ae aec @@ -1375,6 +1598,7 @@ altroot altroots amd anded +antivirus aod aon ap @@ -1386,10 +1610,15 @@ apcsmart apctest apcupsd aphel +apinames +appveyor ar +architecting +archlinux arduino arg argc +argp argparse args argv @@ -1399,8 +1628,11 @@ armhf asapm ascii asciidoc +asciidocs +asciidoctor asem aspell +ast async atcl ats @@ -1426,6 +1658,7 @@ autogen automagically automake automessage +autopoint autoreconf autorestart autosaving @@ -1439,15 +1672,21 @@ avahi avr awd awk +b'some +b'string bAlternateSetting +bUps backend +backends backgrounding backport backported +backports backupspro badpassword bart baseurl +bashrc batchable batt battcap @@ -1476,6 +1715,8 @@ bestfort bestfortress bestuferrups bestups +bfe +bg bigbox bigone bigserver @@ -1494,10 +1735,13 @@ boostvoolts bootable bp br +brazil +brotli bsd bsv bt bti +btn btnG btt buckboosthyst @@ -1507,21 +1751,28 @@ buflen bugfix bugfixes buildbots +builddir +buildtype bullseye +busport busybox bv bypassvolts byv +cStandard cablepower calloc cb cbe +cbi cbl cblimit ccache cd cdc +cdf cef +cee centos cerr certfile @@ -1542,6 +1793,7 @@ cfg cfgadm cflag cflags +cgdisk cgi cgipath cgroup @@ -1551,8 +1803,10 @@ chargermode chargetime charset checksum +checksums chgrp chipset +chkconfig chmod chown chr @@ -1566,6 +1820,7 @@ chunking chv ci cidr +cis clav clearalarms clearhistory @@ -1575,6 +1830,8 @@ clepple clicky cls clueful +clusterware +cmake cmd cmdline cmdname @@ -1588,10 +1845,15 @@ collectd colspan command's commandlen +commentsMap +committer compat +compilerPath conf config +configparser configs +configurationProvider configureaz configureaza confpath @@ -1609,32 +1871,46 @@ cout coverity cp cpp +cppStandard cppcheck +cppdbg +cppnit cppunit cpqpower cpsups cr crestfactor +criticality crlf cron +crontab crossbuild crt crw +crypto csh cshdelay css +cstdint +ctime +ctrl cts ctypes cua cuaa customizations cvt +cwd cx cyberpower d'un da +daemonization +daemonize +daemonized daisychain daisychained +databits datacenter datadir datagrams @@ -1642,41 +1918,54 @@ dataok datasheet datastale dayofweek +dbgsym dblatex dcd dcn +ddk ddl de deUNV debian debootstrap debouncing +debuginfo deci decrement decrypt dedb dedup +deduplication +defs defun dep dephasing deps +dereference desc deschis descr desde dev +devctl devd devel +deviceGetClients deviceamperes devicelua deviceluaOutlet +devicename devscan dfl dhcp dialout +difftool +diffutils dipsw dir dirpath +disassembly +discardable disp distcheck distclean @@ -1685,6 +1974,7 @@ distros dl dll dlopen +dmake dmesg dmf dmfdir @@ -1701,11 +1991,14 @@ dockapp docs dod domxml +dotnet +downloadable dpkg dq driverexec drivername driverpath +drivertool drv drvctl drvpath @@ -1717,6 +2010,7 @@ dsssl dstate dt dtds +dtksh dtr du dumbterm @@ -1733,19 +2027,26 @@ ec echoback eco edb +editorconfig edl +ef +egrep ei el emacs +emptor +emptyDir endchar enddate endian +endianness endif endl energizerups energysave english enum +env envvar envvars ep @@ -1770,10 +2071,13 @@ everups everyone's everything's evilhack +exe executables executeCommand execve extendedhistory +extern +externalConsole extradata fabula facto @@ -1781,12 +2085,15 @@ fallthrough fatalx faultsensitivity fc +fcb fcntl fd fds fe +featureReport fenton fentonups +fffdddxxx ffff fi fieldset @@ -1802,35 +2109,49 @@ firewalling firmwares flts fmt +fno +fontconfig footnoteref +forceshutdown forcessl formatconfig formatparam +formatstring +fosshost fp freebsd freeipmi +freetype freqsensitivity frob frontends fs fsd +fsr +fstab ftdi fuji fullload func functionset gamatronic +gandi gcc gcpp gd gd's +gdb +gdbinit gdlib +gdwarf ge +gedit genericsups genericups genesisII gentoo gestion +getClients getDescription getDevice getDevicesVariableValues @@ -1841,10 +2162,14 @@ getconf getent getenv getopt +gettext +gettextize getvar +gitcache github gitignore gitk +gitlab gmail gmake gnuplot @@ -1852,13 +2177,19 @@ gnutls google goto gotos +gperf gpg +gpgsign +gpio +gpiochip graphviz groupadd groupname +gtk guardpend guardpstart -guestimate +guesstimate +guesstimation guez gufw gui @@ -1868,21 +2199,28 @@ hal hardcoded hasFeature hb +hcd hcl +hexnum hg hh hibernate's hiddev hidparser +hidraw +hidtypes hidups highbattery highfrequency +homebrew hoster hostname hostnames hostsfile hotplug hotplugging +hovercharge +hpe href htaccess html @@ -1892,6 +2230,9 @@ httpd https huawei hunnox +hwdb +hypervisor +hypervisors iBox iDowell iManufacturer @@ -1900,7 +2241,9 @@ iUSB ib ibattery icd +iconv icp +icu idProduct idVendor ident @@ -1910,15 +2253,19 @@ idm ie ietf ifdef +ifeval ifndef +ignoreFailures ignorelb ignoreoff ignoresab ignset illumos im +imagesdir img imv +includePath includedir inductor infos @@ -1933,9 +2280,19 @@ initinfo initscripts initups inline +inlined +innotech +inode +inplace +installable installcheck +installpkg +installurl instcmd instcmds +integrations +intel +intelliSenseMode intercharacter internet interoperability @@ -1944,6 +2301,7 @@ interoperating interprocess interruptonly interruptsize +intltool invcontdelay invcontpolarity inverter @@ -1954,6 +2312,7 @@ io ioLogik ioLogikE ioLogikR +iocage iostream ip ipE @@ -1962,31 +2321,44 @@ ipmi ipmidetectd ipmimonitoring ipmipsu +ipp ippon ipv +ipxe +isDefault isbmex +ish iso isolator ivtscd jNUT jNut jNutWebAPI +jbus jdk jenkins jessie +jimklimov journalctl +jpeg jpg jpgraph jre json jsonify +jumboinfo +jumbotron kVA kadets kaminski kde +keychain keyclick keygen keyout +keyring +keyrings +keyserver killall killpower kludgy @@ -1994,68 +2366,95 @@ kr krauler ksh ktrace +kvm labcd lan langid largp lasaine ld +ldd le len lf +libarchive libaugeas libavahi libc libcommon libcppunit libcrypto +libcurl libdir +libdummy libexec +libexecdir libfreeipmi libgd +libgpgme +libgpiod libhid libhidups libi +libiconv +libintl libipmimonitoring libltdl +liblzma libmodbus libneon +libnetsnmp libnss libnut libnutclient +libnutclientstub +libnutclientsub libnutconfig libnutscan +libpcre libpng libpowerman libre +libregex libs libsnmp libssl +libsystemd libtool libupsclient libusb +libusb's libusbugen libusbx libvirt +libwdi libwrap libxml libxslt libxxx +libz licensor licensors liebert liebertgxt +lifecycle linesensitivity linevoltage linkdoc +linksingledoc linux +linuxdoc lipo +listDeviceClients listdef littleguy lk lkp +lldb +llvm lm ln +lnetsnmp loadPercentage localhost localtime @@ -2077,32 +2476,41 @@ lowruntime lowvoltsout lposix lr +lregex +lsd lsusb +lt ltdl lu lua luaOutlet lv lvo +lws lxc lxcbr lxccontainer +lxcfs lxml lxyz +lz mA mDNS mS macaddr macosx mailx +mainFrame maintainer's maintainership maj +makefile makevartable mandir manpage manpages masterguard +matcher maxacvi maxacvo maxd @@ -2116,12 +2524,14 @@ maxvalue maxvi maxvo mc +mcedit md mdadm mecer megatec memset merchantability +mergetool metadata metasys methodOfFlowControl @@ -2129,6 +2539,7 @@ mge mgeups mgexml mgmt +miDebuggerPath mib mibs microcontroller @@ -2140,6 +2551,7 @@ minacvi minacvo mincharge mindcv +mingw minicol minruntime mins @@ -2157,16 +2569,25 @@ mmZ mmap mmddyyyy mn +mockdrv modbus modelname modprobe monmaster +monofasico monpasswd monslave monuser morbo +mortem mozilla +mqtt msec +msg +msgfmt +msi +msvcrt +msys multi multicommands multilib @@ -2189,15 +2610,20 @@ myprivatepassphrase mysecurityname myups myupsname +nLogic nabcd +nameserver namespace +nano nanosleep nashkaminski natively nb nbr +nbsp nd ndcv +nds nearlowbattery netcat netclient @@ -2217,13 +2643,20 @@ nielchiano nitram nl nlb +nlogic +nls +nm nn nnn noAuthNoPriv nobody's nobt +nodev +nodownload nodtk +noexec noflag +nogroup nohang noimp noinst @@ -2238,6 +2671,7 @@ noout norating noro noscanlangid +nosuid notAfter notifyflags notifyme @@ -2250,6 +2684,7 @@ nowarn np nss ntUPSd +nuget num numOfBytesFromUPS numa @@ -2259,6 +2694,7 @@ nutclient nutclientmem nutconf nutdev +nutdevN nutdrv nutmon nutscan @@ -2267,6 +2703,7 @@ nutupsdrv nutvalue nvi nvo +nwfilter odette odt offdelay @@ -2277,35 +2714,50 @@ oftd oid oids ok +okhlybov +oksh ol oldmac oldmge oldnut +omnios onbatt +onbattery onbattwarn onclick ondelay oneac online +onlinedischarge ont ontd ontimedays ontiniedays +onwards ooce openSUSE +opencollective +openipmi openjdk openlog +openmp opensolaris openssh openssl +optimizations optiups oq os ostream otherprotocols +otheruser outliers +ovmf pF pacman +pacstrap +parallelized +param paramkeywords parsability parsable @@ -2320,13 +2772,16 @@ pathnames pbzip pc pconf +pcre pcs pdf pdu pe +peasy peername pem perl +pfSense pfexec pfy ph @@ -2340,12 +2795,16 @@ pigz pijuice pinout pinouts +pipename +pixmaps pkg pkgconf pkgconfig +pkgin plaintext plugin plugnplay +pluma pmset pmu png @@ -2356,10 +2815,14 @@ pollinterval pollonly popa portname +porttype +posix powercom powerdev powerdown +powerdownflag powerfactor +powerfail powerman powermand powermust @@ -2380,6 +2843,7 @@ ppro pragma pragmas pre +preLaunchTask prepend prepended preprocess @@ -2393,24 +2857,33 @@ printf priv privPassword privProtocol +problemMatcher probu proc productid prog prtconf +ps psu +pthread +pthreads +pts pty +pulizzi pw pwl pwmib pwro px +pxW +pxg pxgx py pycparser pydoc pygments pynut +qDEB qa qemu qfs @@ -2424,10 +2897,12 @@ qx's qxflags rD rackmount +raquo raritan ratedva ratedwatts rb +rcctl readline readonly realpower @@ -2439,6 +2914,7 @@ rebootdelay rebranded receivexhs reconnection +recv redistributors reentrancy refactored @@ -2447,16 +2923,28 @@ referencenominal regex regtype reindex +relatime +releasekeyring relicensing +remoting +renderer +renderers +repindex +repo +reportId reposurgeon repotec req +resetter resistive +resolv resync ret retrydelay revdate revnumber +rex +rexx rf rfc rh @@ -2476,9 +2964,13 @@ rootfs rootfs'es rq rqt +rsa rsync rts +ru +rubygem runlevel +runnable runtime runtimecal runtimek @@ -2490,7 +2982,10 @@ safenet salicru sbin sbindir +scanopts scd +sched +scm schemas screenshot screenshots @@ -2498,6 +2993,7 @@ scriptname sd sdb sddelay +sdk sdl sdorder sdtime @@ -2514,6 +3010,7 @@ selftest sendback sendline sendmail +sequentialized ser seria serialno @@ -2528,11 +3025,14 @@ setpci setpoint setq setuid +setupCommands setvar setvar's sfr sgml sgs +sha +shm shutdownArguments shutdowncmd shutdowndebounce @@ -2542,6 +3042,7 @@ shutdowntime shutup si siemens +sig sigaction sigmask signedness @@ -2551,7 +3052,10 @@ sio sitesearch sitop sizeof +ske skel +slackpkg +slaveid slavesync slewrate slibtool @@ -2568,13 +3072,19 @@ snmpwalk snprintf snprintfcat snr +socat sockdebug +socketname +socomec solaris +solibs solis +somename somepass something's sp spanish +sparc spectype spellcheck splitaddr @@ -2588,18 +3098,24 @@ ss sshd ssize ssl +sstate stan startIP startdelay startup +statefilepath statepath stayoff stderr stdlib stdout stdupsv +stopAtConnect +stopAtEntry stopIP +stopbits str +strUps strace strarr strcasecmp @@ -2610,6 +3126,8 @@ strdup strerror strftime strlen +strnlen +strptime struct structs sts @@ -2618,6 +3136,7 @@ stylesheet stylesheets su subcommand +subdir subdirectories subdirectory subdriver @@ -2636,9 +3155,11 @@ suseconds sv svc svcadm +svcs svn sw symlink +symlinked symlinking symlinks symmetrathreephase @@ -2666,6 +3187,7 @@ tabledef tagname tapswitchdelay tapswitchphase +targetArchitecture tbody tcflush tcgetattr @@ -2676,6 +3198,7 @@ tcsetattr tcsh td tdriver +teknologist tempmax tempmin terminal's @@ -2684,18 +3207,26 @@ testime testtime testuser textproc +tgcware +tgz th +timeframe timehead timeline timername +timestamp timeticks tiocm tios tmp tmpfiles +tmpfs +tmpring tonumber toolchain toolkits +toolset +topFrame topbot tport transmitxhs @@ -2717,39 +3248,53 @@ ttymode ttyp tuple turnon +tv tw tx +txg txt +txz typedef uA uD uM ua +uart ubuntu uc ucb +ucd +ucrt udev udevadm ufw ugen +ui +uid uint ukUNV ul un uname uncomment +unconfigure unconfigured undefine undervoltage unescaped +unicast +unicode uninstall uninterruptible +uniq unistd unitidentify +unix unmapped unmounts unpowered unshutup +unstash updateinfo upexia upower @@ -2760,6 +3305,7 @@ upsIdent upsIdentModel upsMIB upsObjects +upsadmin upsc upscli upsclient @@ -2780,6 +3326,7 @@ upsfetch upsgone upsh upshandler +upsid upsidentmodel upsimage upsload @@ -2790,6 +3337,7 @@ upsmon's upsmonuser upsname upsonbatt +upspass upspasswd upsrw upssched @@ -2800,13 +3348,17 @@ upstype upsuser upswired uptime +urb +url urpmi usb usbconfig usbfs usbhid usbif +usbinfo usbmisc +usbscan usbups usbus usd @@ -2814,6 +3366,7 @@ usec useconds useradd userid +userland usermap username usernames @@ -2822,6 +3375,7 @@ usleep usr utalk utf +utils uu uucp vCPU @@ -2840,38 +3394,61 @@ varname varvalue vbatt vc +vectronic vendorid ver verifySourceSig versa versioned +versioning victron victronups vid vin virsh +virt +virtinst virtualenv virtualization +virtualized +vivo vo vod voltronic von +vscode wDescriptorLength +waitbeforereconnect wakeup wastePower +wc wchar +wdi +webNUT +webnut webserver +websitelayout wf wget whitespace wiki +wildcard +wildcards +wininit +winnutclient +winpthreads +wix +wmNUT wmnut wordformat workflow workspace +workspaceFolder +workspaces writability writeinfo writeups +ws xAAAA xCC xD @@ -2882,6 +3459,7 @@ xZZZZ xa xaabbcc xcalloc +xcode xd xe xferdelay @@ -2890,25 +3468,37 @@ xfff xffff xfmrresistance xh +xhci xhtml +xjf xmalloc xml xmlify xmllint xmlns +xmlto +xpg +xpm xr xrealloc xsd xsl +xsl's xsltproc xstrdup xu xxxAP xxxx xxxxAP +xz +xzf +yaml +yml youruid yyy zaac +zakx +zfs zinto zlib zsh diff --git a/docs/nutdrv_qx-subdrivers.txt b/docs/nutdrv_qx-subdrivers.txt index 8d786450b9..bc0dde9a61 100644 --- a/docs/nutdrv_qx-subdrivers.txt +++ b/docs/nutdrv_qx-subdrivers.txt @@ -960,6 +960,176 @@ Return the currently processed status so that it can be checked with one of the If you need to edit the current status call this function with one of the NUT status (all but +OB+ are supported, simply set it as not +OL+); prefix them with an exclamation mark if you want to clear them from the status (e.g. +!OL+). +Armac Subdriver +~~~~~~~~~~~~~~~ + +Armac subdriver is based on reverse engineering of Power Manager II software by +Richcomm Technologies written in 2005 that is still (as of 2023) being +distributed as a valid software for freshly sold UPS of various manufacturers. +It uses commands as defined for Megatec protocol - but has a different +communication mechanism. + +It uses two types of USB interrupt transfers: +- 4 bytes to send a command (usually single transfer). +- 6 byte chunk to read a reply (multiple transfers). + +Transfers are similar to those of the richcomm nut driver, but the transferred +data is not short binary commands. Instead, serial text data is overlaid in +these transfers in a way that creates a badly made USB serial interface. UPS +reply looks similar to this: + + 0 1 2 3 4 5 +HL 00 00 00 00 00 + +HL is a control byte. Its high nibble meaning is unknown. It changes between +two possible values during transmission. Low nibble encodes number of bytes +that have a meaning in the transaction. For example there are 5 bytes that +might contain ASCII serial data, but only some might be valid, and other might +be random, stale buffer data, etc. + +What follows is set of observed transmissions by various UPSes gathered from +Github issues. + +Transfer dumps +^^^^^^^^^^^^^^ + +#### Vultech V2000 + +---- +419.987514 [D4] armac command Q1 +419.988307 [D4] armac cleanup ret i=0 ret=6 ctrl=c0 +420.119402 [D4] read: ret 6 buf 81: 28 30 31 30 30 >(0100< +420.130383 [D4] read: ret 6 buf c1: 32 30 31 30 30 >20100< +420.141408 [D4] read: ret 6 buf 82: 33 33 31 30 30 >33100< +420.152201 [D4] read: ret 6 buf c3: 2e 30 20 30 30 >.0 00< +420.153237 [D4] read: ret 6 buf 82: 30 30 20 30 30 >00 00< +420.164299 [D4] read: ret 6 buf c1: 30 30 20 30 30 >00 00< +420.175293 [D4] read: ret 6 buf 82: 2e 30 20 30 30 >.0 00< +420.186358 [D4] read: ret 6 buf c3: 20 32 33 30 30 > 2300< +420.190322 [D4] read: ret 6 buf 83: 33 2e 30 30 30 >3.000< +420.194323 [D4] read: ret 6 buf c1: 20 2e 30 30 30 > .000< +420.205358 [D4] read: ret 6 buf 81: 30 2e 30 30 30 >0.000< +420.216318 [D4] read: ret 6 buf c2: 31 34 30 30 30 >14000< +420.227445 [D4] read: ret 6 buf 83: 20 34 39 30 30 > 4900< +420.228334 [D4] read: ret 6 buf c2: 2e 30 39 30 30 >.0900< +420.239461 [D4] read: ret 6 buf 81: 20 30 39 30 30 > 0900< +420.250411 [D4] read: ret 6 buf c2: 32 37 39 30 30 >27900< +420.261405 [D4] read: ret 6 buf 83: 2e 30 20 30 30 >.0 00< +420.265468 [D4] read: ret 6 buf c3: 32 30 2e 30 30 >20.00< +420.269465 [D4] read: ret 6 buf 81: 38 30 2e 30 30 >80.00< +420.280322 [D4] read: ret 6 buf c1: 20 30 2e 30 30 > 0.00< +420.291469 [D4] read: ret 6 buf 82: 30 30 2e 30 30 >00.00< +420.302465 [D4] read: ret 6 buf c3: 30 30 31 30 30 >00100< +420.303511 [D4] read: ret 6 buf 82: 00 30 31 30 30 > <- This has 0x00 and '0', will be read as "00" +420.303515 [D3] found null byte in status bits at 43 byte, assuming 0. +420.314425 [D4] read: ret 6 buf c1: 31 30 31 30 30 >10100< <- this has '1' +420.325432 [D4] read: ret 6 buf 81: 0d 30 31 30 30 >.0100< <- and this finishes with `\r`. +420.325442 [D3] armac command Q1 response read: '(233.0 000.0 233.0 014 49.0 27.0 20.8 00001001' +---- + +---- +1.185164 [D4] armac command ID +1.316257 [D4] read: ret 6 buf c1: 23 31 00 30 30 >#1 +1.327309 [D4] read: ret 6 buf 81: 20 31 00 30 30 > 1 +1.338264 [D4] read: ret 6 buf c2: 20 20 00 30 30 > +1.349151 [D4] read: ret 6 buf 83: 20 20 20 30 30 > 00< +1.360277 [D4] read: ret 6 buf c2: 20 20 20 30 30 > 00< +1.371322 [D4] read: ret 6 buf 83: 20 20 20 30 30 > 00< +1.382265 [D4] read: ret 6 buf c3: 20 20 20 30 30 > 00< +1.393156 [D4] read: ret 6 buf 82: 20 20 20 30 30 > 00< +1.404324 [D4] read: ret 6 buf c3: 20 20 20 30 30 > 00< +1.415342 [D4] read: ret 6 buf 83: 20 20 20 30 30 > 00< +1.426292 [D4] read: ret 6 buf c2: 20 20 20 30 30 > 00< +1.437203 [D4] read: ret 6 buf 83: 20 20 20 30 30 > 00< +1.448328 [D4] read: ret 6 buf c3: 56 34 2e 30 30 >V4.00< +1.459293 [D4] read: ret 6 buf 82: 31 30 2e 30 30 >10.00< +1.470274 [D4] read: ret 6 buf c3: 20 20 20 30 30 > 00< +1.481208 [D4] read: ret 6 buf 82: 20 20 20 30 30 > 00< +1.492261 [D4] read: ret 6 buf c1: 0d 20 20 30 30 > +1.492270 [D3] armac command ID response read: '# V4.10 ' +---- + +---- +4.749667 [D4] armac command F +4.876638 [D4] read: ret 6 buf 81: 23 31 00 30 30 >#1 +4.887614 [D4] read: ret 6 buf c1: 32 31 00 30 30 >21 +4.898644 [D4] read: ret 6 buf 82: 32 30 00 30 30 >20 +4.909595 [D4] read: ret 6 buf c3: 2e 30 20 30 30 >.0 00< +4.920648 [D4] read: ret 6 buf 82: 30 30 20 30 30 >00 00< +4.931629 [D4] read: ret 6 buf c3: 35 20 32 30 30 >5 200< +4.942601 [D4] read: ret 6 buf 83: 34 2e 30 30 30 >4.000< +4.953666 [D4] read: ret 6 buf c2: 30 20 30 30 30 >0 000< +4.964535 [D4] read: ret 6 buf 83: 35 30 2e 30 30 >50.00< +4.975540 [D4] read: ret 6 buf c2: 30 0d 2e 30 30 >0 +4.975546 [D3] armac command F response read: '#220.0 005 24.00 50.0' +---- + +#### Armac R/2000I/PSW + +---- +112.966856 [D4] armac command Q1 +112.968197 [D4] armac cleanup ret i=0 ret=6 ctrl=c0 <- Cleanups required. +113.091193 [D4] read: ret 6 buf 81: 28 30 0d 2e 30 >(0 <- Usually 1-3 bytes available in transfer. +113.103211 [D4] read: ret 6 buf c1: 30 30 0d 2e 30 >00 +113.115180 [D4] read: ret 6 buf 82: 30 30 0d 2e 30 >00 +113.117144 [D4] read: ret 6 buf c3: 2e 30 20 2e 30 >.0 .0< +113.120150 [D4] read: ret 6 buf 81: 31 30 20 2e 30 >10 .0< +113.132178 [D4] read: ret 6 buf c1: 34 30 20 2e 30 >40 .0< +113.144159 [D4] read: ret 6 buf 82: 30 2e 20 2e 30 >0. .0< +113.146149 [D4] read: ret 6 buf c3: 30 20 32 2e 30 >0 2.0< +113.149173 [D4] read: ret 6 buf 81: 32 20 32 2e 30 >2 2.0< +113.161167 [D4] read: ret 6 buf c1: 37 20 32 2e 30 >7 2.0< +113.173159 [D4] read: ret 6 buf 82: 2e 30 32 2e 30 >.02.0< +113.175157 [D4] read: ret 6 buf c3: 20 30 30 2e 30 > 00.0< +113.178158 [D4] read: ret 6 buf 81: 32 30 30 2e 30 >200.0< +113.190157 [D4] read: ret 6 buf c1: 20 30 30 2e 30 > 00.0< +113.202161 [D4] read: ret 6 buf 82: 30 30 30 2e 30 >000.0< +113.204154 [D4] read: ret 6 buf c3: 2e 30 20 2e 30 >.0 .0< +113.207150 [D4] read: ret 6 buf 81: 34 30 20 2e 30 >40 .0< +113.219174 [D4] read: ret 6 buf c1: 36 30 20 2e 30 >60 .0< +113.231165 [D4] read: ret 6 buf 82: 2e 38 20 2e 30 >.8 .0< +113.233157 [D4] read: ret 6 buf c3: 20 35 36 2e 30 > 56.0< +113.237149 [D4] read: ret 6 buf 81: 2e 35 36 2e 30 >.56.0< +113.249168 [D4] read: ret 6 buf c1: 30 35 36 2e 30 >056.0< +113.261155 [D4] read: ret 6 buf 83: 20 31 30 2e 30 > 10.0< +113.263151 [D4] read: ret 6 buf c2: 30 30 30 2e 30 >000.0< +113.266152 [D4] read: ret 6 buf 81: 31 30 30 2e 30 >100.0< +113.278161 [D4] read: ret 6 buf c1: 30 30 30 2e 30 >000.0< <- No Null bytes. +113.290155 [D4] read: ret 6 buf 82: 30 30 30 2e 30 >000.0< +113.292159 [D4] read: ret 6 buf c1: 0d 30 30 2e 30 > +113.292169 [D3] armac command Q1 response read: '(000.0 140.0 227.0 002 00.0 46.8 56.0 10001000' +---- + +Next query would return 0x80 control byte - 0 available bytes. This used to +terminate transmission, but some UPS don't work like that. + + +#### Armac R/3000I/PF1 + +---- +0.083301 [D4] armac command Q1 +0.164847 [D4] read: ret 6 buf a6: 28 32 34 31 2e >(241.< +0.184839 [D4] read: ret 6 buf 86: 35 20 30 30 30 >5 000< +0.205851 [D4] read: ret 6 buf a6: 2e 30 20 32 33 >.0 23< +0.226849 [D4] read: ret 6 buf 86: 30 2e 33 20 30 >0.3 0< +0.247859 [D4] read: ret 6 buf a6: 30 30 20 34 39 >00 49< +0.268862 [D4] read: ret 6 buf 86: 2e 39 20 32 2e >.9 2.< +0.289857 [D4] read: ret 6 buf a6: 32 35 20 34 38 >25 48< +0.309866 [D4] read: ret 6 buf 86: 2e 30 20 30 30 >.0 00< +0.330863 [D4] read: ret 6 buf a6: 30 30 30 30 30 >00000< +0.827913 [D4] read: ret 6 buf 83: 31 0d 30 30 30 >1 000< +0.827927 [D3] armac command Q1 response read: '(241.5 000.0 230.3 000 49.9 2.25 48.0 00000001' +0.827954 [D4] armac command ID +1.394985 [D4] read: ret 6 buf a5: 4e 41 4b 0d 30 >NAK < +1.395001 [D3] armac command ID response read: 'NAK' +---- + +This UPS sends higher nibble set to 6 often, which exceeds available bytes. +Maybe means that more are available. Its serial-USB bridge is probably faster. +We read 5 bytes in case 6 nibble is sent. End of transmission is marked by `\r`, +no 0 nibble is sent. + + Notes ~~~~~ diff --git a/docs/packager-guide.txt b/docs/packager-guide.txt index 4730dcb2bb..123ab417d0 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,18 +164,21 @@ This standard was created by: Overview of the package tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + FIXME: make a dependency graph - <> - <> - <> - <> +- <> - <> - <> - <> - <> - <> +- <> (currently platform-dependent) - <> (platform-dependent) - <> (platform-dependent) @@ -241,6 +246,16 @@ nut-cgi - Size: - Deps: +[[pkg-nut-scanner]] +nut-scanner +^^^^^^^^^^^ +- Desc: +- Files: +- Size: +- Deps: + +NOTE: hard third-party dependency on `libltdl`; recommends `libsnmp`, `libneon`, +and the `libusb` variant (0.1 or 1.0) it was built against. [[pkg-nut-powerman-pdu]] nut-powerman-pdu @@ -282,6 +297,14 @@ nut-modbus - Size: - Deps: +[[pkg-nut-gpio]] +nut-gpio +^^^^^^^^ +- Desc: (currently platform-dependent) +- Files: +- Size: +- Deps: + [[pkg-nut-linux-i2c]] nut-linux-i2c ^^^^^^^^^^^^^ @@ -369,6 +392,10 @@ nut-server Deps: Files: upsmon, upsc, upscmd, upsrw + H) nut-scanner: + + Deps: hard dependency on `libltdl`; recommends `libsnmp`, `libneon`, and the `libusb` variant (0.1 or 1.0) it was built against. + Files: nut-scanner tool and libnutscan + manpages Note: "nut" can be a meta package @@ -383,6 +410,9 @@ TO BE CONTINUED Configuration option ^^^^^^^^^^^^^^^^^^^^ + +Example: + name= "ups" or "nut" ./configure \ --prefix=/ \ @@ -402,5 +432,11 @@ html-path --without-ssl ... ------------------------------------------------------------------------- +NOTE: For packaging (OS distribution or in-house) it is recommended to +primarily `./configure --with-all` and then excise `--without-something` +explicitly for items not supported on your platform, so you do not miss +out on new NUT features as they come with new releases. Some may require +that you update your build environment with new third-party dependencies, +so a broken build of a new NUT release would let you know how to act. +------------------------------------------------------------------------ diff --git a/docs/release-notes.txt b/docs/release-notes.txt new file mode 100644 index 0000000000..c0b989e67e --- /dev/null +++ b/docs/release-notes.txt @@ -0,0 +1,44 @@ +:titles.underlines: "__","==","--","~~","^^" + +Network UPS Tools Release Notes +_______________________________ +:Author: Russell_Kroll,_Arnaud_Quette,_Arjen_de_Korte,_Charles_Lepple_and_Jim_Klimov +:Author Initials: RK, AQ, ADK, CL & JK + +Introduction +============ + +The primary goal of the Network UPS Tools (NUT) project is to provide support +for Power Devices, such as Uninterruptible Power Supplies, Power Distribution +Units and Solar Controllers. + +This document intends to report high-level changes delivered by NUT project +releases, as well as practical nuances for packagers and end-users who would +be upgrading their NUT deployments. + +It DOES NOT intend to detail the change log: it is very large and complicated +to render properly, so is better served by another document artifact. + +If you wish to discover how everything came together, have a look at the +<>. + + +[[NUT_Release_Notes]] +NUT Release Notes (and other feature details) +============================================= + +include::{builddir}../NEWS.adoc-parsed[] + + +[[NUT_Upgrading_Notes]] +NUT Upgrading Notes +=================== + +include::{builddir}../UPGRADING.adoc-parsed[] + + +[[Project_History]] +Project history +=============== + +include::history.txt[] diff --git a/docs/scheduling.txt b/docs/scheduling.txt index 84ba454695..a2db43e8b3 100644 --- a/docs/scheduling.txt +++ b/docs/scheduling.txt @@ -180,12 +180,12 @@ Executing commands immediately ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As an example, consider the scenario where a UPS goes onto battery power. -However, the users are not informed until 60 seconds later -- using a timer as +However, the users are not informed until 30 seconds later -- using a timer as described above. Whilst this may let the *logged in* users know that the UPS is on battery power, it does not inform any users subsequently logging in. To enable this we could, at the same time, create a file which is read and displayed to any user trying to login whilst the UPS is on battery power. If -the UPS comes back onto utility power within 60 seconds, then we can cancel +the UPS comes back onto utility power within 30 seconds, then we can cancel the timer and remove the file, as described above. However, if the UPS comes back onto utility power say 5 minutes later then we do not want to use any timers but we still want to remove the file. To do this we could use: diff --git a/docs/security.txt b/docs/security.txt index 09c101cf4f..99e85b7ee7 100644 --- a/docs/security.txt +++ b/docs/security.txt @@ -22,31 +22,137 @@ How to verify the NUT source code signature In order to verify the NUT source code signature for releases, perform the following steps: -- Retrieve the link:http://www.networkupstools.org/download.html[NUT source code] +- Retrieve the link:https://www.networkupstools.org/download.html[NUT source code] (nut-X.Y.Z.tar.gz) and the matching signature (nut-X.Y.Z.tar.gz.sig) -- Retrieve the link:http://www.networkupstools.org/source/nut-key.gpg[NUT maintainer's signature]: +- Retrieve the link:https://www.networkupstools.org/source/nut-key.gpg[NUT +maintainer's signature keyring]: - $ gpg --fetch-keys http://www.networkupstools.org/source/nut-key.gpg + $ gpg --fetch-keys https://www.networkupstools.org/source/nut-key.gpg -NOTE: As of NUT 2.7.3, a new release key is used. In order to verify a previous -release, please use -link:http://www.networkupstools.org/source/nut-old-key.gpg[NUT old maintainer's signature] +[NOTE] +====== +As of NUT 2.8.0, a new release key is used, but the `nut-key.gpg` should +be cumulative with older chain key files (includes them). You can view the key +list in a downloaded copy of the URL above with: + + $ gpg --with-colons --import-options import-show --dry-run --import < nut-key.gpg + +...and as of this writing, it should contain two key sets for various identities +of "Arnaud Quette" and one set of "Jim Klimov". +====== + +Just in case, the previous key file used since NUT 2.7.3 release is stored as +link:https://www.networkupstools.org/source/nut-key-2.7.3.gpg[NUT +old maintainer's signature for 2.7.3-2.7.4 releases] + +In order to verify an even older release, please use +link:https://www.networkupstools.org/source/nut-old-key.gpg[NUT +old maintainer's signature since 2002 until 2.7.3 release] - Launch the GPG checking using the following command: $ gpg --verify nut-X.Y.Z.tar.gz.sig -- You should see a message mentioning a "Good signature", like: +- You should see a message mentioning a "Good signature", with formatting which + depends on your gpg version, like: + + gpg: Signature made Thu Jun 1 00:10:16 2023 CEST + ... + gpg: Good signature from "Jim Klimov ..." + ... + Primary key fingerprint: B834 59F7 76B9 0224 988F 36C0 DE01 84DA 7043 DCF7 + ... + +[NOTE] +====== +The previously used maintainer's signatures would output (with markup of +older gpg tools here): gpg: Signature made Wed Apr 15 15:55:30 2015 CEST using RSA key ID 55CA5976 gpg: Good signature from "Arnaud Quette ..." ... -NOTE: the previously used maintainer's signature would output: -+ +or: + gpg: Signature made Thu Jul 5 16:15:05 2007 CEST using DSA key ID 204DDF1B gpg: Good signature from "Arnaud Quette ..." ... +====== + +//////// +Maintainer notes for posterity: + +* See https://github.com/networkupstools/nut/issues/1963 and + https://github.com/networkupstools/nut/issues/1410 for recent + forays into this area. + +* Repo is https://github.com/networkupstools/nut-source-archive.git + +* Keys in the file can be viewed with: +---- +:; gpg --with-colons --import-options import-show --dry-run --import < nut-key.gpg +---- + +* Old keys as well as a new maintainer key can be imported first into + a temporary keyring (existing target file and a slash in its path + name argument are allegedly important): +---- +:; rm -f tmpring.gpg ; touch tmpring.gpg +:; gpg --no-default-keyring --keyring ./tmpring.gpg --import nut-key.gpg +:; gpg --no-default-keyring --keyring ./tmpring.gpg --import nut-old-key.gpg +:; gpg --no-default-keyring --keyring ./tmpring.gpg --import nut-key-2.7.3.gpg +:; gpg --no-default-keyring --keyring ./tmpring.gpg --import ~/.gnupg/HEXCODEMYNEWKEYDATA.pub +---- + +** Note that whenever the key owner edits the primary key data, e.g. to add or + remove "uid" entries with e-mail aliases, or sub-keys dedicated for specific + purposes, the `~/.gnupg/HEXCODEMYNEWKEYDATA.pub` is changed and should be + re-published to OpenPGP servers, to nut-website, to GitHub account, etc. + +* This (binary) keychain can be exported into ASCII-armor format, and also + update the keychain file used by default: +---- +:; gpg --no-default-keyring --keyring ./tmpring.gpg --export -a > nut-key.gpg +---- +//////// + +How to verify the NUT source code checksum +------------------------------------------ + +As a weaker but simpler alternative to verifying a *signature*, you can verify +just the accompanying checksums of the source archive file. This is useful +primarily to check against bit-rot in original storage or in transit. As far +as disclaimers go: ideally, you should cover all provided algorithms -- e.g. +MD5 and SHA256 -- to minimize the chance that intentional malicious tampering +on the wire goes undetected. A myriad tools can check that on various platforms; +some examples follow: + + # Example original checksum to compare with, from NUT website: + $ cat nut-2.8.0.tar.gz.sha256 + c3e5a708da797b7c70b653d37b1206a000fcb503b85519fe4cdf6353f792bfe5 nut-2.8.0.tar.gz + + # Generate checksum of downloaded archive with perl (a NUT build dependency + # generally, though you may have to install Digest::SHA module from CPAN): + $ perl -MDigest::SHA=sha256_hex -le "print sha256_hex <>" nut-2.8.0.tar.gz + c3e5a708da797b7c70b653d37b1206a000fcb503b85519fe4cdf6353f792bfe5 + + # Generate checksum of downloaded archive with openssl (another optional + # NUT build dependency): + $ openssl sha256 nut-2.8.0.tar.gz + SHA256(nut-2.8.0.tar.gz)= c3e5a708da797b7c70b653d37b1206a000fcb503b85519fe4cdf6353f792bfe5 + + # Generate checksum of downloaded archive with coreutils: + $ sha256sum nut-2.8.0.tar.gz + c3e5a708da797b7c70b653d37b1206a000fcb503b85519fe4cdf6353f792bfe5 nut-2.8.0.tar.gz + + # Auto-check downloaded checksum against downloaded archive with coreutils: + $ sha256sum -c nut-2.8.0.tar.gz.sha256 + nut-2.8.0.tar.gz: OK + + # Generate checksum of downloaded archive with GPG: + $ gpg --print-md SHA256 nut-2.8.0.tar.gz + nut-2.8.0.tar.gz: C3E5A708 DA797B7C 70B653D3 7B1206A0 + 00FCB503 B85519FE 4CDF6353 F792BFE5 System level privileges and ownership ------------------------------------- @@ -81,7 +187,7 @@ During the initial <>, we have created a monitoring user for `upsmon`. -You can also create an 'administrator' user with full power using: +You can also create an `administrator` user in NUT with full power using: [administrator] password = mypass @@ -128,6 +234,13 @@ compiled in). LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 +As a special case, `LISTEN * ` (with an asterisk) will try to +listen on "ANY" IP address for both IPv6 (`::0`) and IPv4 (`0.0.0.0`), +subject to `upsd` command-line arguments, or system configuration or support. +Note that if the system supports IPv4-mapped IPv6 addressing per RFC-3493, +and does not allow to disable this mode, then there may be one listening +socket to handle both address families. + This parameter will only be read at startup. You'll need to restart (rather than reload) `upsd` to apply any changes made here. @@ -152,7 +265,7 @@ You should use this to restrict network access. [[UFW]] -include::../scripts/ufw/README[] +include::../scripts/ufw/README.adoc[] [[TCP_Wrappers]] TCP Wrappers @@ -182,10 +295,10 @@ Configuring SSL SSL is available as a build option (`--with-ssl`). -It encrypts sessions between upsd and clients, and can also be used to +It encrypts sessions between `upsd` and clients, and can also be used to authenticate servers. -This means that stealing port 3493 from upsd will no longer net you interesting +This means that stealing port 3493 from `upsd` will no longer net you interesting passwords. Several things must happen before this will work, however. This chapter will @@ -262,18 +375,18 @@ Example: cp upsd.crt /usr/local/ups/etc/certs/0123abcd.0 If you already have a file with that name in there, increment the -0 until you get a unique filename that works. +`0` part until you get a unique filename that works. -If you have multiple client systems (like upsmon instances in +If you have multiple client systems (like `upsmon` instances in secondary mode), be sure to install this file on them as well. We recommend making a directory under your existing confpath to keep everything in the same place. Remember the path you created, -since you will need to put it in upsmon.conf later. +since you will need to put it in `upsmon.conf` later. It must not be writable by unprivileged users, since someone could -insert a new client certificate and fool upsmon into trusting a -fake upsd. +insert a new client certificate and fool `upsmon` into trusting a +fake `upsd`. Create the combined file for upsd ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -285,12 +398,12 @@ To do so, use the below commands: chmod 0640 upsd.pem This file must be kept secure, since anyone possessing it could -pretend to be upsd and harvest authentication data if they get a +pretend to be `upsd` and harvest authentication data if they get a hold of port 3493. -Having it be owned by 'root' and readable by group 'nut' allows upsd +Having it owned by `root` and readable by group `nut` allows `upsd` to read the file without being able to change the contents. This -is done to minimize the impact if someone should break into upsd. +is done to minimize the impact if someone should break into `upsd`. NUT reads the key and certificate files after dropping privileges and forking. @@ -326,13 +439,13 @@ Restart upsd It should come back up without any complaints. If it says something about keys or certificates, then you probably missed a step. -If you run upsd as a separate user id (like nutsrv), make sure that -user can read the upsd.pem file. +If you run `upsd` as a separate user id (like `nutsrv`), make sure that +user can read the `upsd.pem` file. Point upsmon at the certificates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Edit your upsmon.conf, and tell it where the CERTPATH is: +Edit your `upsmon.conf`, and tell it where the `CERTPATH` is: CERTPATH @@ -343,27 +456,27 @@ Example: Recommended: make upsmon verify all connections with certificates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Put this in upsmon.conf: +Put this in `upsmon.conf`: CERTVERIFY 1 -Without this, there is no guarantee that the upsd is the right host. +Without this, there is no guarantee that the `upsd` is the right host. Enabling this greatly reduces the risk of man in the middle attacks. This effectively forces the use of SSL, so don't use this unless -all of your upsd hosts are ready for SSL and have their certificates +all of your `upsd` hosts are ready for SSL and have their certificates in order. Recommended: force upsmon to use SSL ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Again in upsmon.conf: +Again in `upsmon.conf`: FORCESSL 1 If you don't use `CERTVERIFY 1`, then this will at least make sure that nobody can sniff your sessions without a large effort. Setting -this will make upsmon drop connections if the remote upsd doesn't +this will make `upsmon` drop connections if the remote `upsd` doesn't support SSL, so don't use it unless all of them have it running. NSS backend usage @@ -397,12 +510,12 @@ Certificates should be signed by a certification authorities (CAs). Following commands are typical samples, contact your SSL guru or security officer to follow your company procedures. -.Generate a server certificate for upsd: +.Generate a server certificate for `upsd`: - Create a directory where store the certificate database: `mkdir cert_db` - Create the certificate database : `certutil -N -d cert_db` - Import the CA certificate: `certutil -A -d cert_db -n "My Root CA" -t "TC,," -a -i rootca.crt` -- Create a server certificate request (here called 'My nut server'): +- Create a server certificate request (here called "My nut server"): `certutil -R -d cert_db -s "CN=My nut server,O=MyCompany,ST=MyState,C=US" -a -o server.req` - Make your CA sign the certificate (produces server.crt) - Import the signed certificate into server database: @@ -429,8 +542,8 @@ an "official" certificate authority. `certutil -N -d CA_db` - Generate a certificate for CA: `certutil -S -d CA_db -n "My Root CA" -s "CN=My CA,O=MyCompany,ST=MyState,C=US" -t "CT,," -x -2` -(Do not forget to answer 'Yes' to the question 'Is this a CA certificate [y/N]?') -- Extract the CA certificate to be able to import it in upsd (or upsmon) +(Do not forget to answer `Yes` to the question "Is this a CA certificate [y/N]?") +- Extract the CA certificate to be able to import it in `upsd` (or `upsmon`) certificate database: `certutil -L -d CA_db -n "My Root CA" -a -o rootca.crt` - Sign a certificate request with the CA certificate (simulate a real CA @@ -449,14 +562,14 @@ database .db files) to the right place, such as `/usr/local/ups/etc/`: upsd (required): certificate database and self certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Edit the upsd.conf to tell where find the certificate database: +Edit the `upsd.conf` to tell where find the certificate database: CERTPATH /usr/local/ups/etc/cert_db Also tell which is the certificate to send to clients to authenticate itself and the password to decrypt private key associated to certificate: - CERTIDENT 'certificate name' 'database password' + CERTIDENT "certificate name" "database password" NOTE: Generally, the certificate name is the server domain name, but is not a hard rule. The certificate can be named as useful. @@ -470,7 +583,7 @@ NUT with `WITH_CLIENT_CERTIFICATE_VALIDATION` defined: make CFLAGS="-DWITH_CLIENT_CERTIFICATE_VALIDATION" UPSD can accept three levels of client authentication. Just specify it with -the directive `CERTREQUEST` with the corresponding value in the upsd.conf +the directive `CERTREQUEST` with the corresponding value in the `upsd.conf` file: - NO: no client authentication. @@ -480,28 +593,29 @@ If the client does not send any certificate, the connection is closed. - REQUIRE: a certificate is requested to the client and if it is not valid (no validation chain) the connection is closed. -Like CA certificates, you can add many 'trusted' client and CA certificates +Like CA certificates, you can add many "trusted" client and CA certificates in server's certificate databases. upsmon (required): upsd authentication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In order for upsmon to securely connect to upsd, it must authenticate it. -You must associate an upsd host name to security rules in upsmon.conf -with the directive 'CERTHOST'. +In order for `upsmon` to securely connect to `upsd`, it must authenticate it. +You must associate an `upsd` host name to security rules in `upsmon.conf` +with the directive `CERTHOST`. -'CERTHOST' associates a hostname to a certificate name. It also determines +`CERTHOST` associates a hostname to a certificate name. It also determines whether a SSL connection is mandatory, and if the server certificate must be validated. - CERTHOST 'hostname' 'certificate name' 'certverify' 'forcessl' + CERTHOST "hostname" "certificate name" "certverify" "forcessl" -If the flag `forcessl` is set to `1`, and upsd answers that it can not +If the flag `forcessl` is set to `1`, and `upsd` answers that it can not connect with SSL, the connection closes. + If the flag `certverify` is set to `1` and the connection is done in SSL, -upsd's certificate is verified and its name must be the specified -'certificate name'. +`upsd`'s certificate is verified and its name must be the specified +`"certificate name"`. To prevent security leaks, you should set all `certverify` and `forcessl` flags to `1` (force SSL connection and validate all certificates for all @@ -518,14 +632,15 @@ name is its hostname. upsmon (optional): certificate database and self certificate ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Like upsd, upsmon may need to authenticate itself (upsd's `CERTREQUEST` +Like `upsd`, `upsmon` may need to authenticate itself (`upsd`'s `CERTREQUEST` directive set to `REQUEST` or `REQUIRE`). + It must access to a certificate (and its private key) in a certificate -database configuring `CERTPATH` and `CERTIDENT` in upsmon.conf in the -same way as upsd. +database configuring `CERTPATH` and `CERTIDENT` in `upsmon.conf` in the +same way as `upsd`. CERTPATH /usr/local/ups/etc/cert_db - CERTIDENT 'certificate name' 'database password' + CERTIDENT "certificate name" "database password" Restart upsd @@ -534,21 +649,21 @@ Restart upsd It should come back up without any complaints. If it says something about keys or certificates, then you probably missed a step. -If you run upsd as a separate user ID (like nutsrv), make sure that +If you run `upsd` as a separate user ID (like `nutsrv`), make sure that user can read files in the certificate directory. NUT reads the keys and certificates after forking and dropping privileges. Restart upsmon ~~~~~~~~~~~~~~ -You should see something like this in the syslog from upsd: +You should see something like this in the syslog from `upsd`: foo upsd[1234]: Client mon@localhost logged in to UPS [myups] (SSL) -If upsd or upsmon give any error messages, or the `(SSL)` is missing, +If `upsd` or `upsmon` give any error messages, or the `(SSL)` is missing, then something isn't right. -If in doubt about upsmon, start it with -D so it will stay in +If in doubt about `upsmon`, start it with `-D` so it will stay in the foreground and print debug messages. It should print something like this every couple of seconds: @@ -618,7 +733,7 @@ Note that the replacement of OpenSSL by Mozilla Network Security Services chrooting and other forms of paranoia ------------------------------------- -It has been possible to run the drivers and upsd in a chrooted jail for +It has been possible to run the drivers and `upsd` in a chrooted jail for some time, but it involved a number of evil hacks. From the 1.3 series, a much saner chroot behavior exists, using BIND 9 as an inspiration. @@ -650,7 +765,7 @@ programs have been built with the default prefix, so they are using cp -a /usr/local/ups/etc/upsd.conf . cp -a /usr/local/ups/etc/ups.conf . -We're using 'cp -a' to maintain the permissions on those files. +We're using `cp -a` to maintain the permissions on those files. Now bring over your state path, maintaining the same permissions as before. @@ -690,7 +805,7 @@ symlinks ~~~~~~~~ After you do this, you will have two copies of many things, like the -confpath and the state path. I recommend deleting the 'real' +confpath and the state path. I recommend deleting the "real" `/var/state/ups`, replacing it with a symlink to `/chroot/nut/var/state/ups`. That will let other programs reference the `.pid` files without a lot of hassle. @@ -698,7 +813,7 @@ confpath and the state path. I recommend deleting the 'real' You can also do this with your confpath and point `/usr/local/ups/etc` (or equivalent on your system) at `/chroot/nut/usr/local/ups/etc` unless you're worried about something hurting the files inside that directory. In that -case, you should maintain a 'golden' copy and push it into the chroot path +case, you should maintain a "golden" copy and push it into the chroot path after making changes. The `upsdrvctl` itself does not chroot, so the `ups.conf` still needs to be @@ -707,9 +822,9 @@ in the usual confpath. upsmon ~~~~~~ -This has not yet been applied to upsmon, since it can be quite +This has not yet been applied to `upsmon`, since it can be quite complicated when there are notifiers that need to be run. One -possibility would be for upsmon to have three instances: +possibility would be for `upsmon` to have three instances: - privileged root parent that listens for a shutdown command diff --git a/docs/sms-brazil-protocol.txt b/docs/sms-brazil-protocol.txt new file mode 100644 index 0000000000..1093304b08 --- /dev/null +++ b/docs/sms-brazil-protocol.txt @@ -0,0 +1,346 @@ +SMS Brazil Protocols +==================== + +Using the PowerView as source, there is a folder, called `protocols` inside the installed directory, e.g. `"C:\Alerta24h\SMS_Power_View\resource"`. There are 4 protocol files (`monofasico.xml`, `trifasico.xml`, `upsilon.xml` and `voltronic.xml`), and one file called `verificaProtocolo.xml`. + +The file `verificaProtocolo.xml` has serial/USB commands to be sent, to detect the UPS device. + +The file contents are: + +.verificaProtocolo.xml +[source,xml] +---- + + + + + QSG + isNobreakVoltronic + Resgata medidores e status do UPS + + + + + + + + + + + + + + + + G1 + isNobreakTrifasico + Detecta se o no-break Ć© trifĆ”sico + + + + + + + + + + + + + + + + + I + isNobreakMonofasico + Detecta se o no-break Ć© monofĆ”sico + + + + + + + + + + + + + + + + + Q1 + isNobreakSinusTriad + Resgata medidores e status do UPS + + + + + + + + + + + + + +---- + +In the `sms_ser.c` and `sms_ser.h` there is only the `monofasico.xml` implementation, because is describes my UPS and I can implement and validate it. But it is totally valid if someone has another SMS_Brazil UPS and wanted to include another protocol, you can send me an email (looking in the `sms_ser.h` source to get it). + +Here is the `monofasico.xml` protocol: + +.monofasico.xml +[source,xml] +---- + + + + + Q + MedidoresEstado + Resgata medidores e status do UPS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Q + MedidoresEstadoDefault + Resgata um bean com os valores prĆ©-definidos para os medidores e status do UPS + + + + + + + + + + + + + + + + + + + + + + + I + Informacoes + Resgata informaƧƵes do UPS + + + + + + + + + + + + + + + + + + + + I + InformacoesDefault + Resgata informaƧƵes do UPS + The + + + + + + + F + Caracteristicas + Resgata caracterĆ­sticas do UPS + + + + + + + + + + + + + + + + + + + + + F + CaracteristicasDefault + Resgata caracterĆ­sticas default do UPS + + + + + + + + + + + + M + MudaBeep + Muda o beep + + + + + + + + + + + + + L + TesteBateriaBaixa + Teste atĆ© bateria baixa + + + + + + + + + + + + + T + TestePorSegundos + Teste por 'n' segundos + + + + + + + + + + + + + S + ShutdownPorSegundos + Shutdown em n segundos + + + + + + + + + + + + + R + ShutdownRestore + Shutdown e restore + + + + + + + + + + + + + D + CancelarTeste + Cancelamento de Teste + + + + + + + + + + + + + C + CancelarShutdownRestore + Cancelamento de Shutdown ou restore + + + + + + + + + + + +---- diff --git a/docs/snmp-subdrivers.txt b/docs/snmp-subdrivers.txt index f010ff8960..2e124767a4 100644 --- a/docs/snmp-subdrivers.txt +++ b/docs/snmp-subdrivers.txt @@ -224,9 +224,10 @@ To help you, above each entry in -mib.c, there is a comment that 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 }, + { "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 @@ -238,6 +239,12 @@ In the above example, the right NUT variable is obviously "device.model". The MIB definition file (.mib) also contains some description of these OIDs, along with the possible enumerated values. +NOTE: To test existing data points (including those not yet translated +to standard NUT mappings conforming to <>), you can use custom drivers built after you +`./configure --with-unmapped-data-points`. +Production driver builds must not include any non-standard names. + //////////////////////////////////////////////////////////////////////////////// FIXME: diff --git a/docs/snmp.txt b/docs/snmp.txt index 64b9c06f6f..0e058761dd 100644 --- a/docs/snmp.txt +++ b/docs/snmp.txt @@ -47,4 +47,4 @@ manpage (man 8 snmp-ups). References: - NUT SNMP Protocols Library Available at: -http://www.networkupstools.org/ups-protocols.html#_snmp +https://www.networkupstools.org/ups-protocols.html#_snmp diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index e4701e5234..bd440cbc18 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -9,6 +9,24 @@ out changes to their local storage immediately, without any sort of prompting from the server. As a result, the server must always check on any driver sockets for activity. +In terms of communications, each driver is a server on the Unix socket +(or Windows named pipe) which it creates, and the data server `upsd` is +a client which knows where to find such sockets, how they are named, +and connects to all of them to send commands and receive data updates. + +During development, it is possible to use tools like `socat` to connect +to the socket (you may want to enable `NOBROADCAST` mode soon), e.g. + + socat - UNIX-CONNECT:/var/state/ups/dummy-ups-UPS1 + +For more insight, NUT provides an optional tool of its own (not built +by default): the `sockdebug` which is built when `configure --with-dev` +is in effect, or can be requested from the root directory of the build +workspace: + + make sockdebug && \ + ./server/sockdebug dummy-ups-UPS1 + Formatting ---------- @@ -23,6 +41,9 @@ together on its way to the other end. Commands used by the drivers ---------------------------- +These commands (or semantically responses to server commands in some cases) +can be sent by drivers to the data server over the socket protocol. + SETINFO ~~~~~~~ @@ -161,6 +182,10 @@ strings, as per docs/net-protocol.txt GET TRACKING. Commands sent by the server --------------------------- +The data server `upsd` (or technically any client that connects to a Unix +socket or Windows named pipe provided by each NUT driver) can send the +following commands to the driver: + PING ~~~~ @@ -175,6 +200,12 @@ If a driver does not respond with the PONG within a few seconds at the most, it should be treated as dead/unavailable. Data stored in the server must not be passed on to the clients when this happens. +NOTE: For the `upsd` data server, the MAXAGE setting in upsd.conf controls +how long since the last message from the driver it is considered stale. +At 1/3 of this time the server sends a `PING` command to the driver, so +there is some time for a `PONG` to arrive and reset the timer (any other +message would serve that goal as well). + INSTCMD ~~~~~~~ @@ -185,6 +216,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 +231,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. @@ -217,6 +250,22 @@ The server can tell when it has a full copy of the data by waiting for DUMPDONE. That special response from the driver is sent once the entire set has been transmitted. +NOBROADCAST +~~~~~~~~~~~ + +This connection does not want to receive broadcast messages (implemented +by `send_to_all()` method in `dstate.c`). Default is to receive everything. + +BROADCAST (NUM) +~~~~~~~~~~~~~~~ + +This connection specified whether it wants to receive broadcast messages +(implemented by `send_to_all()` method in `dstate.c`), and by default +enables that -- unless disabled by providing an optional zero or negative +numeric argument. Note that initial default is to receive everything, so +this command may be useful for connections that disabled broadcasts at +some point. + Design notes ------------ @@ -260,4 +309,4 @@ Re-establishing communications If the server loses its connection to the driver and later reconnects, it must flush any local storage and start again with DUMPALL. The driver may have changed the internal state considerably during that -time, and anything other approach could leave old elements behind. +time, and any other approach could leave old elements behind. diff --git a/docs/solaris-usb.txt b/docs/solaris-usb.txt index ee658805b8..e96aad085f 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,184 @@ 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. + +Troubleshooting and reconnecting +-------------------------------- + +So... your setup worked nicely, and then one day you see the console flooded +with messages like the following: + + Broadcast Message from nut (???) on n54l Mon May 9 12:05:59... + Communications with UPS innotech@localhost lost + + Broadcast Message from nut (???) on n54l Mon May 9 12:10:55... + UPS innotech@localhost is unavailable + +Unfortunately, some devices "get stuck" on USB level (whether in the chips, +in the OS driver layer, libusb or NUT driver) and their NUT drivers have +to be restarted to regain monitoring, as opposed to intermittent losses +of connectivity that software recovers from automatically. + +As in all systems, you should stop all programs using the connection, +including NUT driver instances that might have been started beside the +wrapping service (SMF). It may be possible to just start the new driver +instance at this point, but if it still does not see the device -- you +have to re-initialize the connection on the OS level. + +As a symptom, attempts to start the NUT driver with elevated debug verbosity +would not even see the device details: +---- + 0.000606 [D1] Saving PID 5187 into /var/run/nut/nutdrv_qx-innotech.pid + 0.000727 [D1] upsdrv_initups... + 0.012065 [D2] Checking device 1 of 2 (0665/5161) + 0.012303 [D1] Failed to open device (0665/5161), skipping: Other error + 0.012394 [D2] Checking device 2 of 2 (099A/610A) +... + 0.020364 [D2] Trying to match device + 0.020586 [D3] match_function_regex: matching a device... + 0.020839 [D2] match_function_regex: failed match of VendorID: 99a + 0.021061 [D2] Device does not match - skipping + 0.021371 [D2] libusb1: No appropriate HID device found +Network UPS Tools - Generic Q* USB/Serial driver 0.32 (2.8.0-20-g535395363) +USB communication driver (libusb 1.0) 0.43 + 0.021720 libusb1: Could not open any HID devices: insufficient permissions on everything + 0.021821 No supported devices found. Please check your device availability with 'lsusb' +and make sure you have an up-to-date version of NUT. If this does not help, +try running the driver with at least 'subdriver', 'vendorid' and 'productid' +options specified. Please refer to the man page for details about these options +(man 8 nutdrv_qx). + +Driver failed to start (exit status=1) +Network UPS Tools - UPS driver controller 2.8.0-20-g535395363 +[ May 9 03:10:01 Method "start" exited with status 1. ] +---- + +NOTE: Details of the service instance life-cycle for the NUT driver may be +seen in its SMF log, e.g. by `less /var/svc/log/*innotech*log`, and to see +in-vivo debugs as the service starts in production mode, use `debug_min = 3` +in the `/etc/nut/ups.conf` file (in global context or in driver section). + +Recycle the USB connection +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In case of Solaris/illumos systems, first stop the respective nut-driver +instance, e.g.: + +---- +:; svcadm disable -ts nut-driver:innotech + +:; ps -ef | grep -Ei 'nut|ups' ; svcs -p innotech + root 10522 1 0 May 06 ? 0:00 /usr/sbin/upsmon + root 16927 1 0 Feb 25 ? 1:20 /usr/lib/nut/bin/nutdrv_qx -a innotech + nut 10257 1 0 May 06 ? 0:39 /usr/sbin/upsd + root 16985 15379 0 11:27:36 pts/1 0:00 grep -Ei nut|ups + nut 10524 10522 0 May 06 ? 0:25 /usr/sbin/upsmon +STATE STIME FMRI +offline 11:26:49 svc:/system/power/nut-driver:innotech + +# In the ps listing above, a driver daemon is seen that was started as +# the root user beside the actual service. It has to be stopped too: +:; kill -9 16927 +---- + +To unconfigure and disconnect the USB link on the OS level, you will +need its attachment point identifier. If you don't know your system's +current layout (it can change with device re-enumeration due to hot +plugging and/or reboots), you can execute `cfgadm -lv`, look for +the "Information" field resembling your UPS brand, and make note of +its "Ap_Id". You can also query a single device to confirm a guess +or your earlier records: +---- +:; cfgadm -lv usb10/1 + +Ap_Id Receptacle Occupant Condition +Information +When Type Busy Phys_Id + +usb10/1 connected configured ok +Mfg: INNO TECH Product: USB to Serial NConfigs: 1 Config: 0 : 20100826 +unavailable usb-input n /devices/pci@0,0/pci103c,1609@13:1 +---- + +Disconnect the device; note that if something (typically a program with +an open connection) still has a hold on the device, the system would +fail to complete the command: +---- +:; cfgadm -c disconnect usb10/1 + +Disconnect the device: /devices/pci@0,0/pci103c,1609@13:1 +This operation will suspend activity on the USB device +Continue (yes/no)? yes +cfgadm: Hardware specific failure: Cannot issue devctl + to ap_id: /devices/pci@0,0/pci103c,1609@13:1 +---- + +If that is the case, run `ps` per above and make sure all NUT driver +daemons are stopped (the data server `upsd` and client upsmon should +be inconsequential in this regard). + +Normally, the reconnection should work like this: +---- +:; cfgadm -c unconfigure usb10/1 +Unconfigure the device: /devices/pci@0,0/pci103c,1609@13:1 +This operation will suspend activity on the USB device +Continue (yes/no)? yes + +:; cfgadm -c disconnect usb10/1 +Disconnect the device: /devices/pci@0,0/pci103c,1609@13:1 +This operation will suspend activity on the USB device +Continue (yes/no)? yes + +:; cfgadm -lv usb10/1 +Ap_Id Receptacle Occupant Condition Information +When Type Busy Phys_Id + +usb10/1 disconnected unconfigured ok +unavailable unknown n /devices/pci@0,0/pci103c,1609@13:1 + +:; cfgadm -c configure usb10/1 +cfgadm: Hardware specific failure: Cannot issue devctl + to ap_id: /devices/pci@0,0/pci103c,1609@13:1 + +# Despite the error above, the device is seen now: +:; cfgadm -lv usb10/1 +Ap_Id Receptacle Occupant Condition +Information +When Type Busy Phys_Id + +usb10/1 connected configured ok +Mfg: INNO TECH Product: USB to Serial NConfigs: 1 Config: 0 : 20100826 +unavailable usb-input n /devices/pci@0,0/pci103c,1609@13:1 + +# ... and the driver can start: +:; svcadm enable innotech +---- + +When everything gets recovered, you should see it: + + Broadcast Message from nut (???) on n54l Mon May 9 12:12:30... + Communications with UPS innotech@localhost established + +and `upsc innotech@localhost` would tell you what it sees :) + +Regular auto-recovery via crontab +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Additional tricks that can help involve `crontab` for regular automated +checks if the device got lost. One is just an attempt to "clear" the +service if its earlier startup failed (repetitively) so SMF gave up: + + * * * * * svcadm clear innotech 2>&1 | grep -v 'is not in a maintenance' + +Another is more complicated and involves some custom scripting: + + 0,5,10,15,20,25,30,35,40,45,50,55 * * * * MODE=optional /etc/nut/reset-ups-usb-solaris.sh + +...where the script would be a copy (customized to your device(s) and +connection points!) of `reset-ups-usb-solaris.sh.sample` from either +`scripts/Solaris/` directory in the NUT sources, or a copy which may be +available in your system, e.g. under the `/usr/share/nut/solaris-init/` +data directory. diff --git a/docs/support.txt b/docs/support.txt index 0cb07afce2..a90e7e5385 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 @@ -87,12 +88,12 @@ Refer to the ifdef::website[] link:docs/developer-guide.chunked/index.html[NUT Developer Guide] for more information, and the chapter on how to -link:docs/developer-guide.chunked/ar01s03.html#_submitting_patches[submit patches]. +link:docs/developer-guide.chunked/developers.html#_submitting_patches[submit patches]. endif::website[] ifndef::website[] linkdoc:developer-guide[NUT Developer Guide] for more information, and the chapter on how to -link:../developer-guide.chunked/ar01s03.html#_submitting_patches[submit patches]. +link:../developer-guide.chunked/developers.html#_submitting_patches[submit patches]. endif::website[] Note that the currently preferable way for ultimate submission of improvements @@ -144,4 +145,4 @@ To report new Devices Dumps Library entries, posting an issue is okay, but posting a link:https://github.com/networkupstools/nut-ddl/pulls[pull request] is a lot better -- easier for maintainers to review and merge any time. For some more detailed instructions about useful DDL reports, please see -link:https://networkupstools.org/ddl/#_file_naming_convention[NUT DDL page]. +link:https://www.networkupstools.org/ddl/#_file_naming_convention[NUT DDL page]. diff --git a/docs/user-manual.txt b/docs/user-manual.txt index 0e96ea4e25..b718b8b668 100644 --- a/docs/user-manual.txt +++ b/docs/user-manual.txt @@ -15,7 +15,7 @@ Units and Solar Controllers. NUT provides many control and monitoring <>, with a uniform control and management interface. -More than 140 different manufacturers, and several thousands of models are +More than 170 different manufacturers, and several thousands of models are <>. This software is the combined effort of many @@ -33,7 +33,7 @@ If you wish to discover how everything came together, have a look at the [[Overview]] -include::../README[] +include::../README.adoc[] [[Features]] @@ -81,7 +81,7 @@ include::download.txt[] [[_installation_instructions]] -include::../INSTALL.nut[] +include::{builddir}../INSTALL.nut.adoc-parsed[] [[Configuration_notes]] @@ -147,7 +147,7 @@ include::nut-names.txt[] Appendix D: Hardware Compatibility List ======================================= -Refer to the link:http://www.networkupstools.org/stable-hcl.html[online HCL]. +Refer to the link:https://www.networkupstools.org/stable-hcl.html[online HCL]. Appendix E: Documentation @@ -185,7 +185,7 @@ include::configure.txt[] Appendix I: Upgrading notes =========================== -include::../UPGRADING[] +include::{builddir}../UPGRADING.adoc-parsed[] [[Project_History]] diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 67650a9f99..0aed9bc521 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -1,20 +1,30 @@ # Network UPS Tools: drivers +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + # Make sure out-of-dir dependencies exist (especially when dev-building parts): $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libparseconf.la \ $(top_builddir)/clients/libupsclient.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) # by default, link programs in this directory with libcommon.la # (libtool version of the static lib, in order to access LTLIBOBJS) #FIXME: SERLIBS is only useful for LDADD_DRIVERS_SERIAL not for LDADD_COMMON LDADD_COMMON = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la -LDADD_DRIVERS = libdummy.la $(LDADD_COMMON) +LDADD_DRIVERS = libdummy.la libdummy_upsdrvquery.la $(LDADD_COMMON) LDADD_DRIVERS_SERIAL = libdummy_serial.la $(LDADD_DRIVERS) $(SERLIBS) -# most targets are drivers, so make this the default +# most targets are serial drivers, so make this the default LDADD = $(LDADD_DRIVERS_SERIAL) + # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. @@ -31,16 +41,23 @@ endif if WITH_IPMI AM_CFLAGS += $(LIBIPMI_CFLAGS) endif +if WITH_GPIO + AM_CFLAGS += $(LIBGPIO_CFLAGS) +endif if WITH_MODBUS AM_CFLAGS += $(LIBMODBUS_CFLAGS) endif +if HAVE_LIBREGEX + AM_CFLAGS += $(LIBREGEX_CFLAGS) +endif +NUTSW_DRIVERLIST = dummy-ups clone clone-outlet apcupsd-ups skel SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ - bestfortress bestuferrups bestups dummy-ups etapro everups \ + bestfortress bestuferrups bestups etapro everups \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ mge-utalk microdowell microsol-apc mge-shut oneac optiups powercom rhino \ - safenet nutdrv_siemens-sitop skel solis tripplite tripplitesu upscode2 victronups powerpanel \ - blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser + safenet nutdrv_siemens-sitop solis tripplite tripplitesu upscode2 victronups powerpanel \ + blazer_ser ivtscd apcsmart apcsmart-old riello_ser sms_ser SNMP_DRIVERLIST = snmp-ups if WITH_DMFMIB SNMP_DRIVERLIST += snmp-ups-dmf @@ -53,21 +70,24 @@ 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 apc_modbus LINUX_I2C_DRIVERLIST = asem pijuice POWERMAN_DRIVERLIST = powerman-pdu IPMI_DRIVERLIST = nut-ipmipsu +GPIO_DRIVERLIST = generic_gpio_libgpiod # distribute all drivers, even ones that are not built by default EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(USB_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) EXTRA_PROGRAMS += $(SNMP_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) EXTRA_PROGRAMS += $(LINUX_I2C_DRIVERLIST) +EXTRA_PROGRAMS += $(NUTSW_DRIVERLIST) +EXTRA_PROGRAMS += $(GPIO_DRIVERLIST) # construct the list of drivers to build if SOME_DRIVERS driverexec_PROGRAMS = $(DRIVER_BUILD_LIST) else - driverexec_PROGRAMS = + driverexec_PROGRAMS = $(NUTSW_DRIVERLIST) if WITH_SERIAL driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) else @@ -90,6 +110,9 @@ endif if WITH_IPMI driverexec_PROGRAMS += $(IPMI_DRIVERLIST) endif +if WITH_GPIO + driverexec_PROGRAMS += $(GPIO_DRIVERLIST) +endif if WITH_MACOSX driverexec_PROGRAMS += $(MACOSX_DRIVERLIST) endif @@ -111,11 +134,12 @@ sbin_PROGRAMS = upsdrvctl # upsdrvctl: the all-singing all-dancing driver control program upsdrvctl_SOURCES = upsdrvctl.c -upsdrvctl_LDADD = $(LDADD_COMMON) +upsdrvctl_LDADD = $(LDADD_COMMON) libdummy_upsdrvquery.la # serial drivers: all of them use standard LDADD and CFLAGS al175_SOURCES = al175.c apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c +apcsmart_LDADD = $(LDADD) $(LIBREGEX_LIBS) apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c bcmxcp_LDADD = $(LDADD) -lm @@ -163,10 +187,12 @@ upscode2_LDADD = $(LDADD) -lm victronups_SOURCES = victronups.c riello_ser_SOURCES = riello.c riello_ser.c riello_ser_LDADD = $(LDADD) -lm +sms_ser_SOURCES = sms_ser.c +sms_ser_LDADD = $(LDADD) -lm # non-serial drivers: these use custom LDADD and/or CFLAGS -# dummy +# dummy (in NUTSW_DRIVERLIST) dummy_ups_SOURCES = dummy-ups.c dummy_ups_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients dummy_ups_LDADD = $(LDADD_DRIVERS) $(top_builddir)/clients/libupsclient.la @@ -175,16 +201,16 @@ if WITH_SSL dummy_ups_LDADD += $(LIBSSL_LIBS) endif -# Clone drivers +# Clone drivers (in NUTSW_DRIVERLIST) clone_SOURCES = clone.c clone_outlet_SOURCES = clone-outlet.c -# apcupsd client driver +# apcupsd client driver (in NUTSW_DRIVERLIST) apcupsd_ups_SOURCES = apcupsd-ups.c apcupsd_ups_CFLAGS = $(AM_CFLAGS) apcupsd_ups_LDADD = $(LDADD_DRIVERS) -# sample skeleton driver +# sample skeleton driver (in NUTSW_DRIVERLIST) skel_SOURCES = skel.c skel_LDADD = $(LDADD_DRIVERS) @@ -215,7 +241,7 @@ blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm nutdrv_atcl_usb_SOURCES = nutdrv_atcl_usb.c usb-common.c nutdrv_atcl_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -richcomm_usb_SOURCES = richcomm_usb.c usb-common.c +richcomm_usb_SOURCES = richcomm_usb.c $(LIBUSB_IMPL) usb-common.c richcomm_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) riello_usb_SOURCES = riello.c riello_usb.c $(LIBUSB_IMPL) usb-common.c @@ -231,19 +257,19 @@ mge_shut_LDADD = $(LDADD) -lm # Please keep the MIB table below sorted roughly alphabetically (incidentally # by vendor too) to ease maintenance and codebase fork resynchronisations snmp_ups_SOURCES = snmp-ups.c snmp-ups-helpers.c \ - apc-mib.c apc-pdu-mib.c \ + apc-mib.c apc-pdu-mib.c apc-epdu-mib.c \ baytech-mib.c bestpower-mib.c \ compaq-mib.c cyberpower-mib.c \ delta_ups-mib.c \ eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ - eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c \ + eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c eaton-pdu-nlogic-mib.c \ eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ + eaton-ups-pwnm2-mib.c eaton-ups-pxg-mib.c \ emerson-avocent-pdu-mib.c \ - hpe-pdu-mib.c huawei-mib.c \ + hpe-pdu-mib.c hpe-pdu3-cis-mib.c huawei-mib.c \ ietf-mib.c \ mge-mib.c \ netvision-mib.c \ - powerware-mib.c \ raritan-pdu-mib.c raritan-px2-mib.c \ xppc-mib.c snmp_ups_CFLAGS = $(AM_CFLAGS) @@ -309,18 +335,39 @@ 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) + +# APC Modbus driver (with support of modbus over different media) +# Note that a version of libmodbus built with USB support is also needed +# for USB connections. Legacy versions work for Serial and TCP links. +apc_modbus_SOURCES = apc_modbus.c +apc_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +if WITH_USB + apc_modbus_SOURCES += $(LIBUSB_IMPL) hidparser.c usb-common.c + apc_modbus_LDADD += $(LIBUSB_LIBS) +endif # 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_LDADD = $(LDADD_DRIVERS) $(LIBI2C_LIBS) asem_SOURCES = asem.c -pijuice_LDADD = $(LDADD_DRIVERS) +pijuice_LDADD = $(LDADD_DRIVERS) $(LIBI2C_LIBS) pijuice_SOURCES = pijuice.c +# GPIO drivers +generic_gpio_libgpiod_SOURCES = generic_gpio_common.c generic_gpio_libgpiod.c +generic_gpio_libgpiod_LDADD = $(LDADD_DRIVERS) $(LIBGPIO_LIBS) + # nutdrv_qx USB/Serial nutdrv_qx_SOURCES = nutdrv_qx.c nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm @@ -347,15 +394,16 @@ nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS) # tracking (which is automatic), but to ensure these files are # distributed by "make dist". -dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h \ - baytech-mib.h bcmxcp.h \ - bcmxcp_ser.h bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h \ - dstate.h dummy-ups.h explore-hid.h gamatronic.h genericups.h \ +dist_noinst_HEADERS = \ + apc_modbus.h apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ + bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ + dummy-ups.h explore-hid.h gamatronic.h genericups.h \ + generic_gpio_common.h generic_gpio_libgpiod.h \ hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h nut_libusb.h liebert-hid.h \ main.h mge-hid.h mge-mib.h mge-utalk.h \ mge-xml.h microdowell.h microsol-apc.h microsol-common.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ - powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ - safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ + powercom.h powerpanel.h powerp-bin.h powerp-txt.h raritan-pdu-mib.h \ + safenet.h serial.h sms_ser.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \ delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h \ @@ -363,11 +411,11 @@ dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h \ nutdrv_qx_mecer.h nutdrv_qx_ablerex.h \ nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ + upsdrvquery.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 + apc-pdu-mib.h apc-epdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ + eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h eaton-ups-pwnm2-mib.h eaton-ups-pxg-mib.h legrand-hid.h \ + hpe-pdu-mib.h hpe-pdu3-cis-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi.h eaton-pdu-nlogic-mib.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, @@ -375,18 +423,30 @@ dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h \ # because per-object CFLAGS can only be specified for libraries, not # for object files. This library is used during the build process, # and is not meant to be installed. -EXTRA_LTLIBRARIES = libdummy.la libdummy_serial.la +EXTRA_LTLIBRARIES = libdummy.la libdummy_serial.la libdummy_upsdrvquery.la + libdummy_la_SOURCES = main.c dstate.c libdummy_la_LDFLAGS = -no-undefined -static libdummy_serial_la_SOURCES = serial.c libdummy_serial_la_LDFLAGS = -no-undefined -static +libdummy_upsdrvquery_la_SOURCES = upsdrvquery.c +libdummy_upsdrvquery_la_LDFLAGS = -no-undefined -static + +# The libdummy_mockdrv.la is used for unit-tests to mock a driver +# with near-production codebase but without its standard main(). +# Otherwise, also not meant to be installed. +EXTRA_LTLIBRARIES += libdummy_mockdrv.la +libdummy_mockdrv_la_SOURCES = main.c dstate.c +libdummy_mockdrv_la_CFLAGS = $(AM_CFLAGS) -DDRIVERS_MAIN_WITHOUT_MAIN=1 +libdummy_mockdrv_la_LDFLAGS = -static $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la # Also define a library with serial-port UPS routines needed for nut-scanner noinst_LTLIBRARIES = libserial-nutscan.la libserial_nutscan_la_SOURCES = serial.c bcmxcp_ser.c -libserial_nutscan_la_LDFLAGS = $(SERLIBS) -libserial_nutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include -I$(top_srcdir)/drivers +libserial_nutscan_la_LDFLAGS = +libserial_nutscan_la_LIBADD = $(SERLIBS) +libserial_nutscan_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients -I$(top_srcdir)/include -I$(top_srcdir)/drivers dummy: @@ -396,4 +456,4 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! #clean-local: -# rm -rf $(builddir)/.deps +# $(AM_V_at)rm -rf $(builddir)/.deps diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c new file mode 100644 index 0000000000..3f2135b932 --- /dev/null +++ b/drivers/adelsystem_cbi.c @@ -0,0 +1,1363 @@ +/* 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.02" + +/* 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_STRING; /* device type (e.g. UPS, PDU...) */ +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 */ + uint16_t mask8 = 0x00FF; + uint16_t mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint16_t *)data = *(uint16_t *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint16_t *)data = *(uint16_t *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint16_t *)data = *(uint16_t *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint16_t *)data = *(uint16_t *)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: %u",addr, type, *(unsigned int *)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 */ + uint16_t mask8 = 0x00FF; + uint16_t mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint16_t *)data = *(uint16_t *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint16_t *)data = *(uint16_t *)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: %u",addr, type, *(unsigned int *)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 */ + unsigned int reg_val; /* register value */ +#if READALL_REGS == 0 + unsigned int 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) { + char *fval_s; + double fval; + + state->reg.val.ui16 = reg_val; + fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + 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) { + char *reg_val_s; + + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + 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) { + double fval; + char *fval_s; + + state->reg.val.ui16 = reg_val; + fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + 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" */ + { /* scoping */ + double fval; + char *fval_s; + + state->reg.val.ui16 = reg_val; + fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + 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: + { /* scoping */ + char *reg_val_s; + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + 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"); + dstate_setinfo("driver.state", "reconnect.trying"); + + /* 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_* */ + + dstate_setinfo("driver.state", "quiet"); +} + diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h new file mode 100644 index 0000000000..11fb938e2a --- /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 "nut_stdint.h" + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEM" +#define DEVICE_TYPE_STRING "DC-UPS" +#define DEVICE_MODEL "CBI2801224A" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#ifndef READALL_REGS +#define READALL_REGS 1 +#endif + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union val { + uint16_t ui16; + uint8_t ui8; + } val; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t *alrm; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* Allocate alarm arrays */ +static inline +alrm_ar_t *alloc_alrm_ar(int as, size_t n) +{ + alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); + if (ret) { + memcpy(ret, + &(alrm_ar_t const) { + .alrm_c = as + }, + sizeof(alrm_ar_t) + ); + } + return ret; +} + +/* Initialize alarm arrays */ +static inline +void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) +{ + ar_ptr->alrm_c = as; + ar_ptr->alrm = a_ptr; +} + +/* input mains and shutdown alarms */ +static alrm_t mains_ar[] = { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} +}; +static int mains_c = 2; +static alrm_ar_t *mains; + +/* AC input voltage alarms */ +static alrm_t vaca_ar[] = { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} +}; +static int vaca_c = 2; +static alrm_ar_t *vaca; + +/* device failure alarms */ +static alrm_t devf_ar[] = { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} +}; +static int devf_c = 3; +static alrm_ar_t *devf; + +/* battery sensor failure alarms */ +static alrm_t btsf_ar[] = { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} +}; +static int btsf_c = 2; +static alrm_ar_t *btsf; + +/* battery voltage alarms */ +static alrm_t bval_ar[] = { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} +}; +static int bval_c = 3; +static alrm_ar_t *bval; + +/* battery SoH and SoC alarms */ +static alrm_t shsc_ar[] = { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} +}; +static int shsc_c = 4; +static alrm_ar_t *shsc; + +/* battery status alarm */ +static alrm_t bsta_ar[] = { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} +}; +static int bsta_c = 4; +static alrm_ar_t *bsta; + +/* onboard temperature alarm */ +static alrm_t obta_ar[] = { + {0, "onboard temperature high"} +}; +static int obta_c = 4; +static alrm_ar_t *obta; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEM CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ +}; +#endif /* ADELSYSTEM_CBI_H */ + diff --git a/drivers/al175.c b/drivers/al175.c index f014435df2..6ed311a8dc 100644 --- a/drivers/al175.c +++ b/drivers/al175.c @@ -52,7 +52,7 @@ typedef uint8_t byte_t; #define DRIVER_NAME "Eltek AL175/COMLI driver" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -518,7 +518,7 @@ static int al_parse_reply_head(io_head_t *io, const raw_data_t raw_reply_head) } if (io_len > IO_LEN_MAX) { - upsdebugx(3, "nob too big\t(%zu > %i)", io_len, IO_LEN_MAX); + upsdebugx(3, "nob too big\t(%" PRIuSIZE " > %i)", io_len, IO_LEN_MAX); return -1; /* too much data claimed */ } @@ -553,9 +553,9 @@ static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_ * begin end */ - int err; - size_t i; - const byte_t *reply = NULL; + int err; + size_t i, io_buf_len; + const byte_t *reply = NULL; /* 1: extract header and parse it */ /*const*/ raw_data_t raw_reply_head = raw_reply; @@ -572,15 +572,15 @@ static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_ reply = raw_reply.begin - 1; if ( (raw_reply.end - raw_reply.begin) != (ptrdiff_t)(10 + io_head->len)) { - upsdebugx(3, "%s: corrupt sentence\t(%i != %zi)", + upsdebugx(3, "%s: corrupt sentence\t(%i != %" PRIiSIZE ")", __func__, (int)(raw_reply.end - raw_reply.begin), 10 + io_head->len); return -1; /* corrupt sentence */ } - /* extract the data */ + /* 3: extract the data */ if (io_buf->buf_size < io_head->len) { - upsdebugx(3, "%s: too much data to fit in io_buf\t(%zu > %zu)", + upsdebugx(3, "%s: too much data to fit in io_buf\t(%" PRIuSIZE " > %" PRIuSIZE ")", __func__, io_head->len, io_buf->buf_size); return -1; /* too much data to fit in io_buf */ } @@ -592,7 +592,7 @@ static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_ *(io_buf->end++) = reply[11+i]; assert(io_buf->end - io_buf->begin >= 0); - size_t io_buf_len = (size_t)(io_buf->end - io_buf->begin); + io_buf_len = (size_t)(io_buf->end - io_buf->begin); reverse_bits(io_buf->begin, io_buf_len ); upsdebug_hex(3, "\t\t--> payload", io_buf->begin, io_buf_len); @@ -670,14 +670,15 @@ static int al_check_ack(/*const*/ raw_data_t raw_ack) /* clear any flow control (copy from powercom.c) */ static void ser_disable_flow_control (void) { - struct termios tio; + struct termios tio; + tcflag_t x; tcgetattr (upsfd, &tio); /* Clumsy rewrite of a one-liner * tio.c_iflag &= ~ (IXON | IXOFF); * to avoid type conversion warnings */ - tcflag_t x = (IXON | IXOFF); + x = (IXON | IXOFF); tio.c_iflag &= ~ x; tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; @@ -688,7 +689,7 @@ static void ser_disable_flow_control (void) tcsetattr(upsfd, TCSANOW, &tio); } -static void flush_rx_queue() +static void flush_rx_queue(void) { ser_flush_in(upsfd, "", /*verbose=*/nut_debug_level); } @@ -702,10 +703,11 @@ static void flush_rx_queue() */ static int tx(const char *dmsg, /*const*/ raw_data_t frame) { - ssize_t err; + ssize_t err; + size_t frame_len; assert(frame.end - frame.begin >= 0); - size_t frame_len = (size_t)(frame.end - frame.begin); + frame_len = (size_t)(frame.end - frame.begin); upsdebug_ascii(3, dmsg, frame.begin, frame_len); @@ -807,9 +809,9 @@ static int scan_for(char c) * * @return 0 (ok) -1 (error) */ -static int recv_command_ack() +static int recv_command_ack(void) { - ssize_t err; + ssize_t err; raw_data_t ack; byte_t ack_buf[8]; @@ -855,12 +857,12 @@ static int recv_command_ack() */ static int recv_register_data(io_head_t *io, raw_data_t *io_buf) { - ssize_t err; - int ret; - raw_data_t reply_head; - raw_data_t reply; - - byte_t reply_head_buf[11]; + ssize_t err; + int ret; + size_t reply_head_len; + raw_data_t reply_head; + raw_data_t reply; + byte_t reply_head_buf[11]; /* 1: STX */ err = scan_for(STX); @@ -891,13 +893,13 @@ static int recv_register_data(io_head_t *io, raw_data_t *io_buf) reply_head.begin -= 1; /* restore STX */ - upsdebugx(4, "\t\t--> addr: 0x%zx len: 0x%zx", io->addr, io->len); + upsdebugx(4, "\t\t--> addr: 0x%" PRIxSIZE " len: 0x%" PRIxSIZE, io->addr, io->len); /* 4: allocate space for full reply and copy header there */ reply = raw_xmalloc(11/*head*/ + io->len/*data*/ + 2/*ETX BCC*/); assert (reply_head.end - reply_head.begin >= 0); - size_t reply_head_len = (size_t)(reply_head.end - reply_head.begin); + reply_head_len = (size_t)(reply_head.end - reply_head.begin); memcpy(reply.end, reply_head.begin, reply_head_len); reply.end += reply_head_len; @@ -905,7 +907,7 @@ static int recv_register_data(io_head_t *io, raw_data_t *io_buf) /* 5: receive tail of the frame */ err = get_buf(reply.end, io->len + 2); if (err!=(int)(io->len+2)) { - upsdebugx(4, "rx_tail failed, err=%zi (!= %zi)", err, io->len+2); + upsdebugx(4, "rx_tail failed, err=%" PRIiSIZE " (!= %" PRIiSIZE ")", err, io->len+2); ret = -1; goto out; } @@ -1005,7 +1007,7 @@ static int al175_read(byte_t *dst, size_t addr, size_t count) return -1; if ( (io.addr != addr) || (io.len != count) ) { - upsdebugx(3, "%s: io_head mismatch\t(%zx,%zx != %zx,%zx)", + upsdebugx(3, "%s: io_head mismatch\t(%" PRIxSIZE ",%" PRIxSIZE " != %" PRIxSIZE ",%" PRIxSIZE ")", __func__, io.addr, io.len, addr, count); return -1; } @@ -1053,9 +1055,9 @@ ACT SWITCH_TEMP_COMP (uint16_t on); ACT SWITCH_SYM_ALARM (void); /* Implement */ -ACT TOGGLE_PRS_ONOFF () { return al175_do(0x81, 0x80 Z3); } -ACT CANCEL_BOOST () { return al175_do(0x82, 0x80 Z3); } -ACT STOP_BATTERY_TEST () { return al175_do(0x83, 0x80 Z3); } +ACT TOGGLE_PRS_ONOFF (void) { return al175_do(0x81, 0x80 Z3); } +ACT CANCEL_BOOST (void) { return al175_do(0x82, 0x80 Z3); } +ACT STOP_BATTERY_TEST (void) { return al175_do(0x83, 0x80 Z3); } ACT START_BATTERY_TEST (VV_t EndVolt, mm_t Minutes) { return al175_do(0x83, 0x81, EndVolt, Minutes Z1); } @@ -1068,8 +1070,8 @@ ACT SET_DISCONNECT_LEVEL_AND_DELAY (VV_t level, mm_t delay) { return al175_do(0x87, 0x84, level, delay Z1); } -ACT RESET_ALARMS () { return al175_do(0x88, 0x80 Z3); } -ACT CHANGE_COMM_PROTOCOL () { return al175_do(0x89, 0x80 Z3); } +ACT RESET_ALARMS (void) { return al175_do(0x88, 0x80 Z3); } +ACT CHANGE_COMM_PROTOCOL (void) { return al175_do(0x89, 0x80 Z3); } ACT SET_VOLTAGE_AT_ZERO_T (VV_t v) { return al175_do(0x8a, 0x80, v Z2); } ACT SET_SLOPE_AT_ZERO_T (VV_t mv_per_degree) { return al175_do(0x8a, 0x81, mv_per_degree Z2); } @@ -1078,7 +1080,7 @@ ACT SET_MAX_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x82, v Z2); } ACT SET_MIN_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x83, v Z2); } ACT SWITCH_TEMP_COMP (uint16_t on) { return al175_do(0x8b, 0x80, on Z2); } -ACT SWITCH_SYM_ALARM () { return al175_do(0x8c, 0x80 Z3); } +ACT SWITCH_SYM_ALARM (void) { return al175_do(0x8c, 0x80 Z3); } /** @@ -1263,9 +1265,6 @@ void upsdrv_updateinfo(void) } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* TODO use TOGGLE_PRS_ONOFF for shutdown */ @@ -1276,7 +1275,8 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ @@ -1286,7 +1286,6 @@ void upsdrv_shutdown(void) /* OB: the load must remain off until the power returns */ } - static int instcmd(const char *cmdname, const char *extra) { int err; diff --git a/drivers/apc-ats-mib.c b/drivers/apc-ats-mib.c index 3679a67641..c172f3445d 100644 --- a/drivers/apc-ats-mib.c +++ b/drivers/apc-ats-mib.c @@ -24,485 +24,429 @@ #include "apc-ats-mib.h" -#define APC_ATS_MIB_VERSION "0.5" +#define APC_ATS_MIB_VERSION "0.60" #define APC_ATS_SYSOID ".1.3.6.1.4.1.318.1.3.11" #define APC_ATS_OID_MODEL_NAME ".1.3.6.1.4.1.318.1.1.8.1.5.0" static info_lkp_t apc_ats_sensitivity_info[] = { - { 1, "high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "high"), + info_lkp_default(2, "low"), + info_lkp_sentinel }; static info_lkp_t apc_ats_output_status_info[] = { - { 1, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* fail */ - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* ok */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OFF"), /* fail */ + info_lkp_default(2, "OL"), /* ok */ + info_lkp_sentinel }; static info_lkp_t apc_ats_outletgroups_name_info[] = { - { 1, "total" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "bank1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "bank2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "total"), + info_lkp_default(2, "bank1"), + info_lkp_default(3, "bank2"), + info_lkp_sentinel }; static info_lkp_t apc_ats_outletgroups_status_info[] = { - { 1, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* normal */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* lowload */ - { 3, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* nearoverload */ - { 4, "OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* overload */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OL"), /* normal */ + info_lkp_default(2, ""), /* lowload */ + info_lkp_default(3, ""), /* nearoverload */ + info_lkp_default(4, "OVER"), /* overload */ + info_lkp_sentinel }; /* APC ATS Snmp2NUT lookup table */ static snmp_info_t apc_ats_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* ats2IdentManufacturer.0 = STRING: EATON */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* atsIdentModelNumber.0 = STRING: "AP7724" */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APC_ATS_OID_MODEL_NAME, NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, APC_ATS_OID_MODEL_NAME, NULL, SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware! */ /* atsIdentHardwareRev.0 = STRING: "R01" */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.1.0", NULL, SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware.aux! */ /* atsIdentFirmwareRev.0 = STRING: "3.0.5" */ - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.2.0", NULL, SU_FLAG_OK, NULL), /* atsIdentFirmwareDate.0 = STRING: "09/13/11" */ - /*{ "unmapped.atsIdentFirmwareDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.3.0", NULL, SU_FLAG_OK, NULL },*/ + /*snmp_info_default("unmapped.atsIdentFirmwareDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.3.0", NULL, SU_FLAG_OK, NULL),*/ /* atsIdentSerialNumber.0 = STRING: "5A1516T15268" */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.6.0", NULL, SU_FLAG_OK, NULL), /* FIXME: RFC for device.mfr.date! */ /* atsIdentDateOfManufacture.0 = STRING: "04/18/2015" */ - { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.4.0", NULL, SU_FLAG_OK, NULL), /* atsConfigProductName.0 = STRING: "m-ups-04" */ - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.1.0", NULL, SU_FLAG_OK, NULL), /* Input collection */ /* atsIdentNominalLineVoltage.0 = INTEGER: 230 */ - { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.7.0", NULL, SU_FLAG_OK, NULL), /* atsIdentNominalLineFrequency.0 = INTEGER: 50 */ - { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.8.0", NULL, SU_FLAG_OK, NULL), /* atsStatusSelectedSource.0 = INTEGER: sourceB(2) */ - { "input.source", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.source", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.2.0", NULL, SU_FLAG_OK, NULL), /* atsConfigPreferredSource.0 = INTEGER: sourceB(2) */ - { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.2.0", NULL, SU_FLAG_OK, NULL), /* atsInputVoltage.1.1.1 = INTEGER: 216 */ - { "input.1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputVoltage.2.1.1 = INTEGER: 215 */ - { "input.2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputFrequency.1 = INTEGER: 50 */ - { "input.1.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* atsInputFrequency.2 = INTEGER: 50 */ - { "input.2.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* atsConfigVoltageSensitivity.0 = INTEGER: high(1) */ - { "input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.4.0", NULL, SU_FLAG_OK, &apc_ats_sensitivity_info[0] }, + snmp_info_default("input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.4.0", NULL, SU_FLAG_OK, &apc_ats_sensitivity_info[0]), /* FIXME: RFC for input.count! */ /* atsNumInputs.0 = INTEGER: 2 */ - { "input.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.1.0", NULL, SU_FLAG_OK, NULL), /* Output collection */ /* atsOutputFrequency.1 = INTEGER: 50 */ - { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankOutputVoltage.1 = INTEGER: 215 */ - { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.1", NULL, SU_FLAG_OK, NULL), /* UPS collection */ /* FIXME: RFC for device.status! */ /* atsStatusVoltageOutStatus.0 = INTEGER: ok(2) */ - { "ups.status", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.15.0", NULL, SU_FLAG_OK, &apc_ats_output_status_info[0] }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.15.0", NULL, SU_FLAG_OK, &apc_ats_output_status_info[0]), /* Outlet groups collection */ /* Note: prefer the OutputBank data to the ConfigBank ones */ /* atsConfigBankTableSize.0 = INTEGER: 3 */ - /*{ "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.13.0", NULL, SU_FLAG_OK, NULL },*/ + /*snmp_info_default("outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.13.0", NULL, SU_FLAG_OK, NULL),*/ /* atsOutputBankTableSize.0 = INTEGER: 3 */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.4.0", NULL, SU_FLAG_OK, NULL), /* atsConfigBankTableIndex.%i = INTEGER: %i */ - /*{ "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.14.1.1.%i", NULL, SU_FLAG_OK, NULL },*/ + /*snmp_info_default("outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.14.1.1.%i", NULL, SU_FLAG_OK, NULL),*/ /* atsOutputBankTableIndex.%i = INTEGER: %i */ - { "outlet.group.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.1.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP, NULL }, + snmp_info_default("outlet.group.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.1.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP, NULL), /* atsConfigBank.%i = INTEGER: total(1) */ - /*{ "outlet.group.%i.name", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.2.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_group_name_info[0] },*/ + /*snmp_info_default("outlet.group.%i.name", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.2.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_group_name_info[0]),*/ /* atsOutputBank.1 = INTEGER: total(1) */ - { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.3.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_outletgroups_name_info[0] }, + snmp_info_default("outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.3.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_outletgroups_name_info[0]), /* atsOutputBankCurrent.%i = Gauge32: 88 */ - { "outlet.group.%i.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.4.%i", NULL, SU_OUTLET_GROUP, NULL }, + snmp_info_default("outlet.group.%i.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.4.%i", NULL, SU_OUTLET_GROUP, NULL), /* atsOutputBankState.%i = INTEGER: normal(1) */ - { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.5.%i", NULL, SU_OUTLET_GROUP, &apc_ats_outletgroups_status_info[0] }, + snmp_info_default("outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.5.%i", NULL, SU_OUTLET_GROUP, &apc_ats_outletgroups_status_info[0]), /* atsOutputBankOutputVoltage.%i = INTEGER: 215 */ - { "outlet.group.%i.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.%i", NULL, SU_OUTLET_GROUP, NULL }, + snmp_info_default("outlet.group.%i.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.%i", NULL, SU_OUTLET_GROUP, NULL), /* atsOutputBankPower.1 = INTEGER: 1883 */ - { "outlet.group.%i.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.15.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL }, - + snmp_info_default("outlet.group.%i.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.15.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL), -#if 0 /* FIXME: Remaining data to be processed */ +#if WITH_UNMAPPED_DATA_POINTS /* FIXME: Remaining data to be processed */ /* atsIdentDeviceRating.0 = INTEGER: 32 */ - { "unmapped.atsIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.9.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationNumInputs.0 = INTEGER: 2 */ - { "unmapped.atsCalibrationNumInputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationNumInputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.1.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationNumInputPhases.0 = INTEGER: 1 */ - { "unmapped.atsCalibrationNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.2.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationInputTableIndex.1.1.1 = INTEGER: 1 */ - { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationInputTableIndex.2.1.1 = INTEGER: 2 */ - { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ - { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationInputPhaseTableIndex.2.1.1 = INTEGER: 1 */ - { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsLineVoltageCalibrationFactor.1.1.1 = INTEGER: 487 */ - { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsLineVoltageCalibrationFactor.2.1.1 = INTEGER: 488 */ - { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltages.0 = INTEGER: 5 */ - { "unmapped.atsCalibrationPowerSupplyVoltages", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltages", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.1.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltageTableIndex.1 = INTEGER: 1 */ - { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltageTableIndex.2 = INTEGER: 2 */ - { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltageTableIndex.3 = INTEGER: 3 */ - { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.3", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltageTableIndex.4 = INTEGER: 4 */ - { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.4", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltageTableIndex.5 = INTEGER: 5 */ - { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.5", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltage.1 = INTEGER: powerSupply24V(1) */ - { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltage.2 = INTEGER: powerSupply12V(2) */ - { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltage.3 = INTEGER: powerSupply(3) */ - { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.3", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltage.4 = INTEGER: powerSupply24VSourceB(4) */ - { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.4", NULL, SU_FLAG_OK, NULL), /* atsCalibrationPowerSupplyVoltage.5 = INTEGER: powerSupplyMinus12V(5) */ - { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.5", NULL, SU_FLAG_OK, NULL), /* atsPowerSupplyVoltageCalibrationFactor.1 = INTEGER: 521 */ - { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* atsPowerSupplyVoltageCalibrationFactor.2 = INTEGER: 1076 */ - { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* atsPowerSupplyVoltageCalibrationFactor.3 = INTEGER: 2560 */ - { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.3", NULL, SU_FLAG_OK, NULL), /* atsPowerSupplyVoltageCalibrationFactor.4 = INTEGER: 521 */ - { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.4", NULL, SU_FLAG_OK, NULL), /* atsPowerSupplyVoltageCalibrationFactor.5 = INTEGER: 975 */ - { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.5", NULL, SU_FLAG_OK, NULL), /* atsCalibrationNumOutputs.0 = INTEGER: 1 */ - { "unmapped.atsCalibrationNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.1.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationNumOutputPhases.0 = INTEGER: 1 */ - { "unmapped.atsCalibrationNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.2.0", NULL, SU_FLAG_OK, NULL), /* atsCalibrationOutputTableIndex.1.phase1.1 = INTEGER: 1 */ - { "unmapped.atsCalibrationOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsCalibrationOutputPhasesTableIndex.1.phase1.1 = INTEGER: phase1(1) */ - { "unmapped.atsCalibrationOutputPhasesTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsCalibrationOutputPhasesTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsOutputCurrentCalibrationFactor.1.phase1.1 = INTEGER: 487 */ - { "unmapped.atsOutputCurrentCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputCurrentCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsControlResetATS.0 = INTEGER: none(1) */ - { "unmapped.atsControlResetATS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsControlResetATS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.1.0", NULL, SU_FLAG_OK, NULL), /* atsControlClearAllAlarms.0 = INTEGER: -1 */ - { "unmapped.atsControlClearAllAlarms", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsControlClearAllAlarms", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.2.0", NULL, SU_FLAG_OK, NULL), /* atsConfigFrontPanelLockout.0 = INTEGER: enableFrontPanel(2) */ - { "unmapped.atsConfigFrontPanelLockout", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigFrontPanelLockout", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.3.0", NULL, SU_FLAG_OK, NULL), /* atsConfigTransferVoltageRange.0 = INTEGER: medium(2) */ - { "unmapped.atsConfigTransferVoltageRange", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigTransferVoltageRange", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.5.0", NULL, SU_FLAG_OK, NULL), /* atsConfigCurrentLimit.0 = INTEGER: 32 */ - { "unmapped.atsConfigCurrentLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigCurrentLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.6.0", NULL, SU_FLAG_OK, NULL), /* atsConfigResetValues.0 = INTEGER: -1 */ - { "unmapped.atsConfigResetValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigResetValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.7.0", NULL, SU_FLAG_OK, NULL), /* atsConfigLineVRMS.0 = INTEGER: 230 */ - { "unmapped.atsConfigLineVRMS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigLineVRMS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.8.0", NULL, SU_FLAG_OK, NULL), /* atsConfigLineVRMSNarrowLimit.0 = INTEGER: 16 */ - { "unmapped.atsConfigLineVRMSNarrowLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigLineVRMSNarrowLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.9.0", NULL, SU_FLAG_OK, NULL), /* atsConfigLineVRMSMediumLimit.0 = INTEGER: 23 */ - { "unmapped.atsConfigLineVRMSMediumLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigLineVRMSMediumLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.10.0", NULL, SU_FLAG_OK, NULL), /* atsConfigLineVRMSWideLimit.0 = INTEGER: 30 */ - { "unmapped.atsConfigLineVRMSWideLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigLineVRMSWideLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.11.0", NULL, SU_FLAG_OK, NULL), /* atsConfigFrequencyDeviation.0 = INTEGER: two(2) */ - { "unmapped.atsConfigFrequencyDeviation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigFrequencyDeviation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.12.0", NULL, SU_FLAG_OK, NULL), /* Outlet groups collection */ /* atsConfigBankLowLoadThreshold.1 = INTEGER: 0 */ - { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.1", NULL, SU_FLAG_OK, NULL), /* atsConfigBankLowLoadThreshold.2 = INTEGER: 0 */ - { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.2", NULL, SU_FLAG_OK, NULL), /* atsConfigBankLowLoadThreshold.3 = INTEGER: 0 */ - { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.3", NULL, SU_FLAG_OK, NULL), /* atsConfigBankNearOverLoadThreshold.1 = INTEGER: 28 */ - { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.1", NULL, SU_FLAG_OK, NULL), /* atsConfigBankNearOverLoadThreshold.2 = INTEGER: 12 */ - { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.2", NULL, SU_FLAG_OK, NULL), /* atsConfigBankNearOverLoadThreshold.3 = INTEGER: 12 */ - { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.3", NULL, SU_FLAG_OK, NULL), /* atsConfigBankOverLoadThreshold.1 = INTEGER: 32 */ - { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.1", NULL, SU_FLAG_OK, NULL), /* atsConfigBankOverLoadThreshold.2 = INTEGER: 16 */ - { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.2", NULL, SU_FLAG_OK, NULL), /* atsConfigBankOverLoadThreshold.3 = INTEGER: 16 */ - { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.3", NULL, SU_FLAG_OK, NULL), /* atsConfigPhaseTableSize.0 = INTEGER: 0 */ - { "unmapped.atsConfigPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.15.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.15.0", NULL, SU_FLAG_OK, NULL), /* atsStatusCommStatus.0 = INTEGER: atsCommEstablished(2) */ - { "unmapped.atsStatusCommStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusCommStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.1.0", NULL, SU_FLAG_OK, NULL), /* atsStatusRedundancyState.0 = INTEGER: atsFullyRedundant(2) */ - { "unmapped.atsStatusRedundancyState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusRedundancyState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.3.0", NULL, SU_FLAG_OK, NULL), /* atsStatusOverCurrentState.0 = INTEGER: atsCurrentOK(2) */ - { "unmapped.atsStatusOverCurrentState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusOverCurrentState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.4.0", NULL, SU_FLAG_OK, NULL), /* atsStatus5VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ - { "unmapped.atsStatus5VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatus5VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.5.0", NULL, SU_FLAG_OK, NULL), /* atsStatus24VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ - { "unmapped.atsStatus24VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatus24VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.6.0", NULL, SU_FLAG_OK, NULL), /* atsStatus24VSourceBPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ - { "unmapped.atsStatus24VSourceBPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatus24VSourceBPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.7.0", NULL, SU_FLAG_OK, NULL), /* atsStatusPlus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ - { "unmapped.atsStatusPlus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusPlus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.8.0", NULL, SU_FLAG_OK, NULL), /* atsStatusMinus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ - { "unmapped.atsStatusMinus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusMinus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.9.0", NULL, SU_FLAG_OK, NULL), /* atsStatusSwitchStatus.0 = INTEGER: ok(2) */ - { "unmapped.atsStatusSwitchStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusSwitchStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.10.0", NULL, SU_FLAG_OK, NULL), /* atsStatusFrontPanel.0 = INTEGER: unlocked(2) */ - { "unmapped.atsStatusFrontPanel", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusFrontPanel", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.11.0", NULL, SU_FLAG_OK, NULL), /* atsStatusSourceAStatus.0 = INTEGER: ok(2) */ - { "unmapped.atsStatusSourceAStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusSourceAStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.12.0", NULL, SU_FLAG_OK, NULL), /* atsStatusSourceBStatus.0 = INTEGER: ok(2) */ - { "unmapped.atsStatusSourceBStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusSourceBStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.13.0", NULL, SU_FLAG_OK, NULL), /* atsStatusPhaseSyncStatus.0 = INTEGER: inSync(1) */ - { "unmapped.atsStatusPhaseSyncStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusPhaseSyncStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.14.0", NULL, SU_FLAG_OK, NULL), /* atsStatusHardwareStatus.0 = INTEGER: ok(2) */ - { "unmapped.atsStatusHardwareStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.16.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusHardwareStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.16.0", NULL, SU_FLAG_OK, NULL), /* atsStatusResetMaxMinValues.0 = INTEGER: -1 */ - { "unmapped.atsStatusResetMaxMinValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsStatusResetMaxMinValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.2.1.0", NULL, SU_FLAG_OK, NULL), /* atsInputTableIndex.1 = INTEGER: 1 */ - { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputTableIndex.2 = INTEGER: 2 */ - { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* atsNumInputPhases.1 = INTEGER: 1 */ - { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* atsNumInputPhases.2 = INTEGER: 1 */ - { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* atsInputVoltageOrientation.1 = INTEGER: singlePhase(2) */ - { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* atsInputVoltageOrientation.2 = INTEGER: singlePhase(2) */ - { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* atsInputType.1 = INTEGER: main(2) */ - { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.1", NULL, SU_FLAG_OK, NULL), /* atsInputType.2 = INTEGER: main(2) */ - { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.2", NULL, SU_FLAG_OK, NULL), /* atsInputName.1 = STRING: "Source A" */ - { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* atsInputName.2 = STRING: "Source B" */ - { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.2", NULL, SU_FLAG_OK, NULL), /* atsInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ - { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputPhaseTableIndex.2.1.1 = INTEGER: 2 */ - { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputPhaseIndex.1.1.1 = INTEGER: 1 */ - { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputPhaseIndex.2.1.1 = INTEGER: 1 */ - { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxVoltage.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxVoltage.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinVoltage.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinVoltage.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputCurrent.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputCurrent.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxCurrent.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxCurrent.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinCurrent.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinCurrent.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputPower.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputPower.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxPower.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMaxPower.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinPower.1.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsInputMinPower.2.1.1 = INTEGER: -1 */ - { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.2.1.1", NULL, SU_FLAG_OK, NULL), /* atsNumOutputs.0 = INTEGER: 1 */ - { "unmapped.atsNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.1.0", NULL, SU_FLAG_OK, NULL), /* atsOutputTableIndex.1 = INTEGER: 1 */ - { "unmapped.atsOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* atsNumOutputPhases.1 = INTEGER: 1 */ - { "unmapped.atsNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* atsOutputVoltageOrientation.1 = INTEGER: singlePhase(2) */ - { "unmapped.atsOutputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* atsOutputPhase.1 = INTEGER: phase1(1) */ - { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.1", NULL, SU_FLAG_OK, NULL), /* atsOutputPhase.2 = INTEGER: phase1(1) */ - { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.2", NULL, SU_FLAG_OK, NULL), /* atsOutputPhase.3 = INTEGER: phase1(1) */ - { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxCurrent.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxCurrent.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxCurrent.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinCurrent.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinCurrent.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinCurrent.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankLoad.1 = INTEGER: 1883 */ - { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankLoad.2 = INTEGER: 984 */ - { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankLoad.3 = INTEGER: 898 */ - { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxLoad.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxLoad.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxLoad.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinLoad.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinLoad.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinLoad.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentLoad.1 = INTEGER: 25 */ - { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentLoad.2 = INTEGER: 13 */ - { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentLoad.3 = INTEGER: 12 */ - { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentLoad.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentLoad.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentLoad.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentLoad.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentLoad.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentLoad.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPower.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPower.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPower.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPower.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPower.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPower.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentPower.1 = INTEGER: 25 */ - { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentPower.2 = INTEGER: 13 */ - { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankPercentPower.3 = INTEGER: 12 */ - { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentPower.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentPower.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMaxPercentPower.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.3", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentPower.1 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.1", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentPower.2 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.2", NULL, SU_FLAG_OK, NULL), /* atsOutputBankMinPercentPower.3 = INTEGER: -1 */ - { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.3", NULL, SU_FLAG_OK, NULL }, -#endif /* 0 */ + snmp_info_default("unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.3", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t apc_ats = { "apc_ats", APC_ATS_MIB_VERSION, NULL, APC_ATS_OID_MODEL_NAME, apc_ats_mib, APC_ATS_SYSOID, NULL }; diff --git a/drivers/apc-epdu-mib.c b/drivers/apc-epdu-mib.c new file mode 100644 index 0000000000..455125c79e --- /dev/null +++ b/drivers/apc-epdu-mib.c @@ -0,0 +1,228 @@ +/* apc-epdu-mib.c - subdriver to monitor apc SNMP easy pdu with NUT + * + * Copyright (C) + * 2011 - 2022 Eric Clappier + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "apc-epdu-mib.h" + +#define APC_EPDU_MIB_VERSION "0.10" + +#define APC_EPDU_MIB_SYSOID ".1.3.6.1.4.1.318.1.3.4.9" + +static info_lkp_t apc_epdu_sw_outlet_status_info[] = { + info_lkp_default(1, "off"), + info_lkp_default(2, "on"), + info_lkp_sentinel +}; + +static info_lkp_t apc_epdu_sw_outlet_switchability_info[] = { + info_lkp_default(1, "yes"), + info_lkp_default(2, "yes"), + info_lkp_sentinel +}; + +/* POWERNET-MIB Snmp2NUT lookup table */ +static snmp_info_t apc_epdu_mib[] = { + + /* Device page */ + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + /* ePDUDeviceStatusModelNumber.1 = STRING: "EPDU1016M" */ + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.2.1.1.4.1", "Easy ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_STALE | SU_FLAG_OK, NULL), + snmp_info_default("device.description", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.5.0", NULL, SU_FLAG_STALE | SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_STALE | SU_FLAG_OK, NULL), + /* FIXME: to be RFC'ed */ + snmp_info_default("device.uptime", 0, 1, ".1.3.6.1.2.1.1.3.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL), + /* ePDUDeviceStatusSerialNumber.1 = STRING: "506255604729" */ + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, " .1.3.6.1.4.1.318.1.1.30.2.1.1.5.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + /* ePDUDeviceStatusModelNumber.1 = STRING: "EPDU1016M" */ + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.2.1.1.4.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + /* ePDUDeviceStatusVersion.1 = STRING: "Ver16.10" */ + snmp_info_default("device.version", ST_FLAG_STRING, SU_INFOSIZE, " .1.3.6.1.4.1.318.1.1.30.2.1.1.3.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + + /* Input */ + /* ePDUPhaseTableSize = INTEGER: 1 */ + snmp_info_default("input.phases", 0, 1, ".1.3.6.1.4.1.318.1.1.30.3.0", NULL, SU_FLAG_OK, NULL), + /* ePDUDeviceStatusActivePower.1 = INTEGER: 785 */ + snmp_info_default("input.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.7.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL), + /* Take first phase for global if single phase */ + /* ePDUPhaseStatusVoltage.1 = INTEGER: 2304 */ + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.4.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_1, NULL), + /* Take first phase for global if single phase */ + /* ePDUPhaseStatusCurrent.1 = INTEGER: 355 */ + snmp_info_default("input.current", 0, 0.01, ".1.3.6.1.4.1.318.1.1.30.4.2.1.5.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_1, NULL), + + /* Only if tree-phase */ + /* ePDUPhaseStatusVoltage.1 = INTEGER: 2304 */ + snmp_info_default("input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.4.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusVoltage.2 = INTEGER: 2304 */ + snmp_info_default("input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.4.2", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusVoltage.3 = INTEGER: 2304 */ + snmp_info_default("input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.4.3", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusCurrent.1 = INTEGER: 355 */ + snmp_info_default("input.L1.current", 0, 0.01, ".1.3.6.1.4.1.318.1.1.30.4.2.1.5.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusCurrent.2 = INTEGER: 355 */ + snmp_info_default("input.L2.current", 0, 0.01, ".1.3.6.1.4.1.318.1.1.30.4.2.1.5.2", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusCurrent.3 = INTEGER: 355 */ + snmp_info_default("input.L3.current", 0, 0.01, ".1.3.6.1.4.1.318.1.1.30.4.2.1.5.3", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusActivePower.1 = INTEGER: 785 */ + snmp_info_default("input.L1.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.6.1", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusActivePower.2 = INTEGER: 785 */ + snmp_info_default("input.L2.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.6.2", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + /* ePDUPhaseStatusActivePower.3 = INTEGER: 785 */ + snmp_info_default("input.L3.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.6.3", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_INPUT_3, NULL), + + /* Outlets */ + /* ePDUOutletTableSize.0 = INTEGER: 1 */ + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.318.1.1.30.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + /* ePDUOutletStatusIndex.%i = INTEGER: 1 */ + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.1.1.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_OUTLET, NULL), + /* ePDUOutletStatusModule.%i= INTEGER: 1 */ + snmp_info_default("outlet.%i.name", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.2.1.2.%i", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_OUTLET, NULL), + /* ePDUOutletStatusNumber.%i = INTEGER: 1 */ + snmp_info_default("outlet.%i.desc", 0, 1, NULL, "Outlet %i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET, NULL), + /* ePDUOutletStatusState.%i = INTEGER: off(1) */ + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.6.1.1.4.%i", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_OUTLET, &apc_epdu_sw_outlet_status_info[0]), + /* Also use this OID to determine switchability ; its presence means "yes" */ + /* ePDUOutletStatusState.%i = INTEGER: off(1) */ + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.6.1.1.4.%i", "yes", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, &apc_epdu_sw_outlet_switchability_info[0]), + +#if WITH_UNMAPPED_DATA_POINTS /* keep following scan for future development */ + /* iso.3.6.1.4.1.318.1.1.30.1.0 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.1.0", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.3.1 = STRING: "Ver16.10" */ + snmp_info_default("unmapped.iso", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.2.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.4.1 = STRING: "EPDU1016M" */ + snmp_info_default("unmapped.iso", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.2.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.5.1 = STRING: "506255604717" */ + snmp_info_default("unmapped.iso", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.30.2.1.1.5.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.6.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.7.1 = INTEGER: 785 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.7.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.8.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.8.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.9.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.9.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.10.1 = INTEGER: 965 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.10.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.11.1 = INTEGER: 9114157 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.11.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.1.1.12.1 = INTEGER: 49988 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.1.1.12.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.2.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.2.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.2.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.2.1.3.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.2.1.4.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.2.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.2.2.1.5.1 = INTEGER: 2 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.2.2.1.5.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.3.0 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.3.0", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.3.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.4.1 = INTEGER: 3000 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.5.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.5.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.6.1 = INTEGER: 3200 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.1.1.7.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.1.1.7.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.3.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.4.1 = INTEGER: 2304 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.5.1 = INTEGER: 353 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.5.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.6.1 = INTEGER: 785 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.6.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.7.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.7.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.8.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.8.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.9.1 = INTEGER: 965 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.9.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.4.2.1.10.1 = INTEGER: 9114157 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.4.2.1.10.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.5.0 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.5.0", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.1.1.1.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.1.1.2.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.1.1.3.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.1.1.4.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.2.1.1.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.2.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.2.1.2.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.2.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.2.1.3.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.2.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.6.2.1.4.1 = INTEGER: -1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.6.2.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.7.0 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.7.0", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.3.1 = INTEGER: 900 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.4.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.5.1 = INTEGER: 900 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.5.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.1.1.6.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.2.1.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.2.1.1.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.2.1.2.1 = INTEGER: 1 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.2.1.2.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.2.1.3.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.2.1.3.1", NULL, SU_FLAG_OK, NULL), + /* iso.3.6.1.4.1.318.1.1.30.8.2.1.4.1 = INTEGER: 0 */ + snmp_info_default("unmapped.iso", 0, 1, ".1.3.6.1.4.1.318.1.1.30.8.2.1.4.1", NULL, SU_FLAG_OK, NULL), +#endif + + /* end of structure. */ + snmp_info_sentinel +}; + +mib2nut_info_t apc_pdu_epdu = { "apc", APC_EPDU_MIB_VERSION, NULL, NULL, apc_epdu_mib, APC_EPDU_MIB_SYSOID, NULL }; diff --git a/drivers/apc-epdu-mib.h b/drivers/apc-epdu-mib.h new file mode 100644 index 0000000000..168858ac2c --- /dev/null +++ b/drivers/apc-epdu-mib.h @@ -0,0 +1,29 @@ +/* apc-epdu-mib.h - subdriver to monitor apc SNMP easy pdu with NUT + * + * Copyright (C) + * 2011 - 2022 Eric Clappier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef APC_EPDU_MIB_H +#define APC_EPDU_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t apc_pdu_epdu; + +#endif /* APC_EPDU_MIB_H */ diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index 4482216db7..c7b11825e0 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.100" /* APC */ #define APC_VENDORID 0x051d @@ -93,6 +94,10 @@ static usb_device_id_t apc_usb_device_table[] = { { USB_DEVICE(APC_VENDORID, 0x0002), general_apc_check }, /* various 5G models */ { USB_DEVICE(APC_VENDORID, 0x0003), disable_interrupt_pipe }, + /* APC Smart UPS 1000 with latest firmware 04.3 + * seems to have bumped the productid from 3 to 4 + * See https://github.com/networkupstools/nut/issues/1429 */ + { USB_DEVICE(APC_VENDORID, 0x0004), disable_interrupt_pipe }, /* Terminating entry */ { 0, 0, NULL } @@ -127,8 +132,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 */ @@ -222,7 +243,7 @@ static usage_lkp_t apc_usage_lkp[] = { { "APCPanelTest", 0xff860072 }, /* FIXME: exploit */ { "APCShutdownAfterDelay", 0xff860076 }, /* FIXME: exploit */ { "APC_USB_FirmwareRevision", 0xff860079 }, /* FIXME: exploit */ - { "APCDelayBeforeReboot", 0xff86007c }, + { "APCDelayBeforeReboot", 0xff86007c }, /* WARNING: apcupsd maps this as APCForceShutdown... which one is right? */ { "APCDelayBeforeShutdown", 0xff86007d }, { "APCDelayBeforeStartup", 0xff86007e }, /* FIXME: exploit */ /* usage seen in dumps but unknown: @@ -317,7 +338,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 +520,82 @@ 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; + } + + if (disable_fix_report_desc) { + upsdebugx(3, + "NOT Attempting Report Descriptor fix for UPS: " + "Vendor: %04x, Product: %04x " + "(got disable_fix_report_desc in config)", + vendorID, productID); + 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 +604,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..1c6c575ac6 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.60" #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? */ @@ -64,251 +64,97 @@ #define APCC_OID_BATT_STATUS ".1.3.6.1.4.1.318.1.1.1.2.1.1.0" /* Defines for APCC_OID_BATT_STATUS */ static info_lkp_t apcc_batt_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryNormal */ - { 3, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryLow */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, ""), /* batteryNormal */ + info_lkp_default(3, "LB"), /* batteryLow */ + info_lkp_default(4, "LB"), /* batteryInFaultCondition */ + info_lkp_default(5, "LB"), /* noBatteryPresent */ + info_lkp_sentinel +}; #define APCC_OID_POWER_STATUS ".1.3.6.1.4.1.318.1.1.1.4.1.1.0" /* Defines for APCC_OID_POWER_STATUS */ static info_lkp_t apcc_pwr_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onLine */ - { 3, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onBattery */ - { 4, "OL BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onSmartBoost */ - { 5, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* timedSleeping */ - { 6, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* softwareBypass */ - { 7, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* off */ - { 8, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* rebooting */ - { 9, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* switchedBypass */ - { 10, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* hardwareFailureBypass */ - { 11, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* sleepingUntilPowerReturn */ - { 12, "OL TRIM" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onSmartTrim */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, "OL"), /* onLine */ + info_lkp_default(3, "OB"), /* onBattery */ + info_lkp_default(4, "OL BOOST"), /* onSmartBoost */ + info_lkp_default(5, "OFF"), /* timedSleeping */ + info_lkp_default(6, "OFF"), /* softwareBypass */ + info_lkp_default(7, "OFF"), /* off */ + info_lkp_default(8, ""), /* rebooting */ + info_lkp_default(9, "BYPASS"), /* switchedBypass */ + info_lkp_default(10, "BYPASS"), /* hardwareFailureBypass */ + info_lkp_default(11, "OFF"), /* sleepingUntilPowerReturn */ + info_lkp_default(12, "OL TRIM"), /* onSmartTrim */ + info_lkp_default(13, "OL ECO"), /* ecoMode */ + info_lkp_default(14, "OL"), /* hotStandby */ + info_lkp_default(15, "OL"), /* onBatteryTest */ + info_lkp_default(16, "BYPASS"), /* emergencyStaticBypass */ + info_lkp_default(17, "BYPASS"), /* staticBypassStandby */ + info_lkp_default(18, ""), /* powerSavingMode */ + info_lkp_default(19, "OL"), /* spotMode */ + info_lkp_default(20, "OL ECO"), /* eConversion */ + info_lkp_default(21, "OL"), /* chargerSpotmode */ + info_lkp_default(22, "OL"), /* inverterSpotmode */ + info_lkp_default(23, ""), /* activeLoad */ + info_lkp_default(24, "OL"), /* batteryDischargeSpotmode */ + info_lkp_default(25, "OL"), /* inverterStandby */ + info_lkp_default(26, ""), /* chargerOnly */ + info_lkp_default(27, ""), /* distributedEnergyReserve */ + info_lkp_default(28, "OL"), /* selfTest */ + info_lkp_sentinel } ; #define APCC_OID_CAL_RESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.6.0" static info_lkp_t apcc_cal_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration Successful */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration not done, battery capacity below 100% */ - { 3, "CAL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration in progress */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* Calibration Successful */ + info_lkp_default(2, ""), /* Calibration not done, battery capacity below 100% */ + info_lkp_default(3, "CAL"), /* Calibration in progress */ + info_lkp_default(4, ""), /* Calibration not done, refused */ + info_lkp_default(5, ""), /* Calibration canceled by user or error */ + info_lkp_default(6, ""), /* Calibration pending, about to start */ + info_lkp_sentinel }; #define APCC_OID_NEEDREPLBATT ".1.3.6.1.4.1.318.1.1.1.2.2.4.0" static info_lkp_t apcc_battrepl_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No battery needs replacing */ - { 2, "RB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Batteries need to be replaced */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No battery needs replacing */ + info_lkp_default(2, "RB"), /* Batteries need to be replaced */ + info_lkp_sentinel }; #define APCC_OID_TESTDIAGRESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.3.0" static info_lkp_t apcc_testdiag_results[] = { - { 1, "Ok" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "Failed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "InvalidTest" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "TestInProgress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "Ok"), + info_lkp_default(2, "Failed"), + info_lkp_default(3, "InvalidTest"), + info_lkp_default(4, "TestInProgress"), + info_lkp_sentinel }; #define APCC_OID_SENSITIVITY ".1.3.6.1.4.1.318.1.1.1.5.2.7.0" static info_lkp_t apcc_sensitivity_modes[] = { - { 1, "auto" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "medium" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "auto"), + info_lkp_default(2, "low"), + info_lkp_default(3, "medium"), + info_lkp_default(4, "high"), + info_lkp_sentinel }; #define APCC_OID_TRANSFERREASON "1.3.6.1.4.1.318.1.1.1.3.2.5.0" static info_lkp_t apcc_transfer_reasons[] = { - { 1, "noTransfer" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "highLineVoltage" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "brownout" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "blackout" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "smallMomentarySag" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "deepMomentarySag" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "smallMomentarySpike" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "largeMomentarySpike" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "selfTest" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "rateOfVoltageChange" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "noTransfer"), + info_lkp_default(2, "highLineVoltage"), + info_lkp_default(3, "brownout"), + info_lkp_default(4, "blackout"), + info_lkp_default(5, "smallMomentarySag"), + info_lkp_default(6, "deepMomentarySag"), + info_lkp_default(7, "smallMomentarySpike"), + info_lkp_default(8, "largeMomentarySpike"), + info_lkp_default(9, "selfTest"), + info_lkp_default(10, "rateOfVoltageChange"), + info_lkp_sentinel }; /* --- */ @@ -327,149 +173,156 @@ static info_lkp_t apcc_transfer_reasons[] = { static snmp_info_t apcc_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* info elements. */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "Generic Powernet SNMP device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, - { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.1.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, - { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.2.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, - { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.3.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, - { "input.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", "", SU_FLAG_OK, NULL }, - { "input.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.2.0", "", SU_FLAG_OK, NULL }, - { "input.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.3.0", "", SU_FLAG_OK, NULL }, - { "input.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "input.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L1-L2.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2-L3.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3-L1.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L1-L2.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2-L3.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3-L1.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "input.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.4.0", "", SU_FLAG_OK, NULL }, - { "input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons }, - { "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, APCC_OID_SENSITIVITY, "", SU_TYPE_INT | SU_FLAG_OK, apcc_sensitivity_modes }, - { "ups.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.9.0", "", SU_FLAG_OK, NULL }, - { "ups.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.8.0", "", SU_FLAG_OK, NULL }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_POWER_STATUS, "OFF", - SU_FLAG_OK | SU_STATUS_PWR, apcc_pwr_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_BATT_STATUS, "", - SU_FLAG_OK | SU_STATUS_BATT, apcc_batt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_CAL_RESULTS, "", - SU_FLAG_OK | SU_STATUS_CAL, apcc_cal_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_NEEDREPLBATT, "", - SU_FLAG_OK | SU_STATUS_RB, apcc_battrepl_info }, - { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.2.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, - { "ups.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", "", SU_FLAG_OK, NULL }, - { "ups.load", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.3.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "ups.load", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.3.0", "", SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, 16, ".1.3.6.1.4.1.318.1.1.1.1.2.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.delay.shutdown", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL }, - { "ups.delay.start", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL }, - { "battery.charge", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.1.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "battery.charge", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", "", SU_FLAG_OK, NULL }, - { "battery.charge.restart", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.6.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "battery.runtime", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", "", SU_FLAG_OK, NULL }, - { "battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.8.0", "", SU_FLAG_OK, NULL }, - { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "battery.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.8.0", "", SU_FLAG_OK, NULL }, - { "battery.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.7.0", "", SU_FLAG_OK, NULL }, - { "battery.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.5.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, - { "battery.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.9.0", "", SU_FLAG_OK, NULL }, - { "battery.current.total", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.6.0", "", SU_FLAG_OK, NULL }, - { "battery.packs", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.5.0", "", SU_FLAG_OK, NULL }, - { "battery.packs.bad", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.6.0", "", SU_FLAG_OK, NULL }, - { "battery.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, - { "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.1.1.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_TESTDIAGRESULTS, "", SU_FLAG_OK, apcc_testdiag_results }, - { "ups.test.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.7.2.4.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.1.0", "", SU_FLAG_OK | SU_FLAG_UNIQUE, NULL }, - { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.1.0", "", SU_FLAG_OK, NULL }, - { "output.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.2.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.2.0", "", SU_FLAG_OK, NULL }, - { "output.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, - { "output.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", "", SU_FLAG_OK, NULL }, - { "output.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L1.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L2.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.L3.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, - { "output.voltage.nominal", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "Generic Powernet SNMP device", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL), + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.1.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL), + snmp_info_default("input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.2.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL), + snmp_info_default("input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.3.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL), + snmp_info_default("input.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("input.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("input.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.3.0", "", SU_FLAG_OK, NULL), + snmp_info_default("input.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("input.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1-L2.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2-L3.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3-L1.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1-L2.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2-L3.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3-L1.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("input.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.4.0", "", SU_FLAG_OK, NULL), + snmp_info_default("input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), + snmp_info_default("input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), + snmp_info_default("input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons), + snmp_info_default("input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, APCC_OID_SENSITIVITY, "", SU_TYPE_INT | SU_FLAG_OK, apcc_sensitivity_modes), + snmp_info_default("ups.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.9.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.8.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_POWER_STATUS, "OFF", + SU_FLAG_OK | SU_STATUS_PWR, apcc_pwr_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_BATT_STATUS, "", + SU_FLAG_OK | SU_STATUS_BATT, apcc_batt_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_CAL_RESULTS, "", + SU_FLAG_OK | SU_STATUS_CAL, apcc_cal_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_NEEDREPLBATT, "", + SU_FLAG_OK | SU_STATUS_RB, apcc_battrepl_info), + snmp_info_default("ups.temperature", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.2.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL), + snmp_info_default("ups.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.load", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.3.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("ups.load", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.3.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, 16, ".1.3.6.1.4.1.318.1.1.1.1.2.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.delay.shutdown", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL), + snmp_info_default("ups.delay.start", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL), + snmp_info_default("battery.charge", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.1.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("battery.charge", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.charge.restart", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.6.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.8.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("battery.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.8.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.7.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.5.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL), + snmp_info_default("battery.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.9.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.current.total", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.6.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.packs", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.5.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.packs.bad", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.6.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.1.1.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL), + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_TESTDIAGRESULTS, "", SU_FLAG_OK, apcc_testdiag_results), + snmp_info_default("ups.test.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.7.2.4.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL), + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.1.0", "", SU_FLAG_OK | SU_FLAG_UNIQUE, NULL), + snmp_info_default("output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("output.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.2.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL), + snmp_info_default("output.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", "", SU_FLAG_OK, NULL), + snmp_info_default("output.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3-L1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.4.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.current.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.5.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.6.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.7.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.9.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.10.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power.maximum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.11.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L1.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L2.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.L3.power.minimum.percent", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.12.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL), + snmp_info_default("output.voltage.nominal", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), /* Measure-UPS ambient variables */ -/* Environmental sensors (AP9612TH and others) */ - { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.1.0", "", SU_FLAG_OK, NULL }, - { "ambient.1.temperature.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", "", SU_FLAG_OK, NULL }, - { "ambient.1.temperature.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.4.1", "", SU_FLAG_OK, NULL }, - { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL }, - { "ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, - { "ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, -/* IEM: integrated environment monitor probe */ - { "ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL }, - { "ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL }, + /* Environmental sensors (AP9612TH and others) */ + snmp_info_default("ambient.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.1.temperature.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.1.temperature.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.4.1", "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL), + + /* IEM ambient variables */ + /* IEM: integrated environment monitor probe */ + snmp_info_default("ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL), /* instant commands. */ - { "load.off", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.on", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.6.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "shutdown.stayoff", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("load.off", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("load.on", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.6.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("shutdown.stayoff", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), -/* { CMD_SDRET, 0, APCC_REBOOT_GRACEFUL, APCC_OID_REBOOT, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ +/* snmp_info_default(CMD_SDRET, 0, APCC_REBOOT_GRACEFUL, APCC_OID_REBOOT, "", SU_TYPE_CMD | SU_FLAG_OK, NULL), */ - { "shutdown.return", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.failure.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.4.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.panel.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "bypass.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "bypass.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.battery.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.2.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "calibrate.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "calibrate.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "reset.input.minmax", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("shutdown.return", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("test.failure.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.4.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("test.panel.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("bypass.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("bypass.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("test.battery.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.2.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("calibrate.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("calibrate.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("reset.input.minmax", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t apc = { "apcc", APCC_MIB_VERSION, APCC_OID_POWER_STATUS, APC_UPS_DEVICE_MODEL, apcc_mib, APC_UPS_SYSOID, NULL }; diff --git a/drivers/apc-pdu-mib.c b/drivers/apc-pdu-mib.c index bcd4ac802e..4252ffa640 100644 --- a/drivers/apc-pdu-mib.c +++ b/drivers/apc-pdu-mib.c @@ -23,7 +23,7 @@ #include "apc-pdu-mib.h" -#define APC_PDU_MIB_VERSION "0.4" +#define APC_PDU_MIB_VERSION "0.40" #define APC_PDU_MIB_SYSOID_RPDU ".1.3.6.1.4.1.318.1.3.4.4" #define APC_PDU_MIB_SYSOID_RPDU2 ".1.3.6.1.4.1.318.1.3.4.5" @@ -32,963 +32,936 @@ static info_lkp_t apc_pdu_sw_outlet_status_info[] = { - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "on"), + info_lkp_default(2, "off"), + info_lkp_sentinel }; static info_lkp_t apc_pdu_sw_outlet_switchability_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "yes"), + info_lkp_sentinel }; /* POWERNET-MIB Snmp2NUT lookup table */ static snmp_info_t apc_pdu_mib[] = { /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* sPDUIdentModelNumber.0 = STRING: "AP7900" */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", - "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, - SU_FLAG_STALE | SU_FLAG_OK, NULL }, - { "device.description", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.5.0", NULL, - SU_FLAG_STALE | SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, - SU_FLAG_STALE | SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL), + snmp_info_default("device.description", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.5.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL), /* FIXME: to be RFC'ed */ - { "device.uptime", 0, 1, ".1.3.6.1.2.1.1.3.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL }, + snmp_info_default("device.uptime", 0, 1, ".1.3.6.1.2.1.1.3.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL), /* sPDUIdentSerialNumber.0 = STRING: "5A1234E00874" */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* sPDUIdentModelNumber.0 = STRING: "AP7900" */ - { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* sPDUIdentHardwareRev.0 = STRING: "B2" */ - { "device.version", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.version", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* sPDUIdentFirmwareRev.0 = STRING: "v3.7.3" */ /* FIXME: to be moved to device.firmware */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.2.0", NULL, SU_FLAG_OK, NULL), /* sPDUIdentDateOfManufacture.0 = STRING: "08/13/2012" */ /* FIXME: to be moved to the device collection! */ - { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.3.0", NULL, SU_FLAG_OK, NULL), /* Input */ /* rPDUIdentDevicePowerWatts.0 = INTEGER: 0 */ - { "input.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.16.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.16.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadStatusLoad.1 = Gauge32: 0 */ - { "input.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceLinetoLineVoltage.0 = INTEGER: 120 */ - { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL }, + snmp_info_default("input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL), /* Outlets */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.%i", "%i", - SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL }, + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.%i", "%i", + SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL), /* sPDUOutletCtlName.%i = STRING: "Testing Name" */ - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.4.%i", NULL, - SU_FLAG_STALE | SU_FLAG_OK | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.4.%i", NULL, + SU_FLAG_STALE | SU_FLAG_OK | SU_OUTLET, NULL), /* sPDUOutletCtl.1 = INTEGER: outletOn(1) */ - { "outlet.%i.status", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", NULL, - SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_status_info[0] }, + snmp_info_default("outlet.%i.status", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", NULL, + SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_status_info[0]), /* Also use this OID to determine switchability ; its presence means "yes" */ /* sPDUOutletCtl.1 = INTEGER: outletOn(1) */ - { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", "yes", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_switchability_info[0] }, - - - -#if 0 /* keep following scan for future development */ + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", "yes", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_switchability_info[0]), +#if WITH_UNMAPPED_DATA_POINTS /* keep following scan for future development */ /* sPDUMasterControlSwitch.0 = INTEGER: noCommand(6) */ - { "unmapped.sPDUMasterControlSwitch", 0, 1, ".1.3.6.1.4.1.318.1.1.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterControlSwitch", 0, 1, ".1.3.6.1.4.1.318.1.1.4.2.1.0", NULL, SU_FLAG_OK, NULL), /* sPDUMasterState.0 = STRING: "On On On On On On On On " */ - { "unmapped.sPDUMasterState", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterState", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.2.0", NULL, SU_FLAG_OK, NULL), /* sPDUMasterPending.0 = STRING: "No No No No No No No No " */ - { "unmapped.sPDUMasterPending", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterPending", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.3.0", NULL, SU_FLAG_OK, NULL), /* sPDUMasterConfigPowerOn.0 = INTEGER: 0 */ - { "unmapped.sPDUMasterConfigPowerOn", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterConfigPowerOn", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.1.0", NULL, SU_FLAG_OK, NULL), /* sPDUMasterConfigReboot.0 = INTEGER: 0 */ - { "unmapped.sPDUMasterConfigReboot", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterConfigReboot", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.2.0", NULL, SU_FLAG_OK, NULL), /* sPDUMasterConfigPDUName.0 = STRING: "RackPDU" */ - { "unmapped.sPDUMasterConfigPDUName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.3.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUMasterConfigPDUName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.3.3.0", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlTableSize.0 = INTEGER: 8 */ /* sPDUOutletControlIndex.1 = INTEGER: 1 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.2 = INTEGER: 2 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.3 = INTEGER: 3 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.4 = INTEGER: 4 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.5 = INTEGER: 5 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.6 = INTEGER: 6 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.7 = INTEGER: 7 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletControlIndex.8 = INTEGER: 8 */ - { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.1 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.2 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.3 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.4 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.5 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.6 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.7 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPending.8 = INTEGER: noCommandPending(2) */ - { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigTableSize.0 = INTEGER: 8 */ - { "unmapped.sPDUOutletConfigTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.1.0", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.1 = INTEGER: 1 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.2 = INTEGER: 2 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.3 = INTEGER: 3 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.4 = INTEGER: 4 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.5 = INTEGER: 5 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.6 = INTEGER: 6 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.7 = INTEGER: 7 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletConfigIndex.8 = INTEGER: 8 */ - { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.1 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.2 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.3 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.4 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.5 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.6 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.7 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOnTime.8 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.1 = STRING: "Testing Name" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.2 = STRING: "Testing 2" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.3 = STRING: "Outlet 3" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.4 = STRING: "Outlet 4" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.5 = STRING: "Outlet 5" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.6 = STRING: "Outlet 6" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.7 = STRING: "Outlet 7" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletName.8 = STRING: "Outlet 8" */ - { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.1 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.2 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.3 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.4 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.5 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.6 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.7 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletPowerOffTime.8 = INTEGER: 0 */ - { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.8", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.1 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.1", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.2 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.2", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.3 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.3", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.4 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.4", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.5 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.5", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.6 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.6", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.7 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.7", NULL, SU_FLAG_OK, NULL), /* sPDUOutletRebootDuration.8 = INTEGER: 5 */ - { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.8", NULL, SU_FLAG_OK, NULL), /* rPDUIdentName.0 = STRING: "RackPDU" */ - { "unmapped.rPDUIdentName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.1.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentHardwareRev.0 = STRING: "B2" */ - { "unmapped.rPDUIdentHardwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentHardwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.2.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentFirmwareRev.0 = STRING: "v3.7.3" */ - { "unmapped.rPDUIdentFirmwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentFirmwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.3.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDateOfManufacture.0 = STRING: "08/13/2012" */ - { "unmapped.rPDUIdentDateOfManufacture", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDateOfManufacture", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.4.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentModelNumber.0 = STRING: "AP7900" */ - { "unmapped.rPDUIdentModelNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentModelNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.5.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentSerialNumber.0 = STRING: "5A1234E00874" */ - { "unmapped.rPDUIdentSerialNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentSerialNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.6.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceRating.0 = INTEGER: 12 */ - { "unmapped.rPDUIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.7.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceNumOutlets.0 = INTEGER: 8 */ - { "unmapped.rPDUIdentDeviceNumOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceNumOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.8.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceNumPhases.0 = INTEGER: 1 */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.9.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("input.phases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.9.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* rPDUIdentDeviceNumBreakers.0 = INTEGER: 0 */ - { "unmapped.rPDUIdentDeviceNumBreakers", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceNumBreakers", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.10.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceBreakerRating.0 = INTEGER: 0 */ - { "unmapped.rPDUIdentDeviceBreakerRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceBreakerRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.11.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceOrientation.0 = INTEGER: orientHorizontal(1) */ - { "unmapped.rPDUIdentDeviceOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.12.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceOutletLayout.0 = INTEGER: seqPhaseToNeutral(1) */ - { "unmapped.rPDUIdentDeviceOutletLayout", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceOutletLayout", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.13.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceDisplayOrientation.0 = INTEGER: displayNormal(1) */ - { "unmapped.rPDUIdentDeviceDisplayOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceDisplayOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.14.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDeviceLinetoLineVoltage.0 = INTEGER: 120 */ - { "unmapped.rPDUIdentDeviceLinetoLineVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDeviceLinetoLineVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDevicePowerFactor.0 = INTEGER: 1000 */ - { "unmapped.rPDUIdentDevicePowerFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.17.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDevicePowerFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.17.0", NULL, SU_FLAG_OK, NULL), /* rPDUIdentDevicePowerVA.0 = INTEGER: 0 */ - { "unmapped.rPDUIdentDevicePowerVA", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.18.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUIdentDevicePowerVA", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.18.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevMaxPhaseLoad.0 = INTEGER: 12 */ - { "unmapped.rPDULoadDevMaxPhaseLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevMaxPhaseLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.1.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevNumPhases.0 = INTEGER: 1 */ - { "unmapped.rPDULoadDevNumPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevNumPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.2.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevMaxBankLoad.0 = INTEGER: 0 */ - { "unmapped.rPDULoadDevMaxBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevMaxBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.3.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevNumBanks.0 = INTEGER: 0 */ - { "unmapped.rPDULoadDevNumBanks", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevNumBanks", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.4.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevBankTableSize.0 = INTEGER: 0 */ - { "unmapped.rPDULoadDevBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.5.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadDevMaxOutletTableSize.0 = INTEGER: 0 */ - { "unmapped.rPDULoadDevMaxOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadDevMaxOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.7.0", NULL, SU_FLAG_OK, NULL), /* rPDULoadPhaseConfigIndex.phase1 = INTEGER: phase1(1) */ - { "unmapped.rPDULoadPhaseConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadPhaseConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadPhaseConfigLowLoadThreshold.phase1 = INTEGER: 0 */ - { "unmapped.rPDULoadPhaseConfigLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadPhaseConfigLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadPhaseConfigNearOverloadThreshold.phase1 = INTEGER: 8 */ - { "unmapped.rPDULoadPhaseConfigNearOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadPhaseConfigNearOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadPhaseConfigOverloadThreshold.phase1 = INTEGER: 12 */ - { "unmapped.rPDULoadPhaseConfigOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadPhaseConfigOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.4.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadPhaseConfigAlarm.phase1 = INTEGER: noLoadAlarm(1) */ - { "unmapped.rPDULoadPhaseConfigAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadPhaseConfigAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadStatusIndex.1 = INTEGER: 1 */ - { "unmapped.rPDULoadStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadStatusLoadState.1 = INTEGER: phaseLoadNormal(1) */ - { "unmapped.rPDULoadStatusLoadState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadStatusLoadState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadStatusPhaseNumber.1 = INTEGER: 1 */ - { "unmapped.rPDULoadStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.4.1", NULL, SU_FLAG_OK, NULL), /* rPDULoadStatusBankNumber.1 = INTEGER: 0 */ - { "unmapped.rPDULoadStatusBankNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDULoadStatusBankNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletDevCommand.0 = INTEGER: noCommandAll(1) */ - { "unmapped.rPDUOutletDevCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletDevCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.1.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletDevColdstartDelay.0 = INTEGER: 0 */ - { "unmapped.rPDUOutletDevColdstartDelay", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletDevColdstartDelay", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.2.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletDevNumCntrlOutlets.0 = INTEGER: 8 */ - { "unmapped.rPDUOutletDevNumCntrlOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletDevNumCntrlOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.3.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletDevNumTotalOutlets.0 = INTEGER: 8 */ - { "unmapped.rPDUOutletDevNumTotalOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletDevNumTotalOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.4.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletDevMonitoredOutlets.0 = INTEGER: 0 */ - { "unmapped.rPDUOutletDevMonitoredOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletDevMonitoredOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.5.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletPhaseIndex.phase1 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletPhaseOverloadRestriction.phase1 = INTEGER: alwaysAllowTurnON(1) */ - { "unmapped.rPDUOutletPhaseOverloadRestriction", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletPhaseOverloadRestriction", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.1 = INTEGER: 1 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.2 = INTEGER: 2 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.3 = INTEGER: 3 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.4 = INTEGER: 4 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.5 = INTEGER: 5 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.6 = INTEGER: 6 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.7 = INTEGER: 7 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlIndex.8 = INTEGER: 8 */ - { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.1 = STRING: "Testing Name" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.2 = STRING: "Testing 2" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.3 = STRING: "Outlet 3" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.4 = STRING: "Outlet 4" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.5 = STRING: "Outlet 5" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.6 = STRING: "Outlet 6" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.7 = STRING: "Outlet 7" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletName.8 = STRING: "Outlet 8" */ - { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.1 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.2 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.3 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.4 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.5 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.6 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.7 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletPhase.8 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.1 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.2 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.3 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.4 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.5 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.6 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.7 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletCommand.8 = INTEGER: immediateOn(1) */ - { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.1 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.2 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.3 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.4 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.5 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.6 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.7 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletControlOutletBank.8 = INTEGER: 0 */ - { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.1 = INTEGER: 1 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.2 = INTEGER: 2 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.3 = INTEGER: 3 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.4 = INTEGER: 4 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.5 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.6 = INTEGER: 6 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.7 = INTEGER: 7 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigIndex.8 = INTEGER: 8 */ - { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.1 = STRING: "Testing Name" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.2 = STRING: "Testing 2" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.3 = STRING: "Outlet 3" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.4 = STRING: "Outlet 4" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.5 = STRING: "Outlet 5" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.6 = STRING: "Outlet 6" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.7 = STRING: "Outlet 7" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletName.8 = STRING: "Outlet 8" */ - { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.1 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.2 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.3 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.4 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.5 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.6 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.7 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletPhase.8 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.1 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.2 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.3 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.4 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.5 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.6 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.7 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOnTime.8 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.1 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.2 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.3 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.4 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.5 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.6 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.7 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigPowerOffTime.8 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.1 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.2 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.3 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.4 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.5 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.6 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.7 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigRebootDuration.8 = INTEGER: 5 */ - { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.1 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.2 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.3 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.4 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.5 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.6 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.7 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigOutletBank.8 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletConfigMonitoredTableSize.0 = INTEGER: 0 */ - { "unmapped.rPDUOutletConfigMonitoredTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletConfigMonitoredTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.2.0", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.1 = INTEGER: 1 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.2 = INTEGER: 2 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.3 = INTEGER: 3 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.4 = INTEGER: 4 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.5 = INTEGER: 5 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.6 = INTEGER: 6 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.7 = INTEGER: 7 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusIndex.8 = INTEGER: 8 */ - { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.1 = STRING: "Testing Name" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.2 = STRING: "Testing 2" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.3 = STRING: "Outlet 3" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.4 = STRING: "Outlet 4" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.5 = STRING: "Outlet 5" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.6 = STRING: "Outlet 6" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.7 = STRING: "Outlet 7" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletName.8 = STRING: "Outlet 8" */ - { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.1 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.2 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.3 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.4 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.5 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.6 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.7 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletPhase.8 = INTEGER: phase1(1) */ - { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.1 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.2 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.3 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.4 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.5 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.6 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.7 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletState.8 = INTEGER: outletStatusOn(1) */ - { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.1 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.2 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.3 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.4 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.5 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.6 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.7 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusCommandPending.8 = INTEGER: outletStatusNoCommandPending(2) */ - { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.1 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.2 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.3 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.4 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.5 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.6 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.7 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusOutletBank.8 = INTEGER: 0 */ - { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.8", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.1 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.1", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.2 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.2", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.3 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.3", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.4 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.4", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.5 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.5", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.6 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.6", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.7 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.7", NULL, SU_FLAG_OK, NULL), /* rPDUOutletStatusLoad.8 = Gauge32: 0 */ - { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.8", NULL, SU_FLAG_OK, NULL), /* rPDUPowerSupply1Status.0 = INTEGER: powerSupplyOneOk(1) */ - { "unmapped.rPDUPowerSupply1Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUPowerSupply1Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.1.0", NULL, SU_FLAG_OK, NULL), /* rPDUPowerSupply2Status.0 = INTEGER: powerSupplyTwoOk(1) */ - { "unmapped.rPDUPowerSupply2Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUPowerSupply2Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.2.0", NULL, SU_FLAG_OK, NULL), /* rPDUPowerSupplyAlarm.0 = INTEGER: allAvailablePowerSuppliesOK(1) */ - { "unmapped.rPDUPowerSupplyAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUPowerSupplyAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.3.0", NULL, SU_FLAG_OK, NULL), /* rPDUStatusBankTableSize.0 = INTEGER: 0 */ - { "unmapped.rPDUStatusBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.1.0", NULL, SU_FLAG_OK, NULL), /* rPDUStatusPhaseTableSize.0 = INTEGER: 1 */ - { "unmapped.rPDUStatusPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.3.0", NULL, SU_FLAG_OK, NULL), /* rPDUStatusPhaseIndex.1 = INTEGER: 1 */ - { "unmapped.rPDUStatusPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* rPDUStatusPhaseNumber.1 = INTEGER: 1 */ - { "unmapped.rPDUStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* rPDUStatusPhaseState.1 = INTEGER: phaseLoadNormal(1) */ - { "unmapped.rPDUStatusPhaseState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusPhaseState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* rPDUStatusOutletTableSize.0 = INTEGER: 0 */ - { "unmapped.rPDUStatusOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.rPDUStatusOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.5.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.1.0 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.1.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.1.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.2.1 = STRING: "Rack PDU_ISX" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.3.1 = STRING: "5A1234E00874" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.4.1 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.5.1 = STRING: "Rack PDU" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.5.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.6.1 = STRING: "1" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.7.1 = STRING: "Unknown" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.7.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.8.1 = INTEGER: 255 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.8.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.8.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.9.1 = STRING: "RackPDU" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.9.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.9.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.10.1 = INTEGER: 255 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.10.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.11.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.11.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.11.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.12.1 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.12.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.12.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.13.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.13.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.13.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.2.1.14.1 = STRING: "SB-1" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.14.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.14.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.3.0 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.3.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.1.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.1.2 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.2.1 = STRING: "5A1234E00874" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.2.2 = STRING: "5A1234E00874" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.3.1 = STRING: "apc_hw02_aos_373.bin" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.3.2 = STRING: "apc_hw02_rpdu_373.bin" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.4.1 = STRING: "v3.7.3" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.4.1.4.2 = STRING: "v3.7.3" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.5.0 = INTEGER: 19 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.5.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.2 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.3 = INTEGER: 3 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.3", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.4 = INTEGER: 4 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.4", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.5 = INTEGER: 5 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.5", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.6 = INTEGER: 6 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.6", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.7 = INTEGER: 7 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.7", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.8 = INTEGER: 8 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.8", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.9 = INTEGER: 9 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.9", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.10 = INTEGER: 10 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.10", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.11 = INTEGER: 11 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.11", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.12 = INTEGER: 12 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.12", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.13 = INTEGER: 13 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.13", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.13", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.14 = INTEGER: 14 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.14", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.15 = INTEGER: 15 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.15", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.15", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.16 = INTEGER: 16 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.16", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.16", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.17 = INTEGER: 17 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.17", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.17", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.18 = INTEGER: 18 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.18", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.18", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.1.19 = INTEGER: 19 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.19", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.19", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.2 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.3 = INTEGER: 3 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.3", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.4 = INTEGER: 4 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.4", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.5 = INTEGER: 5 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.5", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.6 = INTEGER: 6 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.6", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.7 = INTEGER: 7 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.7", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.8 = INTEGER: 8 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.8", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.9 = INTEGER: 9 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.9", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.10 = INTEGER: 10 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.10", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.11 = INTEGER: 11 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.11", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.12 = INTEGER: 12 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.12", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.13 = INTEGER: 13 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.13", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.13", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.14 = INTEGER: 14 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.14", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.15 = INTEGER: 15 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.15", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.15", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.16 = INTEGER: 16 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.16", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.16", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.17 = INTEGER: 17 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.17", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.17", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.18 = INTEGER: 18 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.18", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.18", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.2.19 = INTEGER: 19 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.19", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.19", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.1 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.2 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.3 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.3", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.4 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.4", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.5 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.5", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.6 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.6", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.7 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.7", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.8 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.8", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.9 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.9", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.10 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.10", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.11 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.11", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.12 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.12", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.13 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.13", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.13", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.14 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.14", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.15 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.15", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.15", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.16 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.16", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.16", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.17 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.17", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.17", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.18 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.18", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.18", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.3.19 = STRING: "0" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.19", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.19", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.1 = STRING: "MTYx" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.2 = STRING: "MjM=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.3 = STRING: "ODA=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.3", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.4 = STRING: "OTk1MA==" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.4", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.5 = STRING: "OTk1MA==" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.5", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.6 = STRING: "NDQz" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.6", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.7 = STRING: "MjI=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.7", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.8 = STRING: "MjI=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.8", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.9 = STRING: "MTIz" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.9", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.10 = STRING: "MjU=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.10", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.11 = STRING: "MjE=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.11", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.12 = STRING: "MjE=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.12", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.13 = STRING: "Njg=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.13", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.13", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.14 = STRING: "NTQ2" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.14", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.15 = STRING: "MTgxMg==" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.15", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.15", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.16 = STRING: "MTYx" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.16", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.16", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.17 = STRING: "MjE=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.17", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.17", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.18 = STRING: "MjE=" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.18", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.18", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.4.19 = STRING: "OTk1MQ==" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.19", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.19", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.1 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.2 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.2", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.3 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.3", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.4 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.4", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.5 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.5", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.6 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.6", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.7 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.7", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.8 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.8", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.9 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.9", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.10 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.10", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.11 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.11", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.12 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.12", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.13 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.13", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.13", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.14 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.14", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.15 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.15", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.15", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.16 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.16", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.16", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.17 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.17", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.17", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.18 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.18", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.18", NULL, SU_FLAG_OK, NULL), /* experimental.2.6.1.5.19 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.19", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.19", NULL, SU_FLAG_OK, NULL), /* experimental.2.7.0 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.7.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.1.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.1.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.2.1 = STRING: "power" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.2.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.3.1 = STRING: "pdu" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.3.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.4.1 = STRING: "rpdu" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.4.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.5.1 = STRING: "version7" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.5.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.8.1.6.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.6.1", NULL, SU_FLAG_OK, NULL), /* experimental.2.9.0 = INTEGER: 0 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.9.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.10.0 = INTEGER: 0 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.10.0", NULL, SU_FLAG_OK, NULL), /* experimental.2.12.0 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.12.0", NULL, SU_FLAG_OK, NULL), /* experimental.4.1.0 = INTEGER: 0 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.1.0", NULL, SU_FLAG_OK, NULL), /* experimental.4.3.0 = STRING: ",<1 digit type identifier>" */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.4.3.0", NULL, SU_FLAG_OK, NULL), /* experimental.4.4.0 = INTEGER: 0 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.4.0", NULL, SU_FLAG_OK, NULL), /* experimental.5.1.0 = INTEGER: 12 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.1.0", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.1 = INTEGER: 1 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.2 = INTEGER: 2 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.3 = INTEGER: 3 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.4 = INTEGER: 4 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.5 = INTEGER: 5 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.6 = INTEGER: 6 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.7 = INTEGER: 7 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.8 = INTEGER: 8 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.9 = INTEGER: 9 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.9", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.10 = INTEGER: 10 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.10", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.11 = INTEGER: 11 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.11", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.1.12 = INTEGER: 12 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.12", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.1 = INTEGER: 3841 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.2 = INTEGER: 3843 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.3 = INTEGER: 3845 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.4 = INTEGER: 3848 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.5 = INTEGER: 3862 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.6 = INTEGER: 3864 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.7 = INTEGER: 3856 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.8 = INTEGER: 3858 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.9 = INTEGER: 3860 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.9", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.10 = INTEGER: 3871 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.10", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.11 = INTEGER: 3873 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.11", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.11", NULL, SU_FLAG_OK, NULL), /* experimental.5.2.1.2.12 = INTEGER: 3875 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.12", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.12", NULL, SU_FLAG_OK, NULL), /* experimental.6.1.1.0 = Hex-STRING: 07 00 00 00 */ - { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.6.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.6.1.1.0", NULL, SU_FLAG_OK, NULL), /* experimental.7.1.0 = INTEGER: 4 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.1.0", NULL, SU_FLAG_OK, NULL), /* experimental.7.3.0 = INTEGER: 4 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.3.0", NULL, SU_FLAG_OK, NULL), /* experimental.7.4.0 = INTEGER: 3 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.4.0", NULL, SU_FLAG_OK, NULL), /* experimental.7.5.0 = INTEGER: 3 */ - { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.5.0", NULL, SU_FLAG_OK, NULL }, -#endif /* scan result */ + snmp_info_default("unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.5.0", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t apc_pdu_rpdu = { "apc_pdu", APC_PDU_MIB_VERSION, NULL, APC_PDU_DEVICE_MODEL, apc_pdu_mib, APC_PDU_MIB_SYSOID_RPDU, NULL }; diff --git a/drivers/apc_modbus.c b/drivers/apc_modbus.c new file mode 100644 index 0000000000..8aafce1479 --- /dev/null +++ b/drivers/apc_modbus.c @@ -0,0 +1,1862 @@ +/* apc_modbus.c - Driver for APC Modbus UPS + * Copyright Ā© 2023 Axel Gembe + * + * 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" +#if defined NUT_MODBUS_HAS_USB +#include "nut_libusb.h" +#include "libhid.h" +#include "hidparser.h" +#endif /* defined NUT_MODBUS_HAS_USB */ +#include "timehead.h" +#include "nut_stdint.h" +#include "apc_modbus.h" + +#include +#include + +#include + +#define DRIVER_NAME "NUT APC Modbus driver" +#define DRIVER_VERSION "0.10" + +#if defined NUT_MODBUS_HAS_USB + +/* APC */ +#define APC_VENDORID 0x051D + +/* USB IDs device table */ +static usb_device_id_t apc_modbus_usb_device_table[] = { + { USB_DEVICE(APC_VENDORID, 0x0003), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +#endif /* defined NUT_MODBUS_HAS_USB */ + +/* Constants */ +static const uint8_t modbus_default_slave_id = 1; + +static const int modbus_rtu_default_baudrate = 9600; +static const char modbus_rtu_default_parity = 'N'; +static const int modbus_rtu_default_databits = 8; +static const int modbus_rtu_default_stopbits = 1; + +static const uint16_t modbus_tcp_default_port = 502; + +#if defined NUT_MODBUS_HAS_USB +static const HIDNode_t modbus_rtu_usb_usage_rx = 0xff8600fc; +static const HIDNode_t modbus_rtu_usb_usage_tx = 0xff8600fd; +#endif /* defined NUT_MODBUS_HAS_USB */ + +/* Variables */ +static modbus_t *modbus_ctx = NULL; +#if defined NUT_MODBUS_HAS_USB +static USBDevice_t usbdevice; +static USBDeviceMatcher_t *reopen_matcher = NULL; +static USBDeviceMatcher_t *regex_matcher = NULL; +static USBDeviceMatcher_t *best_matcher = NULL; +static int is_usb = 0; +#endif /* defined NUT_MODBUS_HAS_USB */ +static int is_open = 0; +static double power_nominal; +static double realpower_nominal; + +/* Function declarations */ +static int _apc_modbus_read_inventory(void); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Axel Gembe \n", + DRV_BETA, + {NULL} +}; + +typedef enum { + APC_VT_INT = 0, + APC_VT_UINT, + APC_VT_STRING +} apc_modbus_value_types; + +typedef enum { + APC_VF_NONE = 0, + APC_VF_RW = (1 << 0) +} apc_modbus_value_flags; + +static const apc_modbus_value_types apc_modbus_value_types_max = APC_VT_STRING; + +typedef union { + int64_t int_value; + uint64_t uint_value; + char *string_value; +} apc_modbus_value_data_t; + +typedef struct { + apc_modbus_value_types type; + const char *format; + int scale; + void *variable_ptr; + apc_modbus_value_data_t data; +} apc_modbus_value_t; + +typedef struct { + int (*apc_to_nut)(const apc_modbus_value_t *value, char *output, size_t output_len); + int (*nut_to_apc)(const char *value, uint16_t *output, size_t output_len); +} apc_modbus_converter_t; + +/* Converts a Modbus string to a C string. + * See MPAO-98KJ7F_EN section 1.3.3 Strings. + * + * Needs to remove leading zeroes, trailing spaces and translate characters in + * the range 0x20-0x7e as a different character. + * Zero termination is optional, so the output buffer needs to be 1 byte + * longer to add zero termination. */ +static int _apc_modbus_to_string(const uint16_t *value, const size_t value_len, char *output, size_t output_len) +{ + char *op; + size_t i; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + if ((value_len * sizeof(uint16_t)) + 1 > output_len) { + /* Output buffer too small */ + return 0; + } + + op = output; + + /* Find first non-zero register */ + for (i = 0; i < value_len; i++) { + if (value[i] != 0) + break; + } + + if (i < value_len) { + /* The second character could be zero, skip if it is */ + if (((value[i] & 0xff00) >> 8) == 0) { + *(op++) = (char)(value[i] & 0xff); + i++; + } + + /* For each remaining register, take MSB then LSB into string */ + for (; i < value_len; i++) { + *(op++) = (char)((value[i] & 0xff00) >> 8); + *(op++) = (char)(value[i] & 0xff); + } + } + + /* Add zero termination */ + *op = 0; + + assert(op < output + output_len); + + /* Find last non-zero/space */ + for (op--; op > output && (*op == 0 || *op == ' '); op--); + + /* Cut off rest of string */ + *(op + 1) = 0; + + /* Translate charaters outside of 0x20-0x7e range to spaces */ + for (; op > output; op--) { + if (*op < 0x20 || *op > 0x7e) { + *op = ' '; + } + } + + return 1; +} + +static int _apc_modbus_from_string(const char *value, uint16_t *output, size_t output_len) +{ + size_t value_len, vi, oi; + uint16_t tmp; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + value_len = strlen(value); + + if (value_len > (output_len * sizeof(uint16_t))) { + /* Output buffer too small */ + return 0; + } + + for (vi = 0, oi = 0; vi < value_len && oi < output_len; vi += 2, oi++) { + tmp = value[vi] << 8; + if (vi + 1 < value_len) + tmp |= value[vi + 1]; + output[oi] = tmp; + } + + for (; oi < output_len; oi++) { + output[oi] = 0; + } + + return 1; +} + +/* Converts a Modbus integer to a uint64_t. + * See MPAO-98KJ7F_EN section 1.3.1 Numbers. */ +static int _apc_modbus_to_uint64(const uint16_t *value, const size_t value_len, uint64_t *output) +{ + size_t i; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + *output = 0; + for (i = 0; i < value_len; i++) { + *output = (*output << 16) | value[i]; + } + + return 1; +} + +static int _apc_modbus_from_uint64(uint64_t value, uint16_t *output, size_t output_len) +{ + ssize_t oi; + size_t bits; + + if (output == NULL) { + /* Invalid parameters */ + return 0; + } + + bits = output_len * sizeof(uint16_t) * 8; + if (bits < 64) { + if (value > ((1ULL << bits) - 1)) { + /* Overflow */ + return 0; + } + } + + for (oi = output_len - 1; oi >= 0; oi--) { + output[oi] = (uint16_t)(value & 0xFFFF); + value >>= 16; + } + + return 1; +} + +static int _apc_modbus_from_uint64_string(const char *value, uint16_t *output, size_t output_len) +{ + uint64_t value_uint; + char *endptr; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + errno = 0; + value_uint = strtoull(value, &endptr, 0); + if (endptr == value || *endptr != '\0' || errno > 0) { + return 0; + } + + return _apc_modbus_from_uint64(value_uint, output, output_len); +} + +static int _apc_modbus_to_int64(const uint16_t *value, const size_t value_len, int64_t *output) +{ + size_t shiftval; + + if (!_apc_modbus_to_uint64(value, value_len, (uint64_t*)output)) { + return 0; + } + + shiftval = (8 * (sizeof(int64_t) - (value_len * sizeof(uint16_t)))); + *output <<= shiftval; + *output >>= shiftval; + + return 1; +} + +static int _apc_modbus_from_int64(int64_t value, uint16_t *output, size_t output_len) +{ + ssize_t oi; + size_t bits; + + if (output == NULL) { + /* Invalid parameters */ + return 0; + } + + bits = output_len * sizeof(uint16_t) * 8; + if (value > ((1LL << (bits - 1)) - 1) || + value < -(1LL << (bits - 1))) { + /* Overflow */ + return 0; + } + + for (oi = output_len - 1; oi >= 0; oi--) { + output[oi] = (uint16_t)(value & 0xFFFF); + value >>= 16; + } + + return 1; +} + +static int _apc_modbus_from_int64_string(const char *value, uint16_t *output, size_t output_len) +{ + int64_t value_int; + char *endptr; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + errno = 0; + value_int = strtoll(value, &endptr, 0); + if (endptr == value || *endptr != '\0' || errno > 0) { + return 0; + } + + return _apc_modbus_from_int64(value_int, output, output_len); +} + +static int _apc_modbus_to_double(const apc_modbus_value_t *value, double *output) +{ + int factor; + + if (value == NULL || output == NULL) { + /* Invalid parameters */ + return 0; + } + + factor = 1 << value->scale; + + assert(value->type <= apc_modbus_value_types_max); + switch (value->type) { + case APC_VT_INT: + *output = (double)value->data.int_value / factor; + break; + case APC_VT_UINT: + *output = (double)value->data.uint_value / factor; + break; + case APC_VT_STRING: + return 0; + } + + return 1; +} + +static int _apc_modbus_double_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + double double_value; + const char *format; + int res; + + if (output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (!_apc_modbus_to_double(value, &double_value)) { + return 0; + } + + if (value->variable_ptr) { + *((double*)value->variable_ptr) = double_value; + } + + format = "%f"; + if (value->format != NULL) + format = value->format; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + res = snprintf(output, output_len, format, double_value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + + return 1; +} + +static apc_modbus_converter_t _apc_modbus_double_conversion = { _apc_modbus_double_to_nut, NULL }; + +static int _apc_modbus_power_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + double double_value; + const char *format; + int res; + + if (output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (!_apc_modbus_to_double(value, &double_value)) { + return 0; + } + + if (value->variable_ptr) { + /* variable_ptr points to the nominal value */ + double_value = (double_value / 100.0) * *((double*)value->variable_ptr); + } + + format = "%f"; + if (value->format != NULL) + format = value->format; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + res = snprintf(output, output_len, format, double_value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + + return 1; +} + +static apc_modbus_converter_t _apc_modbus_power_conversion = { _apc_modbus_power_to_nut, NULL }; + +static int _apc_modbus_voltage_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + int res; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (value->type != APC_VT_UINT) { + return 0; + } + + if (value->data.uint_value == 0xffff) { + /* Not applicable */ + res = snprintf(output, output_len, "NA"); + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + return 1; + } + + return _apc_modbus_double_to_nut(value, output, output_len); +} + +static apc_modbus_converter_t _apc_modbus_voltage_conversion = { _apc_modbus_voltage_to_nut, NULL }; + +static int _apc_modbus_efficiency_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + char *cause; + int res; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (value->type != APC_VT_INT) { + return 0; + } + + switch (value->data.int_value) { + case -1: + cause = "NotAvailable"; + break; + case -2: + cause = "LoadTooLow"; + break; + case -3: + cause = "OutputOff"; + break; + case -4: + cause = "OnBattery"; + break; + case -5: + cause = "InBypass"; + break; + case -6: + cause = "BatteryCharging"; + break; + case -7: + cause = "PoorACInput"; + break; + case -8: + cause = "BatteryDisconnected"; + break; + default: + return _apc_modbus_double_to_nut(value, output, output_len); + } + + res = snprintf(output, output_len, "%s", cause); + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + + return 1; +} + +static apc_modbus_converter_t _apc_modbus_efficiency_conversion = { _apc_modbus_efficiency_to_nut, NULL }; + +static int _apc_modbus_status_change_cause_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + char *cause; + int res; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (value->type != APC_VT_UINT) { + return 0; + } + + switch (value->data.uint_value) { + case 0: + cause = "SystemInitialization"; + break; + case 1: + cause = "HighInputVoltage"; + break; + case 2: + cause = "LowInputVoltage"; + break; + case 3: + cause = "DistortedInput"; + break; + case 4: + cause = "RapidChangeOfInputVoltage"; + break; + case 5: + cause = "HighInputFrequency"; + break; + case 6: + cause = "LowInputFrequency"; + break; + case 7: + cause = "FreqAndOrPhaseDifference"; + break; + case 8: + cause = "AcceptableInput"; + break; + case 9: + cause = "AutomaticTest"; + break; + case 10: + cause = "TestEnded"; + break; + case 11: + cause = "LocalUICommand"; + break; + case 12: + cause = "ProtocolCommand"; + break; + case 13: + cause = "LowBatteryVoltage"; + break; + case 14: + cause = "GeneralError"; + break; + case 15: + cause = "PowerSystemError"; + break; + case 16: + cause = "BatterySystemError"; + break; + case 17: + cause = "ErrorCleared"; + break; + case 18: + cause = "AutomaticRestart"; + break; + case 19: + cause = "DistortedInverterOutput"; + break; + case 20: + cause = "InverterOutputAcceptable"; + break; + case 21: + cause = "EPOInterface"; + break; + case 22: + cause = "InputPhaseDeltaOutOfRange"; + break; + case 23: + cause = "InputNeutralNotConnected"; + break; + case 24: + cause = "ATSTransfer"; + break; + case 25: + cause = "ConfigurationChange"; + break; + case 26: + cause = "AlertAsserted"; + break; + case 27: + cause = "AlertCleared"; + break; + case 28: + cause = "PlugRatingExceeded"; + break; + case 29: + cause = "OutletGroupStateChange"; + break; + case 30: + cause = "FailureBypassExpired"; + break; + default: + cause = "Unknown"; + break; + } + + res = snprintf(output, output_len, "%s", cause); + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + + return 1; +} + +static apc_modbus_converter_t _apc_modbus_status_change_cause_conversion = { _apc_modbus_status_change_cause_to_nut, NULL }; + +static int _apc_modbus_string_join(const char *values[], size_t values_len, const char *separator, char *output, size_t output_len) +{ + size_t i; + size_t output_idx; + int res; + + if (values == NULL || values_len == 0 || separator == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + output_idx = 0; + + for (i = 0; i < values_len && output_idx < output_len; i++) { + if (values[i] == NULL) + continue; + + if (i == 0) { + res = snprintf(output + output_idx, output_len - output_idx, "%s", values[i]); + } else { + res = snprintf(output + output_idx, output_len - output_idx, "%s%s", separator, values[i]); + } + + if (res < 0 || (size_t)res >= output_len) { + return 0; + } + + output_idx += res; + } + + return 1; +} + +static int _apc_modbus_battery_test_status_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + const char *result, *source, *modifier; + const char *values[3]; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (value->type != APC_VT_UINT) { + return 0; + } + + result = NULL; + if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_PENDING)) { + result = "Pending"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_INPROGRESS)) { + result = "InProgress"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_PASSED)) { + result = "Passed"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_FAILED)) { + result = "Failed"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_REFUSED)) { + result = "Refused"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_ABORTED)) { + result = "Aborted"; + } + + source = NULL; + if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_PROTOCOL)) { + source = "Source: Protocol"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_LOCALUI)) { + source = "Source: LocalUI"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_INTERNAL)) { + source = "Source: Internal"; + } + + modifier = NULL; + if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_INVALIDSTATE)) { + modifier = "Modifier: InvalidState"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_INTERNALFAULT)) { + modifier = "Modifier: InternalFault"; + } else if ((value->data.uint_value & APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_STATEOFCHARGENOTACCEPTABLE)) { + modifier = "Modifier: StateOfChargeNotAcceptable"; + } + + values[0] = result; + values[1] = source; + values[2] = modifier; + return _apc_modbus_string_join(values, SIZEOF_ARRAY(values), ", ", output, output_len); +} + +static apc_modbus_converter_t _apc_modbus_battery_test_status_conversion = { _apc_modbus_battery_test_status_to_nut, NULL }; + +static const time_t apc_date_start_offset = 946684800; /* 2000-01-01 00:00 */ + +static int _apc_modbus_date_to_nut(const apc_modbus_value_t *value, char *output, size_t output_len) +{ + struct tm tm_info; + time_t time_stamp; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + if (value->type != APC_VT_UINT) { + return 0; + } + + time_stamp = ((int64_t)value->data.uint_value * 86400) + apc_date_start_offset; + gmtime_r(&time_stamp, &tm_info); + strftime(output, output_len, "%Y-%m-%d", &tm_info); + + return 1; +} + +static int _apc_modbus_date_from_nut(const char *value, uint16_t *output, size_t output_len) +{ + struct tm tm_struct; + time_t epoch_time; + uint64_t uint_value; + + if (value == NULL || output == NULL || output_len == 0) { + /* Invalid parameters */ + return 0; + } + + memset(&tm_struct, 0, sizeof(tm_struct)); + if (strptime(value, "%Y-%m-%d", &tm_struct) == NULL) { + return 0; + } + + if ((epoch_time = timegm(&tm_struct)) == -1) { + return 0; + } + + uint_value = (epoch_time - apc_date_start_offset) / 86400; + + return _apc_modbus_from_uint64(uint_value, output, output_len); +} + +static apc_modbus_converter_t _apc_modbus_date_conversion = { _apc_modbus_date_to_nut, _apc_modbus_date_from_nut }; + +typedef struct { + const char *nut_variable_name; + size_t modbus_addr; + size_t modbus_len; /* Number of uint16_t registers */ + apc_modbus_value_types value_type; + apc_modbus_value_flags value_flags; + apc_modbus_converter_t *value_converter; + const char *value_format; + int value_scale; + void *variable_ptr; +} apc_modbus_register_t; + +/* Values that only need to be updated once on startup */ +static apc_modbus_register_t apc_modbus_register_map_inventory[] = { + { "ups.firmware", 516, 8, APC_VT_STRING, 0, NULL, "%s", 0, NULL }, + { "ups.model", 532, 16, APC_VT_STRING, 0, NULL, "%s", 0, NULL }, /* also device.model, filled automatically */ + { "ups.serial", 564, 8, APC_VT_STRING, 0, NULL, "%s", 0, NULL }, /* also device.serial, filled automatically */ + { "ups.power.nominal", 588, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.0f", 0, &power_nominal }, + { "ups.realpower.nominal", 589, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.0f", 0, &realpower_nominal }, + { "ups.mfr.date", 591, 1, APC_VT_UINT, 0, &_apc_modbus_date_conversion, NULL, 0, NULL }, + { "battery.date", 595, 1, APC_VT_UINT, APC_VF_RW, &_apc_modbus_date_conversion, NULL, 0, NULL }, + { "ups.id", 596, 8, APC_VT_STRING, APC_VF_RW, NULL, "%s", 0, NULL }, + { "outlet.group.0.name", 604, 8, APC_VT_STRING, APC_VF_RW, NULL, "%s", 0, NULL }, + { "outlet.group.1.name", 612, 8, APC_VT_STRING, APC_VF_RW, NULL, "%s", 0, NULL }, + { "outlet.group.2.name", 620, 8, APC_VT_STRING, APC_VF_RW, NULL, "%s", 0, NULL }, + { "outlet.group.3.name", 628, 8, APC_VT_STRING, APC_VF_RW, NULL, "%s", 0, NULL }, + { NULL, 0, 0, 0, 0, NULL, NULL, 0.0f, NULL } +}; + +static apc_modbus_register_t apc_modbus_register_map_status[] = { + { "input.transfer.reason", 2, 1, APC_VT_UINT, 0, &_apc_modbus_status_change_cause_conversion, NULL, 0, NULL }, + { "ups.test.result", 23, 1, APC_VT_UINT, 0, &_apc_modbus_battery_test_status_conversion, NULL, 0, NULL }, + { NULL, 0, 0, 0, 0, NULL, NULL, 0.0f, NULL } +}; + +static apc_modbus_register_t apc_modbus_register_map_dynamic[] = { + { "battery.runtime", 128, 2, APC_VT_UINT, 0, NULL, "%u", 0, NULL }, + { "battery.charge", 130, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.2f", 9, NULL }, + { "battery.voltage", 131, 1, APC_VT_INT, 0, &_apc_modbus_double_conversion, "%.2f", 5, NULL }, + { "battery.date.maintenance", 133, 1, APC_VT_UINT, 0, &_apc_modbus_date_conversion, NULL, 0, NULL }, + { "battery.temperature", 135, 1, APC_VT_INT, 0, &_apc_modbus_double_conversion, "%.2f", 7, NULL }, + { "ups.load", 136, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.2f", 8, NULL }, + { "ups.realpower", 136, 1, APC_VT_UINT, 0, &_apc_modbus_power_conversion, "%.2f", 8, &realpower_nominal }, + { "ups.power", 138, 1, APC_VT_UINT, 0, &_apc_modbus_power_conversion, "%.2f", 8, &power_nominal }, + { "output.current", 140, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.2f", 5, NULL }, + { "output.voltage", 142, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.2f", 6, NULL }, + { "output.frequency", 144, 1, APC_VT_UINT, 0, &_apc_modbus_double_conversion, "%.2f", 7, NULL }, + { "experimental.output.energy", 145, 2, APC_VT_UINT, 0, NULL, "%u", 0, NULL }, + { "input.voltage", 151, 1, APC_VT_UINT, 0, &_apc_modbus_voltage_conversion, "%.2f", 6, NULL }, + { "ups.efficiency", 154, 1, APC_VT_INT, 0, &_apc_modbus_efficiency_conversion, "%.1f", 7, NULL }, + { "ups.timer.shutdown", 155, 1, APC_VT_INT, 0, NULL, "%d", 0, NULL }, + { "ups.timer.start", 156, 1, APC_VT_INT, 0, NULL, "%d", 0, NULL }, + { "ups.timer.reboot", 157, 2, APC_VT_INT, 0, NULL, "%d", 0, NULL }, + { NULL, 0, 0, 0, 0, NULL, NULL, 0.0f, NULL } +}; + +static apc_modbus_register_t apc_modbus_register_map_static[] = { + { "input.transfer.high", 1026, 1, APC_VT_UINT, APC_VF_RW, NULL, "%u", 0, NULL }, + { "input.transfer.low", 1027, 1, APC_VT_UINT, APC_VF_RW, NULL, "%u", 0, NULL }, + { "ups.delay.shutdown", 1029, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "ups.delay.start", 1030, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "ups.delay.reboot", 1031, 2, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.0.delay.shutdown", 1029, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.0.delay.start", 1030, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.0.delay.reboot", 1031, 2, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.1.delay.shutdown", 1034, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.1.delay.start", 1035, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.1.delay.reboot", 1036, 2, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.2.delay.shutdown", 1039, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.2.delay.start", 1040, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.2.delay.reboot", 1041, 2, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.3.delay.shutdown", 1044, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.3.delay.start", 1045, 1, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { "outlet.group.3.delay.reboot", 1046, 2, APC_VT_INT, APC_VF_RW, NULL, "%d", 0, NULL }, + { NULL, 0, 0, 0, 0, NULL, NULL, 0.0f, NULL } +}; + +static apc_modbus_register_t* apc_modbus_register_maps[] = { + apc_modbus_register_map_inventory, + apc_modbus_register_map_status, + apc_modbus_register_map_dynamic, + apc_modbus_register_map_static +}; + +static void _apc_modbus_close(int free_modbus) +{ + if (modbus_ctx != NULL) { + modbus_close(modbus_ctx); + if (free_modbus) { + modbus_free(modbus_ctx); + modbus_ctx = NULL; + } + } + + is_open = 0; +} + +#if defined NUT_MODBUS_HAS_USB +static void _apc_modbus_create_reopen_matcher(void) +{ + int r; + + if (reopen_matcher != NULL) { + USBFreeExactMatcher(reopen_matcher); + reopen_matcher = NULL; + } + + r = USBNewExactMatcher(&reopen_matcher, &usbdevice); + if (r < 0) { + fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); + } + + reopen_matcher->next = best_matcher; + best_matcher = reopen_matcher; +} +#endif /* defined NUT_MODBUS_HAS_USB */ + +static int _apc_modbus_reopen(void) +{ + dstate_setinfo("driver.state", "reconnect.trying"); + + if (modbus_connect(modbus_ctx) < 0) { + upslogx(LOG_ERR, "%s: Unable to connect Modbus: %s", __func__, modbus_strerror(errno)); + return 0; + } + + +#if defined NUT_MODBUS_HAS_USB + /* We might have matched a new device in the modbus_connect callback. + * Because of this we want a new exact matcher. */ + best_matcher = best_matcher->next; + _apc_modbus_create_reopen_matcher(); +#endif /* defined NUT_MODBUS_HAS_USB */ + + usleep(1000000); + modbus_flush(modbus_ctx); + + is_open = 1; + + dstate_setinfo("driver.state", "reconnect.updateinfo"); + _apc_modbus_read_inventory(); + dstate_setinfo("driver.state", "quiet"); + + return 1; +} + +static useconds_t _apc_modbus_get_time_us(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((useconds_t)tv.tv_sec * (useconds_t)1000000) + (useconds_t)tv.tv_usec; +} + +static void _apc_modbus_interframe_delay(void) +{ + /* 4.2.2 Modbus Message RTU Framing, interframe delay */ + + static useconds_t last_send_time = 0; + static const useconds_t inter_frame_delay = 35000; + useconds_t current_time, delta_time; + + current_time = _apc_modbus_get_time_us(); + delta_time = current_time - last_send_time; + + if (delta_time > inter_frame_delay) { + /* Already over the inter frame delay */ + goto interframe_delay_exit; + } + + usleep(inter_frame_delay - delta_time); + +interframe_delay_exit: + last_send_time = current_time; +} + +static void _apc_modbus_handle_error(modbus_t *ctx) +{ + static int flush_retries = 0; + int flush = 0; +#ifdef WIN32 + int wsa_error; +#endif /* WIN32 */ + + /* + * We could enable MODBUS_ERROR_RECOVERY_LINK but that would just get stuck + * in libmodbus until recovery. The only indication of this is that the + * program is stuck and debug prints by libmodbus, which we don't want to + * enable on release. + * + * Instead we detect timout errors and do a sleep + flush, on every other + * error or when flush didn't work we do a reconnect. + */ + +#ifdef WIN32 + wsa_error = WSAGetLastError(); + if (wsa_error == WSAETIMEDOUT) { + flush = 1; + } +#else + if (errno == ETIMEDOUT) { + flush = 1; + } +#endif /* WIN32 */ + + if (flush > 0 && flush_retries++ < 5) { + usleep(1000000); + modbus_flush(ctx); + } else { + flush_retries = 0; + upslogx(LOG_ERR, "%s: Closing connection", __func__); + /* Close without free, will retry connection on next update */ + _apc_modbus_close(0); + } +} + +static int _apc_modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) +{ + _apc_modbus_interframe_delay(); + + if (modbus_read_registers(ctx, addr, nb, dest) > 0) { + return 1; + } else { + upslogx(LOG_ERR, "%s: Read of %d:%d failed: %s (%s)", __func__, addr, addr + nb, modbus_strerror(errno), device_path); + _apc_modbus_handle_error(ctx); + return 0; + } +} + +static int _apc_modbus_update_value(apc_modbus_register_t *regs_info, const uint16_t *regs, const size_t regs_len) +{ + apc_modbus_value_t value; + char strbuf[33], nutvbuf[128]; + int dstate_flags; + + if (regs_info == NULL || regs == NULL || regs_len == 0) { + /* Invalid parameters */ + return 0; + } + + value.type = regs_info->value_type; + value.format = regs_info->value_format; + value.scale = regs_info->value_scale; + value.variable_ptr = regs_info->variable_ptr; + + assert(regs_info->value_type <= apc_modbus_value_types_max); + switch (regs_info->value_type) { + case APC_VT_STRING: + _apc_modbus_to_string(regs, regs_info->modbus_len, strbuf, sizeof(strbuf)); + value.data.string_value = strbuf; + break; + case APC_VT_INT: + _apc_modbus_to_int64(regs, regs_info->modbus_len, &value.data.int_value); + break; + case APC_VT_UINT: + _apc_modbus_to_uint64(regs, regs_info->modbus_len, &value.data.uint_value); + break; + } + + if (regs_info->value_converter != NULL && regs_info->value_converter->apc_to_nut != NULL) { + /* If we have a converter, use it and set the value as a string */ + if (!regs_info->value_converter->apc_to_nut(&value, nutvbuf, sizeof(nutvbuf))) { + upslogx(LOG_ERR, "%s: Failed to convert register %" PRIuSIZE ":%" PRIuSIZE, __func__, + regs_info->modbus_addr, regs_info->modbus_addr + regs_info->modbus_len); + return 0; + } + dstate_setinfo(regs_info->nut_variable_name, "%s", nutvbuf); + } else { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + assert(regs_info->value_type <= apc_modbus_value_types_max); + switch (regs_info->value_type) { + case APC_VT_STRING: + dstate_setinfo(regs_info->nut_variable_name, regs_info->value_format, value.data.string_value); + break; + case APC_VT_INT: + dstate_setinfo(regs_info->nut_variable_name, regs_info->value_format, value.data.int_value); + break; + case APC_VT_UINT: + dstate_setinfo(regs_info->nut_variable_name, regs_info->value_format, value.data.uint_value); + break; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + + dstate_flags = 0; + if (regs_info->value_type == APC_VT_STRING) { + dstate_flags |= ST_FLAG_STRING; + } + if ((regs_info->value_flags & APC_VF_RW)) { + dstate_flags |= ST_FLAG_RW; + } + dstate_setflags(regs_info->nut_variable_name, dstate_flags); + if (regs_info->value_type == APC_VT_STRING) { + dstate_setaux(regs_info->nut_variable_name, regs_info->modbus_len * sizeof(uint16_t)); + } + + return 1; +} + +static int _apc_modbus_process_registers(apc_modbus_register_t* values, const uint16_t *regs, const size_t regs_len, const size_t regs_offset) +{ + size_t i; + apc_modbus_register_t *v; + + for (i = 0; values[i].nut_variable_name; i++) { + v = &values[i]; + + if ((size_t)v->modbus_addr < regs_offset || (size_t)(v->modbus_addr + v->modbus_len) > regs_offset + regs_len) { + continue; + } + + _apc_modbus_update_value(v, regs + (v->modbus_addr - regs_offset), v->modbus_len); + } + + return 1; +} + +static int _apc_modbus_read_inventory(void) +{ + uint16_t regbuf[120]; + int start_addr; + uint16_t sog_relay_config; + int outlet_group_count; + + /* Inventory Information */ + start_addr = apc_modbus_register_map_inventory[0].modbus_addr; + if (_apc_modbus_read_registers(modbus_ctx, start_addr, SIZEOF_ARRAY(regbuf), regbuf)) { + sog_relay_config = regbuf[APC_MODBUS_SOGRELAYCONFIGSETTING_BF_REG - start_addr]; + + outlet_group_count = 0; + if ((sog_relay_config & APC_MODBUS_SOGRELAYCONFIGSETTING_BF_MOG_PRESENT)) { + outlet_group_count++; + } + if ((sog_relay_config & APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_0_PRESENT)) { + outlet_group_count++; + } + if ((sog_relay_config & APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_1_PRESENT)) { + outlet_group_count++; + } + if ((sog_relay_config & APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_2_PRESENT)) { + outlet_group_count++; + } + /* Documentation says there is a bit for SOG3, but everything else does not have it */ + if ((sog_relay_config & APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_3_PRESENT)) { + upslogx(LOG_WARNING, "%s: SOG3 present, but we don't know how to use it", __func__); + outlet_group_count++; + } + + dstate_setinfo("outlet.group.count", "%d", outlet_group_count); + + _apc_modbus_process_registers(apc_modbus_register_map_inventory, regbuf, SIZEOF_ARRAY(regbuf), start_addr); + } else { + return 0; + } + + return 1; +} + +static int _apc_modbus_setvar(const char *nut_varname, const char *str_value) +{ + size_t mi, i; + int addr, nb, r; + apc_modbus_register_t *apc_map = NULL, *apc_value = NULL; + uint16_t reg_value[16]; + + for (mi = 0; mi < SIZEOF_ARRAY(apc_modbus_register_maps) && apc_value == NULL; mi++) { + apc_map = apc_modbus_register_maps[mi]; + + for (i = 0; apc_map[i].nut_variable_name; i++) { + if (!strcasecmp(nut_varname, apc_map[i].nut_variable_name)) { + apc_value = &apc_map[i]; + break; + } + } + } + + if (!apc_map || !apc_value) { + upslogx(LOG_WARNING, "%s: [%s] is unknown", __func__, nut_varname); + return STAT_SET_UNKNOWN; + } + + if (!(apc_value->value_flags & APC_VF_RW)) { + upslogx(LOG_WARNING, "%s: [%s] is not writable", __func__, nut_varname); + return STAT_SET_INVALID; + } + + assert(apc_value->modbus_len < SIZEOF_ARRAY(reg_value)); + + if (apc_value->value_converter && apc_value->value_converter->nut_to_apc) { + if (!apc_value->value_converter->nut_to_apc(str_value, reg_value, apc_value->modbus_len)) { + upslogx(LOG_WARNING, "%s: [%s] failed to convert value", __func__, nut_varname); + return STAT_SET_CONVERSION_FAILED; + } + } else { + assert(apc_value->value_type <= apc_modbus_value_types_max); + switch (apc_value->value_type) { + case APC_VT_STRING: + r = _apc_modbus_from_string(str_value, reg_value, apc_value->modbus_len); + break; + case APC_VT_INT: + r = _apc_modbus_from_int64_string(str_value, reg_value, apc_value->modbus_len); + break; + case APC_VT_UINT: + r = _apc_modbus_from_uint64_string(str_value, reg_value, apc_value->modbus_len); + break; + } + + if (!r) { + upslogx(LOG_WARNING, "%s: [%s] failed to convert value", __func__, nut_varname); + return STAT_SET_CONVERSION_FAILED; + } + } + + addr = apc_value->modbus_addr; + nb = apc_value->modbus_len; + if (modbus_write_registers(modbus_ctx, addr, nb, reg_value) < 0) { + upslogx(LOG_ERR, "%s: Write of %d:%d failed: %s (%s)", __func__, addr, addr + nb, modbus_strerror(errno), device_path); + _apc_modbus_handle_error(modbus_ctx); + return STAT_SET_FAILED; + } + + /* There seem to be some communication problems if we don't wait after writing. + * Maybe there is some register we need to poll for write completion? + */ + usleep(100000); + + upslogx(LOG_INFO, "SET %s='%s'", nut_varname, str_value); + + if (_apc_modbus_read_registers(modbus_ctx, addr, nb, reg_value)) { + _apc_modbus_process_registers(apc_map, reg_value, nb, addr); + } + + return STAT_SET_HANDLED; +} + +typedef struct { + const char *nut_command_name; + size_t modbus_addr; + size_t modbus_len; /* Number of uint16_t registers */ + uint64_t value; +} apc_modbus_command_t; + +static apc_modbus_command_t apc_modbus_command_map[] = { + { "test.battery.start", APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_REG, 1, APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_START }, + { "test.battery.stop", APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_REG, 1, APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_ABORT }, + { "test.panel.start", APC_MODBUS_USERINTERFACECOMMAND_BF_REG, 1, APC_MODBUS_USERINTERFACECOMMAND_BF_SHORT_TEST }, + { "calibrate.start", APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_REG, 1, APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_START }, + { "calibrate.stop", APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_REG, 1, APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_ABORT }, + { "bypass.start", APC_MODBUS_UPSCOMMAND_BF_REG, 2, APC_MODBUS_UPSCOMMAND_BF_OUTPUT_INTO_BYPASS }, + { "bypass.stop", APC_MODBUS_UPSCOMMAND_BF_REG, 2, APC_MODBUS_UPSCOMMAND_BF_OUTPUT_OUT_OF_BYPASS }, + { "beeper.mute", APC_MODBUS_USERINTERFACECOMMAND_BF_REG, 1, APC_MODBUS_USERINTERFACECOMMAND_BF_MUTE_ALL_ACTIVE_AUDIBLE_ALARMS }, + { "load.off", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "load.on", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "load.off.delay", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "load.on.delay", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_ON_DELAY }, + { "shutdown.return", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "shutdown.stayoff", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "shutdown.reboot", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "shutdown.reboot.graceful", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "outlet.0.shutdown.return", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "outlet.0.load.off", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "outlet.0.load.on", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "outlet.0.load.cycle", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP }, + { "outlet.1.shutdown.return", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_0 | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "outlet.1.load.off", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_0 }, + { "outlet.1.load.on", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_0 }, + { "outlet.1.load.cycle", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_0 }, + { "outlet.2.shutdown.return", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_1 | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "outlet.2.load.off", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_1 }, + { "outlet.2.load.on", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_1 }, + { "outlet.2.load.cycle", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_1 }, + { "outlet.3.shutdown.return", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_2 | APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY }, + { "outlet.3.load.off", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_2 }, + { "outlet.3.load.on", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_2 }, + { "outlet.3.load.cycle", APC_MODBUS_OUTLETCOMMAND_BF_REG, 2, + APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_2 }, + { NULL, 0, 0, 0 } +}; + +static int _apc_modbus_instcmd(const char *nut_cmdname, const char *extra) +{ + size_t i; + int addr, nb; + apc_modbus_command_t *apc_command = NULL; + uint16_t value[4]; /* Max 64-bit */ + + NUT_UNUSED_VARIABLE(extra); + + for (i = 0; apc_modbus_command_map[i].nut_command_name; i++) { + if (!strcasecmp(nut_cmdname, apc_modbus_command_map[i].nut_command_name)) { + apc_command = &apc_modbus_command_map[i]; + break; + } + } + + if (!apc_command) { + upslogx(LOG_WARNING, "%s: [%s] is unknown", __func__, nut_cmdname); + return STAT_INSTCMD_UNKNOWN; + } + + assert(apc_command->modbus_len <= SIZEOF_ARRAY(value)); + + if (!_apc_modbus_from_uint64(apc_command->value, value, apc_command->modbus_len)) { + upslogx(LOG_WARNING, "%s: [%s] failed to convert value", __func__, nut_cmdname); + return STAT_INSTCMD_CONVERSION_FAILED; + } + + addr = apc_command->modbus_addr; + nb = apc_command->modbus_len; + if (modbus_write_registers(modbus_ctx, addr, nb, value) < 0) { + upslogx(LOG_ERR, "%s: Write of %d:%d failed: %s (%s)", __func__, addr, addr + nb, modbus_strerror(errno), device_path); + _apc_modbus_handle_error(modbus_ctx); + return STAT_INSTCMD_FAILED; + } + + return STAT_INSTCMD_HANDLED; +} + +void upsdrv_initinfo(void) +{ + size_t i; + + if (!_apc_modbus_read_inventory()) { + fatalx(EXIT_FAILURE, "Can't read inventory information from the UPS"); + } + + dstate_setinfo("ups.mfr", "American Power Conversion"); /* also device.mfr, filled automatically */ + + dstate_setinfo("device.type", "ups"); + + for (i = 0; apc_modbus_command_map[i].nut_command_name; i++) { + dstate_addcmd(apc_modbus_command_map[i].nut_command_name); + } + + upsh.setvar = _apc_modbus_setvar; + upsh.instcmd = _apc_modbus_instcmd; + +} + +void upsdrv_updateinfo(void) +{ + uint16_t regbuf[32]; + uint64_t value; + + if (!is_open) { + if (!_apc_modbus_reopen()) { + upsdebugx(2, "Failed to reopen modbus"); + dstate_datastale(); + return; + } + } + + alarm_init(); + status_init(); + + /* Status Data */ + if (_apc_modbus_read_registers(modbus_ctx, 0, 27, regbuf)) { + /* UPSStatus_BF, 2 registers */ + _apc_modbus_to_uint64(®buf[0], 2, &value); + if (value & (1 << 1)) { + status_set("OL"); + } + if (value & (1 << 2)) { + status_set("OB"); + } + if (value & (1 << 3)) { + status_set("BYPASS"); + } + if (value & (1 << 4)) { + status_set("OFF"); + } + if (value & (1 << 5)) { + alarm_set("General fault"); + } + if (value & (1 << 6)) { + alarm_set("Input not acceptable"); + } + if (value & (1 << 7)) { + status_set("TEST"); + } + if (value & (1 << 13)) { + status_set("HE"); /* High efficiency / ECO mode*/ + } + if (value & (1 << 21)) { + status_set("OVER"); + } + + /* SimpleSignalingStatus_BF, 1 register */ + _apc_modbus_to_uint64(®buf[18], 1, &value); + if (value & (1 << 1)) { /* ShutdownImminent */ + status_set("LB"); + } + + /* BatterySystemError_BF, 1 register */ + _apc_modbus_to_uint64(®buf[18], 1, &value); + if (value & (1 << 1)) { /* NeedsReplacement */ + status_set("RB"); + } + + /* RunTimeCalibrationStatus_BF, 1 register */ + _apc_modbus_to_uint64(®buf[24], 1, &value); + if (value & (1 << 1)) { /* InProgress */ + status_set("CAL"); + } + + _apc_modbus_process_registers(apc_modbus_register_map_status, regbuf, 27, 0); + } else { + dstate_datastale(); + return; + } + + /* Dynamic Data */ + if (_apc_modbus_read_registers(modbus_ctx, 128, 32, regbuf)) { + /* InputStatus_BF, 1 register */ + _apc_modbus_to_uint64(®buf[22], 1, &value); + if (value & (1 << 5)) { + status_set("BOOST"); + } + if (value & (1 << 6)) { + status_set("TRIM"); + } + + _apc_modbus_process_registers(apc_modbus_register_map_dynamic, regbuf, 32, 128); + } else { + dstate_datastale(); + return; + } + + /* Static Data */ + if (_apc_modbus_read_registers(modbus_ctx, 1026, 22, regbuf)) { + _apc_modbus_process_registers(apc_modbus_register_map_static, regbuf, 22, 1026); + } else { + dstate_datastale(); + return; + } + + alarm_commit(); + status_commit(); + dstate_dataok(); +} + +void upsdrv_shutdown(void) +{ + modbus_write_register(modbus_ctx, APC_MODBUS_OUTLETCOMMAND_BF_REG, APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN | APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP); +} + +void upsdrv_help(void) +{ +} + +void upsdrv_makevartable(void) +{ +#if defined NUT_MODBUS_HAS_USB + nut_usb_addvars(); +#endif /* defined NUT_MODBUS_HAS_USB */ + +#if defined NUT_MODBUS_HAS_USB + addvar(VAR_VALUE, "porttype", "Modbus port type (serial, tcp, usb, default=usb)"); +#else + addvar(VAR_VALUE, "porttype", "Modbus port type (serial, tcp, default=serial)"); +#endif /* defined NUT_MODBUS_HAS_USB */ + addvar(VAR_VALUE, "slaveid", "Modbus slave id (default=1)"); + addvar(VAR_VALUE, "response_timeout_ms", "Modbus response timeout in milliseconds"); + + /* Serial RTU parameters */ + addvar(VAR_VALUE, "baudrate", "Modbus serial RTU communication speed in baud (default=9600)"); + addvar(VAR_VALUE, "parity", "Modbus serial RTU parity (N=none, E=even, O=odd, default=N)"); + addvar(VAR_VALUE, "databits", "Modbus serial RTU data bit count (default=8)"); + addvar(VAR_VALUE, "stopbits", "Modbus serial RTU stop bit count (default=1)"); +} + +#if defined NUT_MODBUS_HAS_USB +static int _apc_modbus_usb_device_match_func(USBDevice_t *hd, void *privdata) +{ + int status; + + NUT_UNUSED_VARIABLE(privdata); + + status = is_usb_device_supported(apc_modbus_usb_device_table, hd); + + switch (status) { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +static USBDeviceMatcher_t apc_modbus_usb_device_matcher = { &_apc_modbus_usb_device_match_func, NULL, NULL }; + +static void _apc_modbus_usb_lib_to_nut(const modbus_usb_device_t *device, USBDevice_t *out) +{ + /* This makes a USBDevice_t from modbus_usb_device_t so we can use our matchers */ + + static char bus_buf[4], device_buf[4], bus_port_buf[4]; + int res; + + assert(device != NULL); + assert(out != NULL); + + memset(out, 0, sizeof(USBDevice_t)); + + out->VendorID = device->vid; + out->ProductID = device->pid; + out->Vendor = device->vendor_str; + out->Product = device->product_str; + out->Serial = device->serial_str; + out->bcdDevice = device->bcd_device; + + res = snprintf(bus_buf, sizeof(bus_buf), "%03u", device->bus); + if (res < 0 || (size_t)res >= sizeof(bus_buf)) { + fatalx(EXIT_FAILURE, "failed to convert USB bus to string"); + } + out->Bus = bus_buf; + + res = snprintf(device_buf, sizeof(device_buf), "%03u", device->device_address); + if (res < 0 || (size_t)res >= sizeof(device_buf)) { + fatalx(EXIT_FAILURE, "failed to convert USB device address to string"); + } + out->Device = device_buf; + +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + res = snprintf(bus_port_buf, sizeof(bus_port_buf), "%03u", device->bus_port); + if (res < 0 || (size_t)res >= sizeof(bus_port_buf)) { + fatalx(EXIT_FAILURE, "failed to convert USB bus port to string"); + } + out->BusPort = bus_port_buf; +#endif +} + +static int _apc_modbus_usb_callback(const modbus_usb_device_t *device) +{ + HIDDesc_t *hid_desc; + size_t i; + HIDData_t *hid_cur_item, *hid_rtu_rx = NULL, *hid_rtu_tx = NULL; + HIDNode_t hid_cur_usage; + USBDeviceMatcher_t *current_matcher; + + if (device == NULL) { + upslogx(LOG_ERR, "%s: NULL device passed", __func__); + return -1; + } + + _apc_modbus_usb_lib_to_nut(device, &usbdevice); + + current_matcher = best_matcher; + while (current_matcher != NULL) { + if (current_matcher->match_function(&usbdevice, current_matcher->privdata) == 1) { + break; + } + + current_matcher = current_matcher->next; + } + + if (current_matcher == NULL) { + upsdebug_with_errno(1, "%s: Failed to match!", __func__); + return -1; + } + + upsdebugx(2, "%s: Matched %s %s (USB VID/PID %04x:%04x)", __func__, device->vendor_str, device->product_str, device->vid, device->pid); + + if (device->hid_report_descriptor_buf == NULL) { + upslogx(LOG_WARNING, "%s: No HID report descriptor, using defaults", __func__); + goto usb_callback_exit; + } + + upsdebugx(2, "%s: Checking %s %s (USB VID/PID %04x:%04x) report descriptors", __func__, device->vendor_str, device->product_str, device->vid, device->pid); + + hid_desc = Parse_ReportDesc(device->hid_report_descriptor_buf, (usb_ctrl_charbufsize)device->hid_report_descriptor_len); + if (!hid_desc) { + upsdebug_with_errno(1, "%s: Failed to parse report descriptor!", __func__); + return -1; + } + + for (i = 0; i < hid_desc->nitems; i++) { + hid_cur_item = &hid_desc->item[i]; + + hid_cur_usage = hid_cur_item->Path.Node[hid_cur_item->Path.Size - 1]; + + if (hid_cur_usage == modbus_rtu_usb_usage_rx) { + hid_rtu_rx = hid_cur_item; + } else if (hid_cur_usage == modbus_rtu_usb_usage_tx) { + hid_rtu_tx = hid_cur_item; + } + } + + if (hid_rtu_rx == NULL || hid_rtu_tx == NULL) { + upsdebugx(1, "%s: No Modbus USB report descriptor found", __func__); + Free_ReportDesc(hid_desc); + return -1; + } + + upsdebugx(1, "%s: Found report ids RX=0x%02x TX=0x%02x", __func__, hid_rtu_rx->ReportID, hid_rtu_tx->ReportID); + + modbus_rtu_usb_set_report_ids(modbus_ctx, hid_rtu_rx->ReportID, hid_rtu_tx->ReportID); + + Free_ReportDesc(hid_desc); + +usb_callback_exit: + dstate_setinfo("ups.vendorid", "%04x", device->vid); + dstate_setinfo("ups.productid", "%04x", device->pid); + + return 0; +} +#endif /* defined NUT_MODBUS_HAS_USB */ + +static int _apc_modbus_parse_host_port(const char *input, char *host, size_t host_buf_size, char *port, size_t port_buf_size, const uint16_t default_port) { + const char *start = input; + const char *end = input; + int port_int, r; + size_t host_size, port_size; + + if (*start == '[') { + start++; + end = strchr(start, ']'); + if (!end) { + upslogx(LOG_ERR, "%s: Invalid IPv6 notation", __func__); + return 0; + } + } else { + end = strchr(start, ':'); + } + + if (!end) { + /* Port is missing, use the default port */ + r = snprintf(host, host_buf_size, "%s", start); + if (r < 0 || (size_t)r >= host_buf_size) { + upslogx(LOG_ERR, "%s: Buffer size too small or encoding error", __func__); + return 0; + } + r = snprintf(port, port_buf_size, "%u", default_port); + if (r < 0 || (size_t)r >= port_buf_size) { + upslogx(LOG_ERR, "%s: Buffer size too small or encoding error", __func__); + return 0; + } + return 1; + } + + /* +1 for zero termination */ + host_size = (size_t)(end - start) + 1; + port_size = strlen(end + 1) + 1; + + if (host_size > host_buf_size || port_size > port_buf_size) { + upslogx(LOG_ERR, "%s: Buffer size too small", __func__); + return 0; + } + + snprintf(host, host_size, "%s", start); + snprintf(port, port_size, "%s", end + 1); + + port_int = atoi(port); + if (port_int < 0 || port_int > 65535) { + upslogx(LOG_ERR, "%s: Port out of range", __func__); + return 0; + } + + return 1; +} + +void upsdrv_initups(void) +{ + int r; +#if defined NUT_MODBUS_HAS_USB + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; + int has_nonzero_regex; + size_t i; +#endif /* defined NUT_MODBUS_HAS_USB */ + char *val; + int rtu_baudrate; + char rtu_parity; + int rtu_databits; + int rtu_stopbits; + int slaveid; + uint32_t response_timeout_ms; + char tcp_host[256]; + char tcp_port[6]; + + val = getval("porttype"); + + if (val == NULL) { +#if defined NUT_MODBUS_HAS_USB + val = "usb"; +#else + val = "serial"; +#endif /* defined NUT_MODBUS_HAS_USB */ + } + + is_open = 0; + +#if defined NUT_MODBUS_HAS_USB + is_usb = 0; +#endif /* defined NUT_MODBUS_HAS_USB */ + + if (!strcasecmp(val, "usb")) { +#if defined NUT_MODBUS_HAS_USB + is_usb = 1; + + /* We default to USB, this is the most common connection type and does not require additional + * parameter, so we can auto-detect these. */ + + warn_if_bad_usb_port_filename(device_path); + + regex_array[0] = getval("vendorid"); + regex_array[1] = getval("productid"); + regex_array[2] = getval("vendor"); + regex_array[3] = getval("product"); + regex_array[4] = getval("serial"); + regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); +# if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +# else + if (getval("busport")) { + upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)"); + } +# endif + + has_nonzero_regex = 0; + for (i = 0; i < SIZEOF_ARRAY(regex_array); i++) { + if (regex_array[i] != NULL) { + has_nonzero_regex = 1; + break; + } + } + + best_matcher = &apc_modbus_usb_device_matcher; + + if (has_nonzero_regex > 0) { + r = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); + if (r < 0) { + fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); + } else if (r) { + fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[r]); + } + + regex_matcher->next = best_matcher; + best_matcher = regex_matcher; + } + + modbus_ctx = modbus_new_rtu_usb(MODBUS_USB_MODE_APC, _apc_modbus_usb_callback); +#else + fatalx(EXIT_FAILURE, "driver was not compiled with USB support"); +#endif /* defined NUT_MODBUS_HAS_USB */ + } else if (!strcasecmp(val, "tcp")) { + if (!_apc_modbus_parse_host_port(device_path, tcp_host, sizeof(tcp_host), tcp_port, sizeof(tcp_port), modbus_tcp_default_port)) { + fatalx(EXIT_FAILURE, "failed to parse host/port"); + } + + modbus_ctx = modbus_new_tcp_pi(tcp_host, tcp_port); + } else if (!strcasecmp(val, "serial")) { + val = getval("baudrate"); + rtu_baudrate = val ? atoi(val) : modbus_rtu_default_baudrate; + + val = getval("parity"); + rtu_parity = val ? (char)toupper(val[0]) : modbus_rtu_default_parity; + + val = getval("databits"); + rtu_databits = val ? atoi(val) : modbus_rtu_default_databits; + + val = getval("stopbits"); + rtu_stopbits = val ? atoi(val) : modbus_rtu_default_stopbits; + + modbus_ctx = modbus_new_rtu(device_path, rtu_baudrate, rtu_parity, rtu_databits, rtu_stopbits); + } + + if (modbus_ctx == NULL) { + fatalx(EXIT_FAILURE, "Unable to create the libmodbus context: %s", modbus_strerror(errno)); + } + + if (nut_debug_level > 5) { + modbus_set_debug(modbus_ctx, 1); + } + + val = getval("slaveid"); + slaveid = val ? atoi(val) : modbus_default_slave_id; + r = modbus_set_slave(modbus_ctx, slaveid); + if (r < 0) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: invalid slave id %d", slaveid); + } + + val = getval("response_timeout_ms"); + if (val != NULL) { + response_timeout_ms = (uint32_t)strtoul(val, NULL, 0); + +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + r = modbus_set_response_timeout(modbus_ctx, response_timeout_ms / 1000, (response_timeout_ms % 1000) * 1000); + if (r < 0) { + modbus_free(modbus_ctx); + 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 = response_timeout_ms / 1000; + to.tv_usec = (response_timeout_ms % 1000) * 1000; + /* void */ modbus_set_response_timeout(modbus_ctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + } + + if (modbus_connect(modbus_ctx) == -1) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + +#if defined NUT_MODBUS_HAS_USB + /* This creates an exact matcher after the first connection so that on + * reconnect we are more likely to match the exact device we connected to + * the first time. */ + _apc_modbus_create_reopen_matcher(); +#endif /* defined NUT_MODBUS_HAS_USB */ + + modbus_flush(modbus_ctx); + + is_open = 1; +} + + +void upsdrv_cleanup(void) +{ + _apc_modbus_close(1); + +#if defined NUT_MODBUS_HAS_USB + USBFreeExactMatcher(reopen_matcher); + USBFreeExactMatcher(regex_matcher); +#endif /* defined NUT_MODBUS_HAS_USB */ +} diff --git a/drivers/apc_modbus.h b/drivers/apc_modbus.h new file mode 100644 index 0000000000..d175eb4974 --- /dev/null +++ b/drivers/apc_modbus.h @@ -0,0 +1,91 @@ +/* apc_modbus.h - Driver for APC Modbus UPS + * Copyright Ā© 2023 Axel Gembe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef APC_MODBUS_H +#define APC_MODBUS_H + +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_PENDING (1 << 0) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_INPROGRESS (1 << 1) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_PASSED (1 << 2) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_FAILED (1 << 3) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_REFUSED (1 << 4) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_ABORTED (1 << 5) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_PROTOCOL (1 << 6) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_LOCALUI (1 << 7) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_SOURCE_INTERNAL (1 << 8) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_INVALIDSTATE (1 << 9) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_INTERNALFAULT (1 << 10) +#define APC_MODBUS_REPLACEBATTERYTESTSTATUS_BF_MOD_STATEOFCHARGENOTACCEPTABLE (1 << 11) + +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_REG 590 +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_MOG_PRESENT (1 << 0) +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_0_PRESENT (1 << 1) +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_1_PRESENT (1 << 2) +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_2_PRESENT (1 << 3) +#define APC_MODBUS_SOGRELAYCONFIGSETTING_BF_SOG_3_PRESENT (1 << 4) + +#define APC_MODBUS_UPSCOMMAND_BF_REG 1536 +/* 0 - 2 are reserved */ +#define APC_MODBUS_UPSCOMMAND_BF_RESTORE_FACTORY_SETTINGS (1 << 3) +#define APC_MODBUS_UPSCOMMAND_BF_OUTPUT_INTO_BYPASS (1 << 4) +#define APC_MODBUS_UPSCOMMAND_BF_OUTPUT_OUT_OF_BYPASS (1 << 5) +/* 6 - 8 are reserved */ +#define APC_MODBUS_UPSCOMMAND_BF_CLEAR_FAULTS (1 << 9) +/* 10 - 12 are reserved */ +#define APC_MODBUS_UPSCOMMAND_BF_RESET_STRINGS (1 << 13) +#define APC_MODBUS_UPSCOMMAND_BF_RESET_LOGS (1 << 14) + +#define APC_MODBUS_OUTLETCOMMAND_BF_REG 1538 +#define APC_MODBUS_OUTLETCOMMAND_BF_CMD_CANCEL (1 << 0) +#define APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_ON (1 << 1) +#define APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_OFF (1 << 2) +#define APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_SHUTDOWN (1 << 3) +#define APC_MODBUS_OUTLETCOMMAND_BF_CMD_OUTPUT_REBOOT (1 << 4) +#define APC_MODBUS_OUTLETCOMMAND_BF_MOD_COLD_BOOT_ALLOWED (1 << 5) +#define APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_ON_DELAY (1 << 6) +#define APC_MODBUS_OUTLETCOMMAND_BF_MOD_USE_OFF_DELAY (1 << 7) +#define APC_MODBUS_OUTLETCOMMAND_BF_TARGET_MAIN_OUTLET_GROUP (1 << 8) +#define APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_0 (1 << 9) +#define APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_1 (1 << 10) +#define APC_MODBUS_OUTLETCOMMAND_BF_TARGET_SWITCHED_OUTLET_GROUP_2 (1 << 11) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_USB_PORT (1 << 12) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_LOCAL_USER (1 << 13) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_RJ45_PORT (1 << 14) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_SMART_SLOT_1 (1 << 15) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_SMART_SLOT_2 (1 << 16) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_INTERNAL_NETWORK_1 (1 << 17) +#define APC_MODBUS_OUTLETCOMMAND_BF_SOURCE_INTERNAL_NETWORK_2 (1 << 18) + +#define APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_REG 1541 +#define APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_START (1 << 0) +#define APC_MODBUS_REPLACEBATTERYTESTCOMMAND_BF_ABORT (1 << 1) + +#define APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_REG 1542 +#define APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_START (1 << 0) +#define APC_MODBUS_RUNTIMECALIBRATIONCOMMAND_BF_ABORT (1 << 1) + +#define APC_MODBUS_USERINTERFACECOMMAND_BF_REG 1543 +#define APC_MODBUS_USERINTERFACECOMMAND_BF_SHORT_TEST (1 << 0) +#define APC_MODBUS_USERINTERFACECOMMAND_BF_CONTINUOUS_TEST (1 << 1) +#define APC_MODBUS_USERINTERFACECOMMAND_BF_MUTE_ALL_ACTIVE_AUDIBLE_ALARMS (1 << 2) +#define APC_MODBUS_USERINTERFACECOMMAND_BF_CANCEL_MUTE (1 << 3) +/* 4 is reserved */ +#define APC_MODBUS_USERINTERFACECOMMAND_BF_ACKNOWLEDGE_BATTERY_ALARMS (1 << 5) +#define APC_MODBUS_USERINTERFACECOMMAND_BF_ACKNOWLEDGE_SITE_WIRING_ALARM (1 << 6) + +#endif /* APC_MODBUS_H */ diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 34a82a56ec..9771e0e52a 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -24,8 +24,8 @@ #include "apcsmart-old.h" #include "nut_stdint.h" -#define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "2.2" +#define DRIVER_NAME "APC Smart protocol driver (old)" +#define DRIVER_VERSION "2.33" static upsdrv_info_t table_info = { "APC command table", @@ -373,32 +373,42 @@ static void do_capabilities(void) /* check for idiocy */ if (ptr >= endtemp) { - /* if we expected this, just ignore it */ if (quirk_capability_overflow) return; fatalx(EXIT_FAILURE, "Capability string has overflowed\n" - "Please report this error\n" + "Please report this error with device details\n" "ERROR: capability overflow!" ); } + entptr = &ptr[4]; cmd = ptr[0]; loc = ptr[1]; + if (ptr[2] < 48 || ptr[3] < 48) { - upsdebugx(0, - "%s: nument (%d) or entlen (%d) out of range", - __func__, (ptr[2] - 48), (ptr[3] - 48)); - fatalx(EXIT_FAILURE, - "nument or entlen out of range\n" - "Please report this error\n" - "ERROR: capability overflow!"); + upsdebugx(3, + "%s: SKIP: nument (%d) or entlen (%d) " + "out of range for cmd %d at loc %d", + __func__, (ptr[2] - 48), (ptr[3] - 48), + cmd, loc); + + /* just ignore it as we did for ages see e.g. v2.7.4 + * (note the next loop cycle was and still would be + * no-op anyway, if "nument <= 0"). + */ + nument = 0; + entlen = 0; + + /* NOT a full skip: Gotta handle "vt" to act like before */ + /*ptr = entptr;*/ + /*continue;*/ + } else { + nument = (size_t)ptr[2] - 48; + entlen = (size_t)ptr[3] - 48; } - nument = (size_t)ptr[2] - 48; - entlen = (size_t)ptr[3] - 48; - entptr = &ptr[4]; vt = vartab_lookup_char(cmd); valid = vt && ((loc == upsloc) || (loc == '4')); @@ -536,7 +546,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 @@ -1312,7 +1322,7 @@ static int setvar(const char *varname, const char *val) if ((vt->flags & APC_RW) == 0) { upslogx(LOG_WARNING, "setvar: [%s] is not writable", varname); - return STAT_SET_UNKNOWN; + return STAT_SET_INVALID; } if (vt->flags & APC_ENUM) diff --git a/drivers/apcsmart-old.h b/drivers/apcsmart-old.h index 6c24da7e4f..d3ef612edf 100644 --- a/drivers/apcsmart-old.h +++ b/drivers/apcsmart-old.h @@ -22,7 +22,9 @@ #define NUT_APCSMART_OLD_H_SEEN 1 #include +#ifndef WIN32 #include +#endif #include "serial.h" #include "timehead.h" diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 0122c924b3..97b488bc46 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -35,6 +35,15 @@ #include "apcsmart.h" #include "apcsmart_tabs.h" +#define DRIVER_NAME "APC Smart protocol driver" +#define DRIVER_VERSION "3.32" + +#ifdef WIN32 +# ifndef ECANCELED +# define ECANCELED ERROR_CANCELLED +# endif +#endif + /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, @@ -111,7 +120,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; } @@ -251,7 +260,7 @@ static void apc_ser_diff(struct termios *tioset, struct termios *tioget) /* clear status flags so that they don't affect our binary compare */ #if defined(PENDIN) || defined(FLUSHO) - for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { + for (i = 0; i < SIZEOF_ARRAY(tio); i++) { #ifdef PENDIN tio[i]->c_lflag &= ~(unsigned int)PENDIN; #endif @@ -274,7 +283,7 @@ static void apc_ser_diff(struct termios *tioset, struct termios *tioget) * rate of 2400. */ - for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { + for (i = 0; i < SIZEOF_ARRAY(tio); i++) { upsdebugx(1, "tc%cetattr(): gfmt1:cflag=%x:iflag=%x:lflag=%x:oflag=%x:", dir[i], (unsigned int) tio[i]->c_cflag, (unsigned int) tio[i]->c_iflag, (unsigned int) tio[i]->c_lflag, (unsigned int) tio[i]->c_oflag); @@ -448,8 +457,9 @@ static ssize_t apc_read_i(char *buf, size_t buflen, int flags, const char *fn, u fatalx (EXIT_FAILURE, "Error: apc_read_i called with buflen too large"); } - if (upsfd == -1) + if (INVALID_FD(upsfd)) return 0; + if (flags & SER_D0) { sec = 0; usec = 0; } @@ -574,7 +584,7 @@ static ssize_t apc_write_i(unsigned char code, const char *fn, unsigned int ln) ssize_t ret; errno = 0; - if (upsfd == -1) + if (INVALID_FD(upsfd)) return 0; ret = ser_send_char(upsfd, code); @@ -1014,25 +1024,36 @@ static void apc_getcaps(int qco) /* if we expected this, just ignore it */ if (qco) return; - fatalx(EXIT_FAILURE, "capability string has overflowed, please report this error !"); + fatalx(EXIT_FAILURE, + "capability string has overflowed, " + "please report this error with device details!"); } + entptr = &ptr[4]; cmd = ptr[0]; loc = ptr[1]; if (ptr[2] < 48 || ptr[3] < 48) { - upsdebugx(0, - "%s: nument (%d) or entlen (%d) out of range", - __func__, (ptr[2] - 48), (ptr[3] - 48)); - fatalx(EXIT_FAILURE, - "nument or entlen out of range\n" - "Please report this error\n" - "ERROR: capability overflow!"); - } + upsdebugx(3, + "%s: SKIP: nument (%d) or entlen (%d) " + "out of range for cmd %d at loc %d", + __func__, (ptr[2] - 48), (ptr[3] - 48), + cmd, loc); + + /* just ignore it as we did for ages see e.g. v2.7.4 + * (note the next loop cycle was and still would be + * no-op anyway, if "nument <= 0"). + */ + nument = 0; + entlen = 0; - nument = (size_t)ptr[2] - 48; - entlen = (size_t)ptr[3] - 48; - entptr = &ptr[4]; + /* NOT a full skip: Gotta handle "vt" to act like before */ + /*ptr = entptr;*/ + /*continue;*/ + } else { + nument = (size_t)ptr[2] - 48; + entlen = (size_t)ptr[3] - 48; + } vt = vt_lookup_char(cmd); valid = vt && ((loc == upsloc) || (loc == '4')) && !(vt->flags & APC_PACK); @@ -1550,7 +1571,7 @@ static int sdcmd_AT(const void *str) /* Range-check: padto is 2 or 3 per above */ if (ret != (ssize_t)padto + 1) { upslogx(LOG_ERR, - "issuing [%s] with %zu digits failed", + "issuing [%s] with %" PRIuSIZE " digits failed", prtchr(APC_CMD_GRACEDOWN), padto); return STAT_INSTCMD_FAILED; } @@ -1712,7 +1733,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 +2061,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); } @@ -2075,7 +2096,7 @@ void upsdrv_help(void) printf( "\nFor detailed information, please refer to:\n" " - apcsmart(8)\n" - " - http://www.networkupstools.org/docs/man/apcsmart.html\n" + " - https://www.networkupstools.org/docs/man/apcsmart.html\n" ); } @@ -2116,7 +2137,7 @@ void upsdrv_cleanup(void) { char temp[APC_LBUF]; - if (upsfd == -1) + if (INVALID_FD(upsfd)) return; apc_flush(0); diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index fe7bd1ad29..5a60f83eb5 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -23,9 +23,6 @@ #ifndef NUT_APCSMART_H_SEEN #define NUT_APCSMART_H_SEEN 1 -#define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "3.2" - #define ALT_CABLE_1 "940-0095B" /* @@ -70,6 +67,10 @@ * we have to ignore this character explicitly */ +#ifndef WIN32 +#include +#endif + /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF (and CR, but it's IGNCRed) */ diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c index 29d20dd331..82bc3af631 100644 --- a/drivers/apcupsd-ups.c +++ b/drivers/apcupsd-ups.c @@ -18,18 +18,46 @@ #include "config.h" +#ifndef WIN32 #include #include #include -#include #include +#else +#include "wincompat.h" +#endif + +#ifdef HAVE_POLL_H +# include /* nfds_t */ +#else +typedef unsigned long int nfds_t; + +# ifndef POLLRDNORM +# define POLLRDNORM 0x0100 +# endif +# ifndef POLLRDBAND +# define POLLRDBAND 0x0200 +# endif +# ifndef POLLIN +# define POLLIN (POLLRDNORM | POLLRDBAND) +# endif +# if ! HAVE_STRUCT_POLLFD +typedef struct pollfd { + SOCKET fd; + short events; + short revents; +} pollfd_t; +# define HAVE_STRUCT_POLLFD 1 +# endif +#endif /* !HAVE_POLL_H */ #include "main.h" #include "apcupsd-ups.h" #include "attribute.h" +#include "nut_stdint.h" #define DRIVER_NAME "apcupsd network client UPS driver" -#define DRIVER_VERSION "0.6" +#define DRIVER_VERSION "0.71" #define POLL_INTERVAL_MIN 10 @@ -159,38 +187,72 @@ static void process(char *item,char *data) static int getdata(void) { ssize_t x; - int fd_flags; uint16_t n; char *item; char *data; struct pollfd p; char bfr[1024]; + st_tree_timespec_t start; + int ret = -1; +#ifndef WIN32 + int fd_flags; +#else + /* Note: while the code below uses "pollfd" for simplicity as it is + * available in mingw headers (although poll() method usually is not), + * WIN32 builds use WaitForMultipleObjects(); see also similar code + * in upsd.c for networking. + */ + HANDLE event = NULL; +#endif - for(x=0;nut_data[x].info_type;x++) - if(!(nut_data[x].drv_flags & DU_FLAG_INIT) && !(nut_data[x].drv_flags & DU_FLAG_PRESERVE)) - dstate_delinfo(nut_data[x].info_type); + state_get_timestamp((st_tree_timespec_t *)&start); - if((p.fd=socket(AF_INET,SOCK_STREAM,0))==-1) + if (INVALID_FD_SOCK( (p.fd = socket(AF_INET, SOCK_STREAM, 0)) )) { upsdebugx(1,"socket error"); - return -1; + /* return -1; */ + ret = -1; + goto getdata_return; } if(connect(p.fd,(struct sockaddr *)&host,sizeof(host))) { upsdebugx(1,"can't connect to apcupsd"); - close(p.fd); - return -1; + /* close(p.fd); + return -1; */ + ret = -1; + goto getdata_return; } +#ifndef WIN32 + /* WSAEventSelect automatically sets the socket to nonblocking mode */ fd_flags = fcntl(p.fd, F_GETFL); + if (fd_flags == -1) { + upsdebugx(1,"unexpected fcntl(fd, F_GETFL) failure"); + /* close(p.fd); + return -1; */ + ret = -1; + goto getdata_return; + } fd_flags |= O_NONBLOCK; - if(fcntl(p.fd, F_SETFL, fd_flags)) + if(fcntl(p.fd, F_SETFL, fd_flags) == -1) { upsdebugx(1,"unexpected fcntl(fd, F_SETFL, fd_flags|O_NONBLOCK) failure"); - close(p.fd); - return -1; + /* close(p.fd); + return -1; */ + ret = -1; + goto getdata_return; } +#else + event = CreateEvent( + NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( p.fd, event, FD_CONNECT ); +#endif p.events=POLLIN; @@ -199,19 +261,23 @@ static int getdata(void) x=write(p.fd,"status",6); /* TODO: double-check for poll() in configure script */ +#ifndef WIN32 while(poll(&p,1,15000)==1) +#else + while (WaitForMultipleObjects(1, &event, FALSE, 15000) == WAIT_TIMEOUT) +#endif { if(read(p.fd,&n,2)!=2) { upsdebugx(1,"apcupsd communication error"); - close(p.fd); - return -1; + ret = -1; + goto getdata_return; } if(!(x=ntohs(n))) { - close(p.fd); - return 0; + ret = 0; + goto getdata_return; } else if(x<0||x>=(int)sizeof(bfr)) /* Note: LGTM.com suggests "Comparison is always false because x >= 0" @@ -222,17 +288,21 @@ static int getdata(void) */ { upsdebugx(1,"apcupsd communication error"); - close(p.fd); - return -1; + ret = -1; + goto getdata_return; } +#ifndef WIN32 if(poll(&p,1,15000)!=1)break; +#else + if (WaitForMultipleObjects(1, &event, FALSE, 15000) != WAIT_OBJECT_0) break; +#endif if(read(p.fd,bfr,(size_t)x)!=x) { upsdebugx(1,"apcupsd communication error"); - close(p.fd); - return -1; + ret = -1; + goto getdata_return; } bfr[x]=0; @@ -240,15 +310,15 @@ static int getdata(void) if(!(item=strtok(bfr," \t:\r\n"))) { upsdebugx(1,"apcupsd communication error"); - close(p.fd); - return -1; + ret = -1; + goto getdata_return; } if(!(data=strtok(NULL,"\r\n"))) { upsdebugx(1,"apcupsd communication error"); - close(p.fd); - return -1; + ret = -1; + goto getdata_return; } while(*data==' '||*data=='\t'||*data==':')data++; @@ -256,8 +326,22 @@ static int getdata(void) } upsdebugx(1,"unexpected connection close by apcupsd"); - close(p.fd); - return -1; + ret = -1; + +getdata_return: + if (VALID_FD_SOCK(p.fd)) + close(p.fd); +#ifdef WIN32 + if (event != NULL) + CloseHandle(event); +#endif + + /* Remove any unprotected entries not refreshed in this run */ + for(x=0;nut_data[x].info_type;x++) + if(!(nut_data[x].drv_flags & DU_FLAG_INIT) && !(nut_data[x].drv_flags & DU_FLAG_PRESERVE)) + dstate_delinfo_olderthan(nut_data[x].info_type, &start); + + return ret; } void upsdrv_initinfo(void) @@ -266,7 +350,7 @@ void upsdrv_initinfo(void) if(getdata())fatalx(EXIT_FAILURE,"can't communicate with apcupsd!"); else dstate_dataok(); - poll_interval = (poll_interval > POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; + poll_interval = (poll_interval < POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; } void upsdrv_updateinfo(void) @@ -274,15 +358,14 @@ void upsdrv_updateinfo(void) if(getdata())upslogx(LOG_ERR,"can't communicate with apcupsd!"); else dstate_dataok(); - poll_interval = (poll_interval > POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; + poll_interval = (poll_interval < POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } void upsdrv_help(void) @@ -298,6 +381,12 @@ void upsdrv_initups(void) char *p; struct hostent *h; +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + if(device_path&&*device_path) { /* TODO: fix parsing since bare IPv6 addresses contain colons */ diff --git a/drivers/arduino-hid.c b/drivers/arduino-hid.c index 5b74c12802..c63fff7573 100644 --- a/drivers/arduino-hid.c +++ b/drivers/arduino-hid.c @@ -1,4 +1,7 @@ /* arduino-hid.c - subdriver to monitor Arduino USB/HID devices with NUT + * + * This was written assuming it would be used to communicate with code + * using this Arduino library: https://github.com/abratchik/HIDPowerDevice * * Copyright (C) * 2003 - 2012 Arnaud Quette @@ -6,6 +9,7 @@ * 2008 - 2009 Arjen de Korte * 2013 Charles Lepple * 2021 Alex Bratchik + * 2023 Kelly Byrd * * Note: this subdriver was initially generated as a "stub" by the * gen-usbhid-subdriver script. It must be customized. @@ -32,7 +36,7 @@ #include "main.h" /* for getval() */ #include "usb-common.h" -#define ARDUINO_HID_VERSION "Arduino HID 0.2" +#define ARDUINO_HID_VERSION "Arduino HID 0.21" /* FIXME: experimental flag to be put in upsdrv_info */ /* Arduino */ @@ -90,6 +94,37 @@ static hid_info_t arduino_hid2nut[] = { { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, + /* Battery */ + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion}, + /* In the sample for the Arduino library, this isn't a date */ + /*{ "battery.mfr.date", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion},*/ + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", HU_FLAG_QUICK_POLL, NULL}, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", HU_FLAG_QUICK_POLL, NULL}, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + { "battery.runtime.low", 0, 0, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* Parse `*Capacity` values assuming `UPS.PowerSummary.CapacityMode` is set to 2, which means the `*Capacity` + units are percent. The Arduino HID Powerdevice library is capable of doing other modes, if the Sketch wants + to use them, but it appears most drivers just assume percent. + */ + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + + /* USB HID PresentStatus Flags + TODO: Parse these into battery.charger.status + */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, HU_FLAG_QUICK_POLL, fullycharged_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyDischarged", NULL, NULL, HU_FLAG_QUICK_POLL, depleted_info }, + /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; @@ -116,18 +151,24 @@ static int arduino_claim(HIDDevice_t *hd) case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { - usb->hid_ep_in=4; - usb->hid_ep_out=5; - usb->hid_rep_index = 2; + if (!getval("usb_hid_ep_in")) + usb->hid_ep_in=4; + if (!getval("usb_hid_ep_out")) + usb->hid_ep_out=5; + if (!getval("usb_hid_rep_index")) + usb->hid_rep_index = 2; return 1; } possibly_supported("Arduino", hd); return 0; case SUPPORTED: - usb->hid_ep_in=4; - usb->hid_ep_out=5; - usb->hid_rep_index = 2; + if (!getval("usb_hid_ep_in")) + usb->hid_ep_in=4; + if (!getval("usb_hid_ep_out")) + usb->hid_ep_out=5; + if (!getval("usb_hid_rep_index")) + usb->hid_rep_index = 2; return 1; case NOT_SUPPORTED: diff --git a/drivers/asem.c b/drivers/asem.c index 7b0af43476..d35a54746a 100644 --- a/drivers/asem.c +++ b/drivers/asem.c @@ -67,7 +67,7 @@ #endif #define DRIVER_NAME "ASEM" -#define DRIVER_VERSION "0.11" +#define DRIVER_VERSION "0.12" /* Valid on ASEM PB1300 UPS */ #define BQ2060_ADDRESS 0x0B @@ -324,9 +324,6 @@ void upsdrv_updateinfo(void) dstate_dataok(); } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -335,7 +332,8 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ diff --git a/drivers/baytech-mib.c b/drivers/baytech-mib.c index ad6aae5ff5..b9c56aba19 100644 --- a/drivers/baytech-mib.c +++ b/drivers/baytech-mib.c @@ -24,89 +24,79 @@ #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.4035" /* Baytech MIB */ #define BAYTECH_OID_MIB ".1.3.6.1.4.1.4779" #define BAYTECH_OID_MODEL_NAME ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1" static info_lkp_t baytech_outlet_status_info[] = { - { -1, "error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "cycling" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "error"), + info_lkp_default(0, "off"), + info_lkp_default(1, "on"), + info_lkp_default(2, "cycling"), /* transitional status, "reboot" in MIB comments */ + info_lkp_default(3, "lockon"), + info_lkp_default(4, "lockoff"), + info_lkp_default(5, "unlock"), + info_lkp_default(6, "unknown"), + info_lkp_sentinel }; /* Snmp2NUT lookup table for BayTech MIBs */ static snmp_info_t baytech_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "BayTech", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Baytech", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.3.0", - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.1.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.10.2.1", NULL, 0, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Baytech", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.3.0", + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.temperature", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.10.2.1", NULL, 0, NULL), /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.2.1.15.1", "0", 0, NULL }, - { "outlet.current", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.6.2.1", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.8.2.1", NULL, 0, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.2.1.15.1", "0", 0, NULL), + snmp_info_default("outlet.current", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.6.2.1", NULL, 0, NULL), + snmp_info_default("outlet.voltage", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.8.2.1", NULL, 0, NULL), /* outlet template definition */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_OUTLET, &baytech_outlet_status_info[0] }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.4.1.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.6.1.3.2.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET | SU_FLAG_OK, NULL }, - { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_OUTLET, &baytech_outlet_status_info[0]), + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.4.1.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.6.1.3.2.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET | SU_FLAG_OK, NULL), + snmp_info_default("outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL), /* instant commands. */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t baytech = { "baytech", BAYTECH_MIB_VERSION, NULL, BAYTECH_OID_MODEL_NAME, baytech_mib, BAYTECH_OID_MIB, NULL }; diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index e0ea90499b..fd6676ff9a 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -118,7 +118,7 @@ TODO List: #include "bcmxcp.h" #define DRIVER_NAME "BCMXCP UPS driver" -#define DRIVER_VERSION "0.32" +#define DRIVER_VERSION "0.33" #define MAX_NUT_NAME_LENGTH 128 #define NUT_OUTLET_POSITION 7 @@ -389,7 +389,7 @@ unsigned char calc_checksum(const unsigned char *buf) return c; } -void init_command_map() +void init_command_map(void) { int i = 0; @@ -442,7 +442,7 @@ void init_command_map() } } -void init_meter_map() +void init_meter_map(void) { /* Clean entire map */ memset(&bcmxcp_meter_map, 0, sizeof(BCMXCP_METER_MAP_ENTRY_t) * BCMXCP_METER_MAP_MAX); @@ -519,7 +519,7 @@ void init_meter_map() bcmxcp_meter_map[BCMXCP_METER_MAP_LINE_EVENT_COUNTER].nut_entity = "input.quality"; } -void init_alarm_map() +void init_alarm_map(void) { /* Clean entire map */ memset(&bcmxcp_alarm_map, 0, sizeof(BCMXCP_ALARM_MAP_ENTRY_t) * BCMXCP_ALARM_MAP_MAX); @@ -794,11 +794,11 @@ bool_t init_command(int size) res = answer[iIndex]; NumComms = (int)res; /* Number of commands implemented in this UPS */ - upsdebugx(3, "Number of commands implemented in ups %zd", res); + upsdebugx(3, "Number of commands implemented in ups %" PRIiSIZE, res); iIndex++; res = answer[iIndex]; /* Entry length - bytes reported for each command */ iIndex++; - upsdebugx(5, "bytes per command %zd", res); + upsdebugx(5, "bytes per command %" PRIiSIZE, res); /* In case of debug - make explanation of values */ upsdebugx(2, "Index\tCmd byte\tDescription"); @@ -1043,7 +1043,7 @@ unsigned char init_outlet(unsigned char len) if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); else - upsdebugx(1, "init_outlet(%i), res=%zi", len, res); + upsdebugx(1, "init_outlet(%i), res=%" PRIiSIZE, len, res); num_outlet = answer[iIndex++]; upsdebugx(2, "Number of outlets: %u", num_outlet); @@ -1564,11 +1564,11 @@ void upsdrv_initinfo(void) len = init_outlet((unsigned char)outlet_block_len /* arg ignored */); for (res = 1 ; (unsigned int)res <= (unsigned int)len ; res++) { - snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.shutdown.return", res); + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%" PRIiSIZE ".shutdown.return", res); dstate_addcmd(outlet_name); - snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.load.on", res); + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%" PRIiSIZE ".load.on", res); dstate_addcmd(outlet_name); - snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.load.off", res); + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%" PRIiSIZE ".load.off", res); dstate_addcmd(outlet_name); } } @@ -1962,7 +1962,8 @@ void upsdrv_shutdown(void) return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } diff --git a/drivers/bcmxcp.h b/drivers/bcmxcp.h index 7050d1c0af..054dce2466 100644 --- a/drivers/bcmxcp.h +++ b/drivers/bcmxcp.h @@ -2,8 +2,8 @@ * bcmxcp.h -- header for BCM/XCP module */ -#ifndef _POWERWARE_H -#define _POWERWARE_H +#ifndef NUT_BCMXCP_H_SEEN +#define NUT_BCMXCP_H_SEEN 1 #include "timehead.h" @@ -622,5 +622,4 @@ typedef enum ebool { FALSE, TRUE } bool_t; typedef int bool_t; #endif -#endif /*_POWERWARE_H */ - +#endif /* NUT_BCMXCP_H_SEEN */ diff --git a/drivers/bcmxcp_ser.c b/drivers/bcmxcp_ser.c index 58595f390b..c11f987e84 100644 --- a/drivers/bcmxcp_ser.c +++ b/drivers/bcmxcp_ser.c @@ -6,7 +6,7 @@ #include "nut_stdint.h" #define SUBDRIVER_NAME "RS-232 communication subdriver" -#define SUBDRIVER_VERSION "0.21" +#define SUBDRIVER_VERSION "0.22" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -102,7 +102,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) if (res != 1) { upsdebugx(1, - "Receive error (PW_COMMAND_START_BYTE): %zd, cmd=%x!!!\n", + "Receive error (PW_COMMAND_START_BYTE): %" PRIiSIZE ", cmd=%x!!!\n", res, command); return -1; } @@ -120,7 +120,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) res = ser_get_char(upsfd, my_buf + 1, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (Block number): %zd!!!\n", res); + ser_comm_fail("Receive error (Block number): %" PRIiSIZE "!!!\n", res); return -1; } @@ -149,14 +149,14 @@ ssize_t get_answer(unsigned char *data, unsigned char command) res = ser_get_char(upsfd, my_buf + 2, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (length): %zd!!!\n", res); + ser_comm_fail("Receive error (length): %" PRIiSIZE "!!!\n", res); return -1; } length = (unsigned char)my_buf[2]; if (length < 1) { - ser_comm_fail("Receive error (length): packet length %zx!!!\n", length); + ser_comm_fail("Receive error (length): packet length %" PRIxSIZE "!!!\n", length); return -1; } @@ -164,7 +164,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) res = ser_get_char(upsfd, my_buf + 3, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (sequence): %zd!!!\n", res); + ser_comm_fail("Receive error (sequence): %" PRIiSIZE "!!!\n", res); return -1; } @@ -184,12 +184,12 @@ ssize_t get_answer(unsigned char *data, unsigned char command) /* Try to read all the remaining bytes */ res = ser_get_buf_len(upsfd, my_buf + 4, length, 1, 0); if (res < 0) { - ser_comm_fail("%s(): ser_get_buf_len() returned error code %zd", __func__, res); + ser_comm_fail("%s(): ser_get_buf_len() returned error code %" PRIiSIZE, __func__, res); return res; } if ((size_t)res != length) { - ser_comm_fail("Receive error (data): got %zd bytes instead of %zu!!!\n", res, length); + ser_comm_fail("Receive error (data): got %" PRIiSIZE " bytes instead of %" PRIuSIZE "!!!\n", res, length); return -1; } @@ -197,7 +197,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) res = ser_get_char(upsfd, my_buf + (4 + length), 1, 0); if (res != 1) { - ser_comm_fail("Receive error (checksum): %zx!!!\n", res); + ser_comm_fail("Receive error (checksum): %" PRIxSIZE "!!!\n", res); return -1; } @@ -270,7 +270,7 @@ ssize_t command_write_sequence(unsigned char *command, size_t command_length, un return bytes_read; } -void upsdrv_comm_good() +void upsdrv_comm_good(void) { ser_comm_good(); } @@ -397,11 +397,11 @@ static void pw_comm_setup(const char *port) } if (ret > 0) { - upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %zu", port, pw_baud_rates[i].name); + upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %" PRIuSIZE, port, pw_baud_rates[i].name); return; } - upsdebugx(2, "No response from UPS on %s with baudrate %zu", port, pw_baud_rates[i].name); + upsdebugx(2, "No response from UPS on %s with baudrate %" PRIuSIZE, port, pw_baud_rates[i].name); } fatalx(EXIT_FAILURE, "Can't connect to the UPS on port %s!\n", port); diff --git a/drivers/bcmxcp_usb.c b/drivers/bcmxcp_usb.c index 49cf8ade58..9b66aef8e0 100644 --- a/drivers/bcmxcp_usb.c +++ b/drivers/bcmxcp_usb.c @@ -23,6 +23,7 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_TRY 4 +#define MAX_TRY_OPENUSB 32 /* Powerware */ #define POWERWARE 0x0592 @@ -243,7 +244,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) /* Check data length byte (remove the header length) */ length = my_buf[2]; - upsdebugx(3, "get_answer: data length = %zu", length); + upsdebugx(3, "get_answer: data length = %" PRIuSIZE, length); if (bytes_read < (length + PW_HEADER_SIZE)) { if (need_data < 0) --need_data; /* count zerro byte too */ need_data += length + 1; /* packet lenght + checksum */ @@ -305,7 +306,7 @@ ssize_t get_answer(unsigned char *data, unsigned char command) else if (tail == 0) my_buf = &buf[0]; else { /* if (tail < 0) */ - upsdebugx(1, "get_answer(): did not expect to get negative tail size: %zd", tail); + upsdebugx(1, "get_answer(): did not expect to get negative tail size: %" PRIiSIZE, tail); return -1; } @@ -384,13 +385,18 @@ void upsdrv_cleanup(void) void upsdrv_reconnect(void) { + dstate_setinfo("driver.state", "reconnect.trying"); + upsdebugx(4, "=================================================="); upsdebugx(4, "= device has been disconnected, try to reconnect ="); upsdebugx(4, "=================================================="); nutusb_close(upsdev, "USB"); upsdev = NULL; + upsdrv_initups(); + + dstate_setinfo("driver.state", "quiet"); } /* USB functions */ @@ -417,7 +423,8 @@ static usb_dev_handle *open_powerware_usb(void) ssize_t devcount = 0; libusb_device_handle *udev; struct libusb_device_descriptor dev_desc; - uint8_t bus; + uint8_t bus_num; + /* TODO: consider device_addr */ int i; devcount = libusb_get_device_list(NULL, &devlist); @@ -435,13 +442,13 @@ static usb_dev_handle *open_powerware_usb(void) curDevice.VendorID = dev_desc.idVendor; curDevice.ProductID = dev_desc.idProduct; - bus = libusb_get_bus_number(device); + bus_num = libusb_get_bus_number(device); curDevice.Bus = (char *)malloc(4); if (curDevice.Bus == NULL) { libusb_free_device_list(devlist, 1); fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - sprintf(curDevice.Bus, "%03d", bus); + sprintf(curDevice.Bus, "%03d", bus_num); /* FIXME: we should also retrieve * dev->descriptor.iManufacturer @@ -499,6 +506,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 @@ -512,6 +520,7 @@ usb_dev_handle *nutusb_open(const char *port) usb_find_devices(); #endif /* WITH_LIBUSB_1_0 */ + /* for (retry = 0; dev_h == NULL && retry < MAX_TRY_OPENUSB; retry++) */ for (retry = 0; retry < MAX_TRY ; retry++) { dev_h = open_powerware_usb(); @@ -523,6 +532,13 @@ usb_dev_handle *nutusb_open(const char *port) upsdebugx(1, "device %s opened successfully", curDevice.Bus); errout = 0; +#ifdef WIN32 + if ((ret = usb_set_configuration(dev_h, 1)) < 0) + { + upsdebugx(1, "Can't set POWERWARE USB configuration: %s", nut_usb_strerror(ret)); + errout = 1; + } +#endif if ((ret = usb_claim_interface(dev_h, 0)) < 0) { upsdebugx(1, "Can't claim POWERWARE USB interface: %s", nut_usb_strerror(ret)); diff --git a/drivers/belkin-hid.c b/drivers/belkin-hid.c index c1aaccf1eb..f1d4083afa 100644 --- a/drivers/belkin-hid.c +++ b/drivers/belkin-hid.c @@ -31,7 +31,7 @@ #include /* for fabs() */ -#define BELKIN_HID_VERSION "Belkin/Liebert HID 0.18" +#define BELKIN_HID_VERSION "Belkin/Liebert HID 0.19" /* Belkin */ #define BELKIN_VENDORID 0x050d @@ -68,9 +68,11 @@ static usb_device_id_t belkin_usb_device_table[] = { { USB_DEVICE(BELKIN_VENDORID, 0x1100), NULL }, /* Liebert GXT4 UPS */ - { USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL }, + { USB_DEVICE(LIEBERT_VENDORID, 0x0000), NULL }, /* Liebert PowerSure PSA UPS */ { USB_DEVICE(LIEBERT_VENDORID, 0x0001), NULL }, + /* Liebert PowerSure PST UPS */ + { USB_DEVICE(LIEBERT_VENDORID, 0x0002), NULL }, /* Liebert PowerSure PSI 1440 */ { USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL }, /* Liebert GXT3 */ @@ -529,7 +531,7 @@ static hid_info_t belkin_hid2nut[] = { yet implemented) workaround, see the belkinunv(8) man page. -PS 2005/08/28 */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* added for debugging Liebert GXT3 : */ { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, @@ -543,7 +545,7 @@ static hid_info_t belkin_hid2nut[] = { { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, -#endif +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } diff --git a/drivers/belkin.c b/drivers/belkin.c index 6757fc7e3e..34a3f39b24 100644 --- a/drivers/belkin.c +++ b/drivers/belkin.c @@ -26,9 +26,10 @@ #include "main.h" #include "serial.h" #include "belkin.h" +#include "nut_stdint.h" #define DRIVER_NAME "Belkin Smart protocol driver" -#define DRIVER_VERSION "0.25" +#define DRIVER_VERSION "0.26" static ssize_t init_communication(void); static ssize_t get_belkin_reply(char *buf); @@ -120,7 +121,7 @@ static ssize_t get_belkin_reply(char *buf) ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { - ser_comm_fail("Initial read returned %zd bytes", ret); + ser_comm_fail("Initial read returned %" PRIiSIZE " bytes", ret); return -1; } @@ -147,7 +148,7 @@ static ssize_t get_belkin_reply(char *buf) upsdebugx(3, "Received: %s", buf); if (ret != cnt) { - ser_comm_fail("Second read returned %zd bytes, expected %ld", ret, cnt); + ser_comm_fail("Second read returned %" PRIiSIZE " bytes, expected %ld", ret, cnt); return -1; } @@ -168,7 +169,7 @@ static ssize_t do_broken_rat(char *buf) ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { - ser_comm_fail("Initial read returned %zd bytes", ret); + ser_comm_fail("Initial read returned %" PRIiSIZE " bytes", ret); return -1; } @@ -200,7 +201,7 @@ static ssize_t do_broken_rat(char *buf) upsdebugx(3, "Received: %s", buf); if (ret != cnt) { - ser_comm_fail("Second read returned %zd bytes, expected %ld", ret, cnt); + ser_comm_fail("Second read returned %" PRIiSIZE " bytes, expected %ld", ret, cnt); return -1; } diff --git a/drivers/belkin.h b/drivers/belkin.h index 4e8e8d8f40..7a91c07eb7 100644 --- a/drivers/belkin.h +++ b/drivers/belkin.h @@ -17,7 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef WIN32 #include +#endif #include "serial.h" #include "timehead.h" diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 0a46be4951..83826a8b7f 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -94,7 +94,7 @@ #include "serial.h" #define DRIVER_NAME "Belkin 'Universal UPS' driver" -#define DRIVER_VERSION "0.08" +#define DRIVER_VERSION "0.09" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -159,7 +159,7 @@ upsdrv_info_t upsdrv_info = { #define BS_REPLACE 0x80 /* size of an array */ -#define asize(x) ((int)(sizeof(x)/sizeof(x[0]))) +#define asize(x) ((int)(SIZEOF_ARRAY(x))) static const char *upstype[3] = { "ONLINE", @@ -447,17 +447,19 @@ static int belkin_nut_write_int(unsigned char reg, int val) { will be discarded. After this call, the device is ready for reading and writing via read(2) and write(2). Return a valid file descriptor on success, or else -1 with errno set. */ -static int belkin_std_open_tty(const char *device) { - int fd; +static TYPE_FD_SER belkin_std_open_tty(const char *device) { + TYPE_FD_SER fd; struct termios tios; +#ifndef WIN32 struct flock flock; +#endif char buf[128]; ssize_t r; /* open the device */ fd = open(device, O_RDWR | O_NONBLOCK); - if (fd == -1) { - return -1; + if (INVALID_FD_SER(fd)) { + return ERROR_FD_SER; } /* set communications parameters: 2400 baud, 8 bits, 1 stop bit, no @@ -470,7 +472,7 @@ static int belkin_std_open_tty(const char *device) { r = tcsetattr(fd, TCSANOW, &tios); if (r == -1) { close(fd); - return -1; + return ERROR_FD_SER; } /* signal the UPS to enter "smart" mode. This is done by setting RTS @@ -487,17 +489,20 @@ static int belkin_std_open_tty(const char *device) { r = ser_flush_io(fd); if (r == -1) { close(fd); - return -1; + return ERROR_FD_SER; } +/* TODO: port to WIN32 */ +#ifndef WIN32 /* lock the port */ memset(&flock, 0, sizeof(flock)); flock.l_type = F_RDLCK; r = fcntl(fd, F_SETLK, &flock); if (r == -1) { close(fd); - return -1; + return ERROR_FD_SER; } +#endif /* sleep at least 0.25 seconds for the UPS to wake up. Belkin's own software sleeps 1 second, so that's what we do, too. */ @@ -508,13 +513,20 @@ static int belkin_std_open_tty(const char *device) { r = tcflush(fd, TCIFLUSH); if (r == -1) { close(fd); - return -1; + return ERROR_FD_SER; } +#ifndef WIN32 r = read(fd, buf, 127); +#else +/* WIN32 : w32_serial_read is blocking, using select_read with 0ms timeout + * is non-blocking */ + r = select_read(fd, buf, 127, 0, 0); +#endif + if (r == -1 && errno != EAGAIN) { close(fd); - return -1; + return ERROR_FD_SER; } /* leave port in non-blocking state */ @@ -523,13 +535,19 @@ static int belkin_std_open_tty(const char *device) { } /* blocking read with 1-second timeout (use non-blocking i/o) */ -static int belkin_std_upsread(int fd, unsigned char *buf, int n) { +static int belkin_std_upsread(TYPE_FD_SER fd, unsigned char *buf, int n) { int count = 0; ssize_t r; int tries = 0; while (count < n) { +#ifndef WIN32 r = read(fd, &buf[count], (size_t)(n-count)); +#else + /* WIN32 : w32_serial_read is blocking, using select_read + * with 0ms timeout is non-blocking */ + r = select_read(fd, buf, (size_t)(n-count), 0, 0); +#endif if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); @@ -547,7 +565,7 @@ static int belkin_std_upsread(int fd, unsigned char *buf, int n) { } /* blocking write with 1-second timeout (use non-blocking i/o) */ -static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { +static int belkin_std_upswrite(TYPE_FD_SER fd, unsigned char *buf, int n) { int count = 0; ssize_t r; int tries = 0; @@ -573,7 +591,7 @@ static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { /* receive Belkin message from UPS, check for well-formedness (leading byte, checksum). Return length of message, or -1 if not well-formed */ -static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { +static int belkin_std_receive(TYPE_FD_SER fd, unsigned char *buf, int bufsize) { int r; int n=0; int len; @@ -619,7 +637,7 @@ static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { /* read the value of an integer register from UPS. Return -1 on failure. */ -static int belkin_std_read_int(int fd, unsigned char reg) { +static int belkin_std_read_int(TYPE_FD_SER fd, unsigned char reg) { unsigned char buf[MAXMSGSIZE]; int len, r; @@ -661,7 +679,7 @@ static int belkin_std_read_int(int fd, unsigned char reg) { /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ -static int belkin_std_write_int(int fd, unsigned char reg, int val) { +static int belkin_std_write_int(TYPE_FD_SER fd, unsigned char reg, int val) { unsigned char buf[MAXMSGSIZE]; int r; @@ -766,7 +784,7 @@ static int belkin_wait(void) char *val; int failcount = 0; /* count consecutive failed connection attempts */ int failerrno = 0; - int fd; + TYPE_FD_SER fd; int r; int bs, ov, bl, st; @@ -792,7 +810,7 @@ static int belkin_wait(void) updatestatus(smode, "Connecting to UPS..."); failcount = 0; - fd = -1; + fd = ERROR_FD_SER; while (1) { if (failcount >= 3 && nohang) { @@ -802,10 +820,10 @@ static int belkin_wait(void) } else if (failcount >= 3) { updatestatus(smode, "UPS is not responding, will keep trying: %s", strerror(failerrno)); } - if (fd == -1) { + if (INVALID_FD_SER(fd)) { fd = belkin_std_open_tty(device_path); } - if (fd == -1) { + if (INVALID_FD_SER(fd)) { failcount++; failerrno = errno; sleep(1); @@ -819,7 +837,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD_SER; sleep(1); continue; } @@ -828,7 +846,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD_SER; sleep(1); continue; } @@ -837,7 +855,7 @@ static int belkin_wait(void) failcount++; failerrno = errno; close(fd); - fd = -1; + fd = ERROR_FD_SER; sleep(1); continue; } @@ -1196,35 +1214,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 +1262,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/bestfcom.c b/drivers/bestfcom.c index c53aa953f2..38a996dac1 100644 --- a/drivers/bestfcom.c +++ b/drivers/bestfcom.c @@ -45,7 +45,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups/Fortress driver" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index 97300f673d..d86d1b4daa 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -35,7 +35,7 @@ #endif #define DRIVER_NAME "Best Fortress UPS driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.08" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -47,12 +47,39 @@ upsdrv_info_t upsdrv_info = { { NULL } }; +/* + * Choose 20s for off delay, reading the tea leaves from other drivers. + */ +static const char *shutdown_delay = "20"; + +/* + * Logging plan: + * CRIT: executing poweroff + * ERR: indicates a bug in nut + * WARNING: indicates a serious misconfiguration that will cause nut + * to fail to function + * NOTICE: rare situations without a planned reporting path (none so + * far) + * INFO: One message each for startup, first establishment of comms + * DEBUG: Loss/restore of comms, and perhaps things that are normally + * reported via other daemons (on battery, low battery) + * D1: Things that happen rarely (startup/shutdown, param set, instant + * commands) + * D2: ? + * D3: trace of communications + * D4: more io than necessary to see working communications + * D5: log even succesful acks + */ + static int instcmd (const char *cmdname, const char *extra); static int upsdrv_setvar (const char *varname, const char *val); -/* rated VA load if known */ +/* Rated maximum VA output as configured by the user. */ static int maxload = 0; +/* + * NB: Not called for shutdown. + */ void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "Best Power"); @@ -64,7 +91,6 @@ void upsdrv_initinfo(void) if (maxload) dstate_setinfo("ups.load", "0"); dstate_setinfo("output.voltamps", "0"); - dstate_setinfo("ups.delay.shutdown", "10"); /* write only */ /* tunable via front panel: (european voltage level) parameter factory default range @@ -197,12 +223,23 @@ static int upssend(const char *fmt,...) { #endif va_end(ap); + /* Why do we not upsflushin here? */ + + upsdebugx(3, "%s: sending %d bytes <%s>", __func__, ret, buf); + if ((ret < 1) || (ret >= (int) sizeof(buf))) - upslogx(LOG_WARNING, "ser_send_pace: vsnprintf needed more " - "than %d bytes", (int)sizeof(buf)); + upslogx(LOG_ERR, "%s: vsnprintf needed more than %d bytes", + __func__, (int) sizeof(buf)); for (p = buf; *p && sent < INT_MAX - 1; p++) { +#ifndef WIN32 if (write(upsfd, p, 1) != 1) +#else + DWORD bytes_written; + BOOL res; + res = WriteFile(upsfd, p, 1, &bytes_written,NULL); + if (res == 0 || bytes_written == 0) +#endif return -1; /* Note: LGTM.com analysis warns that here @@ -217,22 +254,35 @@ static int upssend(const char *fmt,...) { sent++; if (sent >= INT_MAX) { - upslogx(LOG_WARNING, "ser_send_pace: sent more than INT_MAX, aborting"); + upslogx(LOG_ERR, "%s: sent >= INT_MAX, aborting", + __func__); } } + if (ret != (int) sent) { + upsdebugx(1, "%s: ret %d != sent %u", __func__, ret, sent); + } + return (int)sent; } static ssize_t upsrecv(char *buf,size_t bufsize,char ec,const char *ic) { - return ser_get_line(upsfd, buf, bufsize - 1, ec, ic, - SER_WAIT_SEC, SER_WAIT_USEC); + ssize_t nread; + + nread = ser_get_line(upsfd, buf, bufsize - 1, ec, ic, + SER_WAIT_SEC, SER_WAIT_USEC); + + /* \todo is buf null terminated? */ + upsdebugx(4, "%s: read %" PRIiSIZE " <%s>", __func__, nread, buf); + + return nread; } static ssize_t upsflushin(int f, int verbose, const char *ignset) { NUT_UNUSED_VARIABLE(f); + upsdebugx(4, "%s: begin", __func__); return ser_flush_in(upsfd, ignset, verbose); } @@ -248,7 +298,7 @@ void upsdrv_updateinfo(void) char ch; int checksum_ok = -1, is_online = 1, is_off, low_batt, trimming, boosting; - upsdebugx(1, "upsdrv_updateinfo"); + upsdebugx(2, "%s: begin", __func__); for (retry = 0; retry < 5; ++retry) { upsflushin (0, 0, "\r "); @@ -257,14 +307,16 @@ void upsdrv_updateinfo(void) temp[2] = 0; do { if ((recv = upsrecv (temp+2, sizeof temp - 2, ENDCHAR, IGNCHARS)) <= 0) { + upsdebugx(1, "%s: upsrecv failed, " + "retrying without counting", __func__); upsflushin (0, 0, "\r "); upssend ("f\r"); while (ser_get_char(upsfd, &ch, 0, UPSDELAY) > 0 && ch != '\n'); /* response starts with \r\n */ } } while (temp[2] == 0); - upsdebugx(1, "upsdrv_updateinfo: received %zi bytes (try %i)", recv, retry); - upsdebug_hex(5, "buffer", temp, (size_t)recv); + upsdebugx(3, "%s: received %" PRIiSIZE " bytes (try %i)", + __func__, recv, retry); /* syslog (LOG_DAEMON | LOG_NOTICE,"ups: got %d chars '%s'\n", recv, temp + 2); */ /* status example: @@ -282,6 +334,16 @@ void upsdrv_updateinfo(void) checksum_ok = ( (checksum (temp+2) & 0xff) == 0 ); /* setinfo (INFO_, ""); */ + if (!checksum_ok) { + upsdebug_hex(5, "upsdrv_updateinfo: " + "checksum failure buffer hex", + temp, (size_t)recv); + upsdebug_ascii(5, "upsdrv_updateinfo: " + "checksum failure buffer ascii", + temp, (size_t)recv); + } + + /* I can't figure out why this is missing the first two chars. But the first two chars are not used, so just set them to zero when missing. */ @@ -294,23 +356,29 @@ void upsdrv_updateinfo(void) else if (len != 80) checksum_ok = 0; if (checksum_ok) break; + + upsdebugx(1, "%s: failed to read status try %d", + __func__, retry); sleep(SER_WAIT_SEC); } if (!p || len < 1 || checksum_ok < 0) { - upsdebugx(2, "pointer to data not initialized after processing"); + /* \todo: Analyze/fix code and rewrite message. */ + upsdebugx(2, "%s: pointer to data not initialized after processing", + __func__); dstate_datastale(); return; } if (!checksum_ok) { - upsdebugx(2, "checksum corruption"); + upsdebugx(2, "%s: checksum corruption", __func__); upsdebug_hex(3, "buffer", temp, (size_t)len); dstate_datastale(); return; } - /* upslogx(LOG_INFO, "updateinfo: %s", p); */ + /* Log assuming ASCII which it 99% is. \todo Improve. */ + upsdebugx(3, "%s: %s", __func__, p); setinfo_int ("input.voltage", p+24,4); setinfo_int ("output.voltage", p+28,4); @@ -361,11 +429,23 @@ void upsdrv_updateinfo(void) static int setparam (int parameter, int dlen, const char * data) { char reply[80]; + + upsdebugx(2, "%s: begin", __func__); + /* Note the use of "%*s" - parameter (int)dlen specifies * the string width reserved for data */ upssend ("p%d=%*s\r", parameter, dlen, data); - if (upsrecv (reply, sizeof(reply), ENDCHAR, "") < 0) return 0; - return strncmp (reply, "OK", 2) == 0; + if (upsrecv (reply, sizeof(reply), ENDCHAR, "") < 0) { + upsdebugx(1, "%s: did not get reply", __func__); + return 0; + } + if (strncmp (reply, "OK", 2) == 0) { + upsdebugx(5, "%s: reply OK", __func__); + return 1; + } else { + upsdebugx(1, "%s: reply NOT ok", __func__); + return 0; + } } /* ups_setsuper: set super-user access @@ -390,7 +470,7 @@ static void autorestart (int restart) static int upsdrv_setvar (const char *var, const char * data) { int parameter; size_t len = strlen(data); - upsdebugx(1, "Setvar: %s %s", var, data); + upsdebugx(1, "%s: %s %s (%" PRIuSIZE " bytes)", __func__, var, data, len); if (strcmp("input.transfer.low", var) == 0) { parameter = 7; } @@ -401,7 +481,13 @@ static int upsdrv_setvar (const char *var, const char * data) { parameter = 2; } else { - upslogx(LOG_INFO, "Setvar: unsettable variable %s", var); + /* + * \todo Figure out if we get here by a code error or + * by the user asking for a variable that does not + * exist. If the former, change to LOG_ERR and if the + * latter change to LOG_DEBUG. + */ + upslogx(LOG_INFO, "%s: unsettable variable %s", __func__, var); return STAT_SET_UNKNOWN; } ups_setsuper (1); @@ -413,42 +499,60 @@ static int upsdrv_setvar (const char *var, const char * data) { return STAT_SET_HANDLED; } +/* + * The "power down and maybe return command" is "OFF %d\r", with a + * delay in seconds before poweroff. As a special case, "OFF 0" does + * not shut down. The UPS will power on the load when power returns + * (or after a delay if power is not out), according to the front panel + * parameter, or the value set via `autorestart()`. + */ + +/* + * This is equivalent to the `shutdown.return` instant command, but + * invoked with `-k`. + * \todo Reduce duplication. + */ void upsdrv_shutdown(void) { const char *grace; + upsdebugx(2, "%s: begin", __func__); + grace = dstate_getinfo("ups.delay.shutdown"); + if (!grace) { + upsdebugx(1, "%s: ups.delay.shutdown is NULL!", __func__); + /* Pick a different value than 20 so we can see it in the logs. */ + grace = "30"; + } - if (!grace) - grace = "1"; /* apparently, OFF0 does not work */ + upslogx(LOG_CRIT, "%s: OFF/restart in %s seconds", __func__, grace); - printf ("shutdown in %s seconds\n", grace); - /* make power return when utility power returns */ + /* Start again, overriding front panel setting. */ autorestart (1); + upssend ("OFF%s\r", grace); /* I'm nearly dead, Jim */ - /* OFF will powercycle when line power is available again */ + + upsdebugx(2, "%s: end", __func__); } static int instcmd (const char *cmdname, const char *extra) { - const char *p; - if (!strcasecmp(cmdname, "load.off")) { - printf ("powering off\n"); + upslogx(LOG_CRIT, "%s: %s: OFF/stayoff in 1s", + __func__, cmdname); autorestart (0); upssend ("OFF1\r"); return STAT_INSTCMD_HANDLED; } else if (!strcasecmp(cmdname, "shutdown.return")) { - p = dstate_getinfo ("ups.delay.shutdown"); - if (!p) p = "1"; - printf ("shutdown in %s seconds\n", p); - autorestart (1); - upssend ("OFF%s\r", p); + upsdebugx(2, "%s: %s: start", __func__, cmdname); + upsdrv_shutdown(); return STAT_INSTCMD_HANDLED; } - upslogx(LOG_INFO, "instcmd: unknown command [%s] [%s]", cmdname, extra); + /* \todo Software error or user error? */ + upslogx(LOG_ERR, "%s: unknown command [%s] [%s]", + __func__, cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -474,6 +578,9 @@ static struct { {NULL, B1200}, }; +/* + * Called first, for normal operation and for shutdown. + */ void upsdrv_initups(void) { speed_t speed = B1200; @@ -481,6 +588,8 @@ void upsdrv_initups(void) char * speed_val = getval("baudrate"); char * max_load = getval("max_load"); + upsdebugx(1, "%s: begin", __func__); + if (max_load) maxload = atoi(max_load); if (speed_val) { @@ -493,14 +602,25 @@ void upsdrv_initups(void) } upsfd = ser_open(device_path); + if (INVALID_FD(upsfd)) { + upslogx(LOG_WARNING, "%s: failed to open %s", + __func__, device_path); + /* \todo: Deal with the failure */ + } + + /* ser_set_speed returns int 0 always; fatal if ioctl fails */ ser_set_speed(upsfd, device_path, speed); - /* TODO: probe ups type */ - /* the upsh handlers can't be done here, as they get initialized - * shortly after upsdrv_initups returns to main. - */ + upsdebugx(1, "%s: opened %s speed %s upsfd %d", + __func__, device_path, speed_val ? speed_val : "DEFAULT", upsfd); + + /* Set early so that it is in place for shutdown. */ + dstate_setinfo("ups.delay.shutdown", "%s", shutdown_delay); + + upsdebugx(1, "%s: end", __func__); } void upsdrv_cleanup(void) { + upsdebugx(1, "%s: begin/end", __func__); } diff --git a/drivers/bestpower-mib.c b/drivers/bestpower-mib.c index fd3263ef8c..4ec21e7c8a 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.40" #define BESTPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.2947.1.1.2.0" /* @@ -35,62 +35,56 @@ #define BESTPOWER_SYSOID BESTPOWER_OID_MODEL_NAME static info_lkp_t bestpower_power_status[] = { - { 1, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(1, "OL"), + info_lkp_default(2, "OB"), + info_lkp_sentinel +}; /* Snmp2NUT lookup table for Best Power MIB */ static snmp_info_t bestpower_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", + 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 }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /*.1.3.6.1.4.1.2947.1.1.1.0 = STRING: "Ferrups" .1.3.6.1.4.1.2947.1.1.2.0 = STRING: "FE850VA"*/ - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, BESTPOWER_OID_MODEL_NAME, - "Best Ferrups", SU_FLAG_STATIC, NULL }, + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, BESTPOWER_OID_MODEL_NAME, + "Best Ferrups", SU_FLAG_STATIC, NULL), - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.5.0", - "", SU_FLAG_STATIC, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.7.0", - "", SU_FLAG_STATIC, NULL }, - { "ups.power", 0, 1, ".1.3.6.1.4.1.2947.1.1.3.0", "", - 0, NULL }, - { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.8.0", "", - 0, NULL }, + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.5.0", + "", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.7.0", + "", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.power", 0, 1, ".1.3.6.1.4.1.2947.1.1.3.0", "", + 0, NULL), + snmp_info_default("ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.8.0", "", + 0, NULL), - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.2.1.0", "", - 0 /*SU_STATUS_PWR*/, &bestpower_power_status[0] }, + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.2.1.0", "", + 0 /*SU_STATUS_PWR*/, &bestpower_power_status[0]), /* Battery runtime is expressed in minutes */ - { "battery.runtime", 0, 60.0, ".1.3.6.1.4.1.2947.1.2.3.0", "", - 0, NULL }, + snmp_info_default("battery.runtime", 0, 60.0, ".1.3.6.1.4.1.2947.1.2.3.0", "", + 0, NULL), /* The elapsed time in seconds since the * UPS has switched to battery power */ - { "battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.2947.1.2.2.0", "", - 0, NULL }, - { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.4.0", "", - 0, NULL }, - { "battery.current", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.5.0", "", - 0, NULL }, + snmp_info_default("battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.2947.1.2.2.0", "", + 0, NULL), + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.4.0", "", + 0, NULL), + snmp_info_default("battery.current", 0, 0.1, ".1.3.6.1.4.1.2947.1.2.5.0", "", + 0, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel } ; mib2nut_info_t bestpower = { "bestpower", BESTPOWER_MIB_VERSION, NULL, diff --git a/drivers/bestuferrups.c b/drivers/bestuferrups.c index 8bed32bfc6..c248b13ba7 100644 --- a/drivers/bestuferrups.c +++ b/drivers/bestuferrups.c @@ -33,7 +33,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups Series ME/RE/MD driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -420,7 +420,7 @@ static void setup_serial(void) } -void upsdrv_initups () +void upsdrv_initups (void) { char temp[256], fcstring[512]; diff --git a/drivers/bestups.c b/drivers/bestups.c index c0d56d7b48..d388658e12 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -26,9 +26,10 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #define DRIVER_NAME "Best UPS driver" -#define DRIVER_VERSION "1.07" +#define DRIVER_VERSION "1.08" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -353,13 +354,13 @@ void upsdrv_updateinfo(void) } if (ret < 46) { - ser_comm_fail("Poll failed: short read (got %zd bytes)", ret); + ser_comm_fail("Poll failed: short read (got %" PRIiSIZE " bytes)", ret); dstate_datastale(); return; } if (ret > 46) { - ser_comm_fail("Poll failed: response too long (got %zd bytes)", + ser_comm_fail("Poll failed: response too long (got %" PRIiSIZE " bytes)", ret); dstate_datastale(); return; @@ -436,6 +437,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..230cd9a89e 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -7,7 +7,7 @@ * device support from such legacy drivers over time. * * A document describing the protocol implemented by this driver can be - * found online at http://www.networkupstools.org/ups-protocols/megatec.html + * found online at https://www.networkupstools.org/ups-protocols/megatec.html * * Copyright (C) * 2008,2009 - Arjen de Korte @@ -88,7 +88,7 @@ static const struct { /* * Do whatever we think is needed when we read a battery voltage from the UPS. - * Basically all it does now, is guestimating the battery charge, but this + * Basically all it does now, is guesstimating the battery charge, but this * could be extended. */ static double blazer_battery(const char *ptr, char **endptr) @@ -132,7 +132,7 @@ static double blazer_load(const char *ptr, char **endptr) /* * The battery voltage will quickly return to at least the nominal value after - * discharging them. For overlapping battery.voltage.low/high ranges therefor + * discharging them. For overlapping battery.voltage.low/high ranges therefore * choose the one with the highest multiplier. */ static double blazer_packs(const char *ptr, char **endptr) @@ -150,7 +150,7 @@ static double blazer_packs(const char *ptr, char **endptr) for (i = 0; packs[i] > 0; i++) { - if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) { + if (packs[i] * batt.volt.act > 1.25 * batt.volt.nom) { continue; } @@ -616,7 +616,7 @@ static void blazer_initbattery(void) dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); - upslogx(LOG_INFO, "Using 'guestimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high); + upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high); } val = getval("runtimecal"); @@ -680,7 +680,7 @@ static void blazer_initbattery(void) if (val) { load.low = strtod(val, NULL) / 100; - if ((load.low <= 0) || (load.low > 1)) { + if ((load.low < 0) || (load.low > 1)) { fatalx(EXIT_FAILURE, "Idle load out of range [0..100]"); } @@ -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; @@ -840,9 +849,6 @@ void upsdrv_updateinfo(void) dstate_dataok(); } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { int retry; @@ -869,9 +875,11 @@ void upsdrv_shutdown(void) continue; } - fatalx(EXIT_SUCCESS, "Shutting down in %ld seconds", offdelay); - + upslogx(LOG_ERR, "Shutting down in %ld seconds", offdelay); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } diff --git a/drivers/blazer.h b/drivers/blazer.h index 6a8c71c24e..67597d5b4e 100644 --- a/drivers/blazer.h +++ b/drivers/blazer.h @@ -7,7 +7,7 @@ * device support from such legacy drivers over time. * * A document describing the protocol implemented by this driver can be - * found online at "http://www.networkupstools.org/protocols/megatec.html". + * found online at "https://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2008 - Arjen de Korte * diff --git a/drivers/blazer_ser.c b/drivers/blazer_ser.c index a906181b35..f0cd90e4e8 100644 --- a/drivers/blazer_ser.c +++ b/drivers/blazer_ser.c @@ -7,7 +7,7 @@ * device support from such legacy drivers over time. * * A document describing the protocol implemented by this driver can be - * found online at "http://www.networkupstools.org/protocols/megatec.html". + * found online at "https://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2008 - Arjen de Korte * @@ -31,7 +31,7 @@ #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol serial driver" -#define DRIVER_VERSION "1.58" +#define DRIVER_VERSION "1.60" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -121,6 +121,7 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { #ifndef TESTING +#ifndef WIN32 /* TODO : Correctly set the port parameters for WIN32 */ const struct { const char *val; const int dtr; @@ -187,7 +188,10 @@ void upsdrv_initups(void) * Allow some time to settle for the cablepower */ usleep(100000); -#endif +#else + upsdebugx(0, "blazer_ser: upsdrv_init(): serial port setup for WIN32 currently has not been ported (TODO)"); +#endif /* WIN32 */ +#endif /* TESTING */ blazer_initups(); } diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index 78e271bf6d..27c5ceecbf 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -7,7 +7,7 @@ * device support from such legacy drivers over time. * * A document describing the protocol implemented by this driver can be - * found online at "http://www.networkupstools.org/protocols/megatec.html". + * found online at "https://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2003-2009 Arjen de Korte * Copyright (C) 2011-2012 Arnaud Quette @@ -32,9 +32,12 @@ #include "nut_libusb.h" #include "usb-common.h" #include "blazer.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #define DRIVER_NAME "Megatec/Q1 protocol USB driver" -#define DRIVER_VERSION "0.14" +#define DRIVER_VERSION "0.17" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -129,11 +132,11 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) */ switch (ret) { - case ERROR_PIPE: /** Pipe error or Broken pipe */ + case LIBUSB_ERROR_PIPE: /** Pipe error or Broken pipe */ usb_clear_halt(udev, 0x81); break; - case ERROR_TIMEOUT: /** Operation or Connection timed out */ + case LIBUSB_ERROR_TIMEOUT: /** Operation or Connection timed out */ break; } @@ -204,7 +207,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) 0x09, 0x2, 0, (usb_ctrl_charbuf)&tmp[i], 8, 1000); if (ret <= 0) { - upsdebugx(3, "send: %s", (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); + upsdebugx(3, "send: %s", (ret != LIBUSB_ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); return ret; } } @@ -221,7 +224,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { - upsdebugx(3, "read: %s", (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); + upsdebugx(3, "read: %s", (ret != LIBUSB_ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); return ret; } @@ -297,6 +300,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) upsdebugx(1, "received %d (%d)", ret, buf[0]); if (langid_fix != -1) { + size_t di, si, size; /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ if (ret != buf[0]) @@ -308,7 +312,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ - size_t di, si, size = (size_t)buf[0]; + size = (size_t)buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) break; @@ -442,11 +446,15 @@ ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) ssize_t ret; if (udev == NULL) { - ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); + dstate_setinfo("driver.state", "reconnect.trying"); + + ret = usb->open_dev(&udev, &usbdevice, reopen_matcher, NULL); if (ret < 1) { return ret; } + + dstate_setinfo("driver.state", "reconnect.updateinfo"); } ret = (*subdriver_command)(cmd, buf, buflen); @@ -456,7 +464,7 @@ ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) switch (ret) { - case ERROR_BUSY: /* Device or resource busy */ + case LIBUSB_ERROR_BUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); #ifndef HAVE___ATTRIBUTE__NORETURN exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ @@ -470,7 +478,7 @@ ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) # endif #endif /* WITH_LIBUSB_0_1 */ - case ERROR_PIPE: /* Broken pipe */ + case LIBUSB_ERROR_PIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; @@ -484,23 +492,28 @@ ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) upsdebugx(1, "Device reset handled"); } goto fallthrough_case_reconnect; - case ERROR_NO_DEVICE: /* No such device */ - case ERROR_ACCESS: /* Permission denied */ - case ERROR_IO: /* I/O error */ + case LIBUSB_ERROR_NO_DEVICE: /* No such device */ + case LIBUSB_ERROR_ACCESS: /* Permission denied */ + case LIBUSB_ERROR_IO: /* I/O error */ #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -ENXIO: /* No such device or address */ #endif - case ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NOT_FOUND: /* No such file or directory */ fallthrough_case_reconnect: /* Uh oh, got to reconnect! */ - usb->close(udev); + dstate_setinfo("driver.state", "reconnect.trying"); + usb->close_dev(udev); udev = NULL; break; - case ERROR_TIMEOUT: /* Connection timed out */ - case ERROR_OVERFLOW: /* Value too large for defined data type */ -#if EPROTO && WITH_LIBUSB_0_1 + case LIBUSB_ERROR_TIMEOUT: /* Connection timed out */ +/* libusb-win32 does not know EPROTO and EOVERFLOW, + * it only returns EIO for any IO errors */ +#ifndef WIN32 + case LIBUSB_ERROR_OVERFLOW: /* Value too large for defined data type */ +# if EPROTO && WITH_LIBUSB_0_1 case -EPROTO: /* Protocol error */ +# endif #endif default: break; @@ -552,9 +565,10 @@ static const struct subdriver_t { void upsdrv_help(void) { #ifndef TESTING - printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); size_t i; + printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); + for (i = 0; subdriver[i].name != NULL; i++) { if (i>0) printf(", "); @@ -570,6 +584,8 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); + + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)"); @@ -583,10 +599,11 @@ void upsdrv_initups(void) #ifndef TESTING int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ - char *regex_array[7]; - + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; 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"); @@ -594,6 +611,13 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); regex_array[6] = getval("device"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +# else + if (getval("busport")) { + upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)"); + } +# endif /* check for language ID workaround (#1) */ if (getval("langid_fix")) { @@ -646,7 +670,7 @@ void upsdrv_initups(void) /* link the matchers */ regex_matcher->next = &device_matcher; - ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); + ret = usb->open_dev(&udev, &usbdevice, regex_matcher, NULL); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" @@ -684,7 +708,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); } } @@ -702,7 +726,7 @@ void upsdrv_initinfo(void) void upsdrv_cleanup(void) { #ifndef TESTING - usb->close(udev); + usb->close_dev(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); @@ -710,5 +734,8 @@ void upsdrv_cleanup(void) free(usbdevice.Serial); free(usbdevice.Bus); free(usbdevice.Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(usbdevice.BusPort); +# endif #endif /* TESTING */ } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 81374358ad..62f3cd186d 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -20,13 +20,16 @@ #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include +#ifndef WIN32 #include #include +#endif #define DRIVER_NAME "clone outlet UPS Driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -64,7 +67,15 @@ static struct { static int dumpdone = 0; static PCONF_CTX_t sock_ctx; -static time_t last_heard = 0, last_ping = 0, last_connfail = 0; +static time_t last_heard = 0, last_ping = 0; + +#ifdef WIN32 +static char read_buf[SMALLBUF]; +static OVERLAPPED read_overlapped; +#else +/* TODO: Why not built in WIN32? */ +static time_t last_connfail = 0; +#endif static int parse_args(size_t numargs, char **arg) { @@ -140,22 +151,39 @@ static int parse_args(size_t numargs, char **arg) } -static int sstate_connect(void) +static TYPE_FD sstate_connect(void) { + TYPE_FD fd; + +#ifndef WIN32 ssize_t ret; - int fd; + int 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 (%" PRIuSIZE ") for 'struct sockaddr_un->sun_path' " + "on this system (%" PRIuSIZE ")", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { + if (INVALID_FD(fd)) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", device_path); - return -1; + return ERROR_FD; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); @@ -169,13 +197,13 @@ static int sstate_connect(void) time(&now); if (difftime(now, last_connfail) < 60) { - return -1; + return ERROR_FD; } last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_GETFL, 0); @@ -183,7 +211,7 @@ static int sstate_connect(void) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); @@ -191,7 +219,7 @@ static int sstate_connect(void) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; } /* get a dump started so we have a fresh set of data */ @@ -200,9 +228,55 @@ static int sstate_connect(void) if (ret != (int)strlen(dumpcmd)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; } + /* continued below... */ +#else /* WIN32 */ + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s/%s", dflt_statepath(), device_path); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if (result == FALSE) { + return ERROR_FD; + } + + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); + return ERROR_FD; + } + + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); + CloseHandle(fd); + return ERROR_FD; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd, read_buf, + sizeof(read_buf) - 1, /*-1 to be sure to have a trailling 0 */ + NULL, &(read_overlapped)); +#endif + + /* sstate_connect() continued for both platforms: */ pconf_init(&sock_ctx, NULL); time(&last_heard); @@ -219,14 +293,20 @@ static int sstate_connect(void) static void sstate_disconnect(void) { - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { + /* Already disconnected... or not yet? ;) */ return; } pconf_finish(&sock_ctx); +#ifndef WIN32 close(upsfd); - upsfd = -1; +#else + CloseHandle(upsfd); +#endif + + upsfd = ERROR_FD; } @@ -234,11 +314,25 @@ static int sstate_sendline(const char *buf) { ssize_t ret; - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { return -1; /* failed */ } +#ifndef WIN32 ret = write(upsfd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (upsfd,buf,strlen(buf),&bytesWritten,NULL); + + if( result == 0 ) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret == (int)strlen(buf)) { return 0; @@ -253,9 +347,10 @@ static int sstate_readline(void) { int i; ssize_t ret; +#ifndef WIN32 char buf[SMALLBUF]; - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { return -1; /* failed */ } @@ -264,33 +359,44 @@ static int sstate_readline(void) if (ret < 0) { switch(errno) { - case EINTR: - case EAGAIN: - return 0; + case EINTR: + case EAGAIN: + return 0; - default: - upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); - return -1; + default: + upslog_with_errno(LOG_WARNING, "Read from UPS [%s] failed", device_path); + return -1; } } +#else + if (INVALID_FD(upsfd)) { + return -1; /* failed */ + } + + /* FIXME? I do not see this buf or read_buf filled below */ + char *buf = read_buf; + DWORD bytesRead; + GetOverlappedResult(upsfd, &read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif for (i = 0; i < ret; i++) { switch (pconf_char(&sock_ctx, buf[i])) { - case 1: - if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { - time(&last_heard); - } - continue; - - case 0: - continue; /* haven't gotten a line yet */ - - default: - /* parse error */ - upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); - return -1; + case 1: + if (parse_args(sock_ctx.numargs, sock_ctx.arglist)) { + time(&last_heard); + } + continue; + + case 0: + continue; /* haven't gotten a line yet */ + + default: + /* parse error */ + upslogx(LOG_NOTICE, "Parse error on sock: %s", sock_ctx.errmsg); + return -1; } } @@ -304,7 +410,7 @@ static int sstate_dead(int maxage) double elapsed; /* an unconnected ups is always dead */ - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path); return -1; /* dead */ } @@ -373,6 +479,11 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { + /* replace with a proper shutdown function */ +/* + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); + */ } diff --git a/drivers/clone.c b/drivers/clone.c index ab0824d9da..9f963b09a6 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -22,13 +22,16 @@ #include "main.h" #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" #include +#ifndef WIN32 #include #include +#endif #define DRIVER_NAME "Clone UPS driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -63,7 +66,13 @@ static long offdelay = 120, ondelay = 30; static PCONF_CTX_t sock_ctx; static time_t last_poll = 0, last_heard = 0, - last_ping = 0, last_connfail = 0; + last_ping = 0; +#ifndef WIN32 +static time_t last_connfail = 0; +#else +static char read_buf[SMALLBUF]; +static OVERLAPPED read_overlapped; +#endif static int instcmd(const char *cmdname, const char *extra); @@ -156,22 +165,39 @@ static int parse_args(size_t numargs, char **arg) } -static int sstate_connect(void) +static TYPE_FD sstate_connect(void) { + TYPE_FD fd; + +#ifndef WIN32 ssize_t ret; - int fd; + int 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 (%" PRIuSIZE ") for 'struct sockaddr_un->sun_path' " + "on this system (%" PRIuSIZE ")", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { + if (INVALID_FD(fd)) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", device_path); - return -1; + return ERROR_FD; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); @@ -185,13 +211,13 @@ static int sstate_connect(void) time(&now); if (difftime(now, last_connfail) < 60) { - return -1; + return ERROR_FD; } last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_GETFL, 0); @@ -199,7 +225,7 @@ static int sstate_connect(void) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); @@ -207,7 +233,7 @@ static int sstate_connect(void) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; } /* get a dump started so we have a fresh set of data */ @@ -216,9 +242,55 @@ static int sstate_connect(void) if (ret != (int)strlen(dumpcmd)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); close(fd); - return -1; + return ERROR_FD; + } + + /* continued below... */ +#else /* WIN32 */ + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s/%s", dflt_statepath(), device_path); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if (result == FALSE) { + return ERROR_FD; + } + + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s]", device_path); + return ERROR_FD; } + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", device_path); + CloseHandle(fd); + return ERROR_FD; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd, read_buf, + sizeof(read_buf) - 1, /*-1 to be sure to have a trailling 0 */ + NULL, &(read_overlapped)); +#endif + + /* sstate_connect() continued for both platforms: */ pconf_init(&sock_ctx, NULL); time(&last_heard); @@ -235,14 +307,20 @@ static int sstate_connect(void) static void sstate_disconnect(void) { - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { + /* Already disconnected... or not yet? ;) */ return; } pconf_finish(&sock_ctx); +#ifndef WIN32 close(upsfd); - upsfd = -1; +#else + CloseHandle(upsfd); +#endif + + upsfd = ERROR_FD; } @@ -250,11 +328,25 @@ static int sstate_sendline(const char *buf) { ssize_t ret; - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { return -1; /* failed */ } +#ifndef WIN32 ret = write(upsfd, buf, strlen(buf)); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (upsfd,buf,strlen(buf),&bytesWritten,NULL); + + if (result == 0) { + ret = 0; + } + else { + ret = (int)bytesWritten; + } +#endif if (ret == (int)strlen(buf)) { return 0; @@ -269,9 +361,10 @@ static int sstate_readline(void) { int i; ssize_t ret; +#ifndef WIN32 char buf[SMALLBUF]; - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { return -1; /* failed */ } @@ -289,6 +382,17 @@ static int sstate_readline(void) return -1; } } +#else + if (INVALID_FD(upsfd)) { + return -1; /* failed */ + } + + /* FIXME? I do not see this buf or read_buf filled below */ + char *buf = read_buf; + DWORD bytesRead; + GetOverlappedResult(upsfd, &read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif for (i = 0; i < ret; i++) { @@ -320,7 +424,7 @@ static int sstate_dead(int maxage) double elapsed; /* an unconnected ups is always dead */ - if (upsfd < 0) { + if (INVALID_FD(upsfd)) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", device_path); return -1; /* dead */ } @@ -466,7 +570,7 @@ void upsdrv_updateinfo(void) if (ups.timer.shutdown >= 0) { - ups.timer.shutdown -= difftime(now, last_poll); + ups.timer.shutdown -= (suseconds_t)(difftime(now, last_poll)); if (ups.timer.shutdown < 0) { const char *val; @@ -485,7 +589,7 @@ void upsdrv_updateinfo(void) } else if (ups.timer.start >= 0) { if (online) { - ups.timer.start -= difftime(now, last_poll); + ups.timer.start -= (suseconds_t)(difftime(now, last_poll)); } else { ups.timer.start = ondelay; } @@ -524,12 +628,11 @@ void upsdrv_updateinfo(void) } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } diff --git a/drivers/compaq-mib.c b/drivers/compaq-mib.c index ee618485ec..5857873df8 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.67" #define DEFAULT_ONDELAY "30" #define DEFAULT_OFFDELAY "20" @@ -84,296 +84,89 @@ /* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_ob[] = { - { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OB"), + info_lkp_sentinel }; */ /* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_lb[] = { - { 1, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "LB"), + info_lkp_sentinel }; */ /* Defines for CPQPOWER_OID_POWER_STATUS (1) */ static info_lkp_t cpqpower_pwr_info[] = { - { 1, "" /* other */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OFF" /* none */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "OL" /* normal */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "OL BYPASS" /* bypass */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "OB" /* battery */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "OL BOOST" /* booster */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "OL TRIM" /* reducer */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "OL" /* parallelCapacity */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "OL" /* parallelRedundant */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "OL" /* HighEfficiencyMode */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(1, ""), /* other */ + info_lkp_default(2, "OFF"), /* none */ + info_lkp_default(3, "OL"), /* normal */ + info_lkp_default(4, "OL BYPASS"), /* bypass */ + info_lkp_default(5, "OB"), /* battery */ + info_lkp_default(6, "OL BOOST"), /* booster */ + info_lkp_default(7, "OL TRIM"), /* reducer */ + info_lkp_default(8, "OL"), /* parallelCapacity */ + info_lkp_default(9, "OL"), /* parallelRedundant */ + info_lkp_default(10, "OL"), /* HighEfficiencyMode */ + info_lkp_sentinel +}; static info_lkp_t cpqpower_mode_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "normal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "parallel capacity" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "parallel redundancy" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - {10, "high efficiency" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), + info_lkp_default(2, ""), + info_lkp_default(3, "normal"), + info_lkp_default(4, ""), + info_lkp_default(5, ""), + info_lkp_default(6, ""), + info_lkp_default(7, ""), + info_lkp_default(8, "parallel capacity"), + info_lkp_default(9, "parallel redundancy"), + info_lkp_default(10, "high efficiency"), + info_lkp_sentinel }; static info_lkp_t cpqpower_battery_abm_status[] = { - { 1, "CHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "DISCHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, + info_lkp_default(1, "CHRG"), + info_lkp_default(2, "DISCHRG"), /* - { 3, "Floating" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ -/* { 4, "Resting" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, + info_lkp_default(3, "Floating"), + info_lkp_default(4, "Resting"), + info_lkp_default(5, "Unknown"), */ -/* - { 5, "Unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_sentinel +}; /* Defines for CPQPOWER_OID_UPS_TEST_RES */ static info_lkp_t cpqpower_test_res_info[] = { - { 1, "Unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "Done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "Done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "In progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "Not supported" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "Inhibited" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "Scheduled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(1, "Unknown"), + info_lkp_default(2, "Done and passed"), + info_lkp_default(3, "Done and error"), + info_lkp_default(4, "In progress"), + info_lkp_default(5, "Not supported"), + info_lkp_default(6, "Inhibited"), + info_lkp_default(7, "Scheduled"), + info_lkp_sentinel +}; #define CPQPOWER_START_TEST "1" static info_lkp_t cpqpower_outlet_status_info[] = { - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "pendingOff" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 4, "pendingOn" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 5, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "on"), + info_lkp_default(2, "off"), + info_lkp_default(3, "pendingOff"), /* transitional status */ + info_lkp_default(4, "pendingOn"), /* transitional status */ + info_lkp_default(5, "unknown"), + info_lkp_sentinel }; /* Ugly hack: having the matching OID present means that the outlet is * switchable. So, it should not require this value lookup */ static info_lkp_t cpqpower_outlet_switchability_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "yes"), + info_lkp_default(3, "yes"), + info_lkp_default(4, "yes"), + info_lkp_sentinel }; #define CPQPOWER_OID_SD_AFTER_DELAY ".1.3.6.1.4.1.232.165.3.8.1.0" /* UPS-MIB::upsControlOutputOffDelay */ @@ -382,34 +175,40 @@ static info_lkp_t cpqpower_outlet_switchability_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t cpqpower_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MFR_NAME, "HP/Compaq", SU_FLAG_STATIC, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MODEL_NAME, "SNMP UPS", SU_FLAG_STATIC, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MFR_NAME, "HP/Compaq", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MODEL_NAME, "SNMP UPS", SU_FLAG_STATIC, NULL), /* { "ups.model.aux", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_OEMCODE, "", SU_FLAG_STATIC, NULL },*/ - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.1.2.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.1.2.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: split between firmware and firmware.aux ("00.01.0019;00.01.0004") * UPS Firmware Revision : 00.01.0004 * Communication Board Firmware Revision : 00.01.0019 */ /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_FIRMREV, "", 0, NULL }, - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", 0, NULL }, - { "ups.load", 0, 1.0, CPQPOWER_OID_LOAD_LEVEL, "", 0, NULL }, - { "ups.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER, "", SU_OUTPUT_1, NULL }, - { "ups.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.3.0", "", SU_OUTPUT_1, NULL }, - { "ups.L1.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".1", "", SU_OUTPUT_3, NULL }, - { "ups.L2.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".2", "", SU_OUTPUT_3, NULL }, - { "ups.L3.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, cpqpower_pwr_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_BATT_STATUS, "", SU_STATUS_PWR, cpqpower_battery_abm_status }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_FIRMREV, "", 0, NULL), + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", 0, NULL), + snmp_info_default("ups.load", 0, 1.0, CPQPOWER_OID_LOAD_LEVEL, "", 0, NULL), + snmp_info_default("ups.realpower.nominal", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.3.0", "", SU_OUTPUT_1, NULL), + snmp_info_default("ups.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("ups.L1.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER ".1", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.L2.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER ".2", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.L3.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, cpqpower_pwr_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_BATT_STATUS, "", SU_STATUS_PWR, cpqpower_battery_abm_status), /* The next two lines are no longer supported by MIB ver. 1.76 (Github issue 118) * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_OB, "", SU_STATUS_BATT, cpqpower_alarm_ob }, * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_LB, "", SU_STATUS_BATT, cpqpower_alarm_lb }, */ /* { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_BATT_STATUS, "", SU_STATUS_BATT, ietf_batt_info }, */ /* FIXME: this should use either .1.3.6.1.4.1.232.165.3.11.1.0 (upsTopologyType) * or .1.3.6.1.4.1.232.165.3.11.2.0 (upsTopoMachineCode) */ - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "", SU_STATUS_PWR, cpqpower_mode_info }, - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_UPS_TEST_RES, "", 0, cpqpower_test_res_info }, + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "", SU_STATUS_PWR, cpqpower_mode_info), + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_UPS_TEST_RES, "", 0, cpqpower_test_res_info), /* FIXME: handle ups.date and ups.time * - OID: .1.3.6.1.4.1.232.165.3.9.5.0 * - format MM/DD/YYYY HH:MM:SS */ @@ -423,64 +222,55 @@ static snmp_info_t cpqpower_mib[] = { * flywheel(7) * fuelcell(8) */ - { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.timer.shutdown", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_FLAG_OK, NULL }, - { "ups.timer.start", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_FLAG_OK, NULL }, + snmp_info_default("ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.timer.shutdown", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.timer.start", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_FLAG_OK, NULL), /* Ambient page */ - { "ambient.temperature", 0, 1.0, CPQPOWER_OID_AMBIENT_TEMP, "", 0, NULL }, - { "ambient.temperature.low", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.2.0", "", 0, NULL }, - { "ambient.temperature.high", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.3.0", "", 0, NULL }, + snmp_info_default("ambient.temperature", 0, 1.0, CPQPOWER_OID_AMBIENT_TEMP, "", 0, NULL), + snmp_info_default("ambient.temperature.low", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.2.0", "", 0, NULL), + snmp_info_default("ambient.temperature.high", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.3.0", "", 0, NULL), /* Battery page */ - { "battery.charge", 0, 1.0, CPQPOWER_OID_BATT_CHARGE, "", 0, NULL }, - { "battery.runtime", 0, 1.0, CPQPOWER_OID_BATT_RUNTIME, "", 0, NULL }, - { "battery.voltage", 0, 0.1, CPQPOWER_OID_BATT_VOLTAGE, "", 0, NULL }, - { "battery.current", 0, 0.1, CPQPOWER_OID_BATT_CURRENT, "", 0, NULL }, + snmp_info_default("battery.charge", 0, 1.0, CPQPOWER_OID_BATT_CHARGE, "", 0, NULL), + snmp_info_default("battery.runtime", 0, 1.0, CPQPOWER_OID_BATT_RUNTIME, "", 0, NULL), + snmp_info_default("battery.voltage", 0, 0.1, CPQPOWER_OID_BATT_VOLTAGE, "", 0, NULL), + snmp_info_default("battery.current", 0, 0.1, CPQPOWER_OID_BATT_CURRENT, "", 0, NULL), /* FIXME: need the new variable (for ABM) - { "battery.status", 0, 0.1, ".1.3.6.1.4.1.232.165.3.2.5.0", "", 0, NULL }, */ + snmp_info_default("battery.status", 0, 0.1, ".1.3.6.1.4.1.232.165.3.2.5.0", "", 0, NULL), */ /* Input page */ - { "input.phases", 0, 1.0, CPQPOWER_OID_IN_LINES, "", 0, NULL }, -/* { "input.phase", 0, 1.0, CPQPOWER_OID_IN_PHASE, "", SU_OUTPUT_1, NULL }, */ - { "input.frequency", 0, 0.1, CPQPOWER_OID_IN_FREQ , "", 0, NULL }, - { "input.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE, "", SU_OUTPUT_1, NULL }, - { "input.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.3.4.1.2.1", "", SU_OUTPUT_1, NULL }, - { "input.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.2.0", "", SU_OUTPUT_1, NULL }, - { "input.L1-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".3", "", SU_INPUT_3, NULL }, - { "input.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT, "", SU_OUTPUT_1, NULL }, - { "input.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.3.4.1.3.1", "", SU_OUTPUT_1, NULL }, - - { "input.L1.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".1", "", SU_INPUT_3, NULL }, - { "input.L2.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".2", "", SU_INPUT_3, NULL }, - { "input.L3.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".3", "", SU_INPUT_3, NULL }, - { "input.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER, "", SU_OUTPUT_1, NULL }, - { "input.L1.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".1", "", SU_INPUT_3, NULL }, - { "input.L2.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".2", "", SU_INPUT_3, NULL }, - { "input.L3.realpower", 0, 0.1, CPQPOWER_OID_IN_POWER ".3", "", SU_INPUT_3, NULL }, - { "input.quality", 0, 1.0, CPQPOWER_OID_IN_LINEBADS, "", 0, NULL }, + snmp_info_default("input.phases", 0, 1.0, CPQPOWER_OID_IN_LINES, "", 0, NULL), + snmp_info_default("input.frequency", 0, 0.1, CPQPOWER_OID_IN_FREQ , "", 0, NULL), + snmp_info_default("input.voltage.nominal", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.2.0", "", 0, NULL), + snmp_info_default("input.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("input.L1-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".3", "", SU_INPUT_3, NULL), + snmp_info_default("input.current", 0, 1.0, CPQPOWER_OID_IN_CURRENT ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("input.L1.current", 0, 1.0, CPQPOWER_OID_IN_CURRENT ".1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 1.0, CPQPOWER_OID_IN_CURRENT ".2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 1.0, CPQPOWER_OID_IN_CURRENT ".3", "", SU_INPUT_3, NULL), + snmp_info_default("input.realpower", 0, 1.0, CPQPOWER_OID_IN_POWER ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("input.L1.realpower", 0, 1.0, CPQPOWER_OID_IN_POWER ".1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.realpower", 0, 1.0, CPQPOWER_OID_IN_POWER ".2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.realpower", 0, 1.0, CPQPOWER_OID_IN_POWER ".3", "", SU_INPUT_3, NULL), + snmp_info_default("input.quality", 0, 1.0, CPQPOWER_OID_IN_LINEBADS, "", 0, NULL), /* Output page */ - { "output.phases", 0, 1.0, CPQPOWER_OID_OUT_LINES, "", 0, NULL }, -/* { "output.phase", 0, 1.0, CPQPOWER_OID_OUT_PHASE, "", SU_OUTPUT_1, NULL }, */ - { "output.frequency", 0, 0.1, CPQPOWER_OID_OUT_FREQUENCY, "", 0, NULL }, - /* FIXME: handle multiplier (0.1 there) */ - { "output.frequency.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.4.0", "", SU_OUTPUT_1, NULL }, - { "output.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE, "", SU_OUTPUT_1, NULL }, - { "output.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, - { "output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.1.0", "", SU_OUTPUT_1, NULL }, - { "output.L1-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".2", "", SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".3", "", SU_OUTPUT_3, NULL }, - { "output.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT, "", SU_OUTPUT_1, NULL }, - { "output.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.4.4.1.3.1", "", SU_OUTPUT_1, NULL }, - /* { "output.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.4", "", SU_OUTPUT_1, NULL }, */ - { "output.L1.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".1", "", SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".2", "", SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".3", "", SU_OUTPUT_3, NULL }, + snmp_info_default("output.phases", 0, 1.0, CPQPOWER_OID_OUT_LINES, "", 0, NULL), + snmp_info_default("output.frequency.nominal", 0, 0.1, ".1.3.6.1.4.1.232.165.3.9.4.0", "", 0, NULL), + snmp_info_default("output.frequency", 0, 0.1, CPQPOWER_OID_OUT_FREQUENCY, "", 0, NULL), + snmp_info_default("output.voltage.nominal", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.1.0", "", 0, NULL), + snmp_info_default("output.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("output.L1-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".3", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.current", 0, 1.0, CPQPOWER_OID_OUT_CURRENT ".1", "", SU_OUTPUT_1, NULL), + snmp_info_default("output.L1.current", 0, 1.0, CPQPOWER_OID_OUT_CURRENT ".1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 1.0, CPQPOWER_OID_OUT_CURRENT ".2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 1.0, CPQPOWER_OID_OUT_CURRENT ".3", "", SU_OUTPUT_3, NULL), /* FIXME: what to map with these? * Name/OID: upsConfigLowOutputVoltageLimit.0; Value (Integer): 160 @@ -489,22 +279,24 @@ static snmp_info_t cpqpower_mib[] = { * => input.transfer.high? */ /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, ".1.3.6.1.4.1.232.165.3.10.1.0", "0", 0, NULL }, /* upsNumReceptacles */ + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.232.165.3.10.1.0", "0", 0, NULL), /* upsNumReceptacles */ -/* { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL }, - { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL }, - { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL }, - { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL }, */ +/* + snmp_info_default("outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL), + snmp_info_default("outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL), + snmp_info_default("outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL), + snmp_info_default("outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL), +*/ /* outlet template definition */ /* FIXME always true? */ - { "outlet.%i.switchable", ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &cpqpower_outlet_switchability_info[0] }, - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &cpqpower_outlet_switchability_info[0]), + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL), /* { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL }, */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.3.10.2.1.2.%i", NULL, SU_FLAG_OK | SU_OUTLET, &cpqpower_outlet_status_info[0] }, + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.3.10.2.1.2.%i", NULL, SU_FLAG_OK | SU_OUTLET, &cpqpower_outlet_status_info[0]), /* FIXME: come up with a suitable varname! * - The delay after going On Battery until the Receptacle is automatically turned Off. * A value of -1 means that this Output should never be turned Off automatically, but must be turned Off only by command. @@ -515,27 +307,27 @@ static snmp_info_t cpqpower_mib[] = { */ /* FIXME: also define .stop (as for 'shutdown.reboot') * and .delay */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.3.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.4.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.3.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.4.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL), /* FIXME: also define a .delay or map to "outlet.%i.delay.shutdown" */ - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.7.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.7.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL), /* instant commands. */ /* We need to duplicate load.{on,off} Vs load.{on,off}.delay, since * "0" cancels the shutdown, so we put "1" (second) for immediate off! */ - { "load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "1", SU_TYPE_CMD, NULL }, - { "load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "1", SU_TYPE_CMD, NULL }, - { "shutdown.stop", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "1", SU_TYPE_CMD, NULL), + snmp_info_default("load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "1", SU_TYPE_CMD, NULL), + snmp_info_default("shutdown.stop", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* FIXME: need ups.{timer,delay}.{start,shutdown} param counterparts! */ - { "load.off.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL }, - { "load.on.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL }, - /* { CMD_SHUTDOWN, 0, CPQPOWER_OFF_GRACEFUL, CPQPOWER_OID_OFF, "", 0, NULL }, */ - { "shutdown.reboot", 0, 1, ".1.3.6.1.4.1.232.165.3.8.6.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.battery.start", 0, 1, ".1.3.6.1.4.1.232.165.3.7.1.0", CPQPOWER_START_TEST, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("load.off.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL), + snmp_info_default("load.on.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL), + /* snmp_info_default(CMD_SHUTDOWN, 0, CPQPOWER_OFF_GRACEFUL, CPQPOWER_OID_OFF, "", 0, NULL), */ + snmp_info_default("shutdown.reboot", 0, 1, ".1.3.6.1.4.1.232.165.3.8.6.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("test.battery.start", 0, 1, ".1.3.6.1.4.1.232.165.3.7.1.0", CPQPOWER_START_TEST, SU_TYPE_CMD | SU_FLAG_OK, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t compaq = { "cpqpower", CPQPOWER_MIB_VERSION, NULL, CPQPOWER_OID_MFR_NAME, cpqpower_mib, CPQPOWER_SYSOID, NULL }; diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c index 06fc595bd3..cbe714e787 100644 --- a/drivers/cps-hid.c +++ b/drivers/cps-hid.c @@ -3,6 +3,7 @@ * Copyright (C) * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger + * 2020 - 2024 Jim Klimov * * Note: this subdriver was initially generated as a "stub" by the * gen-usbhid-subdriver script. It must be customized. @@ -25,15 +26,23 @@ #include "main.h" /* for getval() */ #include "nut_float.h" +#include "hidparser.h" /* for FindObject_with_ID_Node() */ #include "usbhid-ups.h" #include "cps-hid.h" #include "usb-common.h" -#define CPS_HID_VERSION "CyberPower HID 0.6" +#define CPS_HID_VERSION "CyberPower HID 0.80" /* Cyber Power Systems */ #define CPS_VENDORID 0x0764 +/* ST Microelectronics */ +#define STMICRO_VENDORID 0x0483 +/* Please note that USB vendor ID 0x0483 is from ST Microelectronics - + * with actual product IDs delegated to different OEMs. + * Devices handled in this driver are marketed under Cyber Energy brand. + */ + /* Values for correcting the HID on some models * where LogMin and LogMax are set incorrectly in the HID. */ @@ -71,6 +80,9 @@ static usb_device_id_t cps_usb_device_table[] = { /* OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U */ { USB_DEVICE(CPS_VENDORID, 0x0601), NULL }, + /* Cyber Energy branded devices by CPS */ + { USB_DEVICE(STMICRO_VENDORID, 0xa430), NULL }, + /* Terminating entry */ { 0, 0, NULL } }; @@ -162,15 +174,18 @@ static usage_tables_t cps_utab[] = { /* --------------------------------------------------------------- */ static hid_info_t cps_hid2nut[] = { - /* { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, */ - /* { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, */ - /* { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, */ - /* { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, */ - /* { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, */ - /* { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* Battery page */ { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_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.iOEMInformation", NULL, "%s", 0, stringid_conversion }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -268,7 +283,7 @@ static int cps_claim(HIDDevice_t *hd) { } } -/* CPS Models like CP900EPFCLCD return a syntactically legal but incorrect +/* CPS Models like CP900EPFCLCD/CP1500PFCLCDa return a syntactically legal but incorrect * Report Descriptor whereby the Input High Transfer Max/Min values * are used for the Output Voltage Usage Item limits. * Additionally the Input Voltage LogMax is set incorrectly for EU models. @@ -276,35 +291,21 @@ 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; int vendorID = pDev->VendorID; int productID = pDev->ProductID; - if (vendorID != CPS_VENDORID || productID != 0x0501) { + if (vendorID != CPS_VENDORID || (productID != 0x0501 && productID != 0x0601)) { + return 0; + } + + if (disable_fix_report_desc) { + upsdebugx(3, + "NOT Attempting Report Descriptor fix for UPS: " + "Vendor: %04x, Product: %04x " + "(got disable_fix_report_desc in config)", + vendorID, productID); return 0; } @@ -315,12 +316,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 +332,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..d7faf39b9f 100644 --- a/drivers/cyberpower-mib.c +++ b/drivers/cyberpower-mib.c @@ -24,217 +24,214 @@ #include "cyberpower-mib.h" -#define CYBERPOWER_MIB_VERSION "0.4" +#define CYBERPOWER_MIB_VERSION "0.55" #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" +/* Per https://github.com/networkupstools/nut/issues/1997 + * some CPS devices offer the shorter vendor OID as sysOID + */ +#define CYBERPOWER_SYSOID2 ".1.3.6.1.4.1.3808" + +/* 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" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "OL BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "NULL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(2, "OL"), /* onLine */ + info_lkp_default(3, "OB"), /* onBattery */ + info_lkp_default(4, "OL BOOST"), /* onBoost */ + info_lkp_default(5, "OFF"), /* onSleep */ + info_lkp_default(6, "OFF"), /* off */ + info_lkp_default(7, "OL"), /* rebooting */ + info_lkp_default(8, "OL"), /* onECO */ + info_lkp_default(9, "OL BYPASS"), /* onBypass */ + info_lkp_default(10, "OL TRIM"), /* onBuck */ + info_lkp_default(11, "OL OVER"), /* onOverload */ + + /* Note: a "NULL" string must be last due to snmp-ups.c parser logic */ + info_lkp_default(1, "NULL"), /* unknown */ + + info_lkp_sentinel +}; static info_lkp_t cyberpower_battery_status[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryNormal */ - { 3, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryLow */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, ""), /* batteryNormal */ + info_lkp_default(3, "LB"), /* batteryLow */ + info_lkp_sentinel +}; static info_lkp_t cyberpower_cal_status[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration Successful */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration Invalid */ - { 3, "CAL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Calibration in progress */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* Calibration Successful */ + info_lkp_default(2, ""), /* Calibration Invalid */ + info_lkp_default(3, "CAL"), /* Calibration in progress */ + info_lkp_sentinel }; static info_lkp_t cyberpower_battrepl_status[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No battery needs replacing */ - { 2, "RB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Batteries need to be replaced */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No battery needs replacing */ + info_lkp_default(2, "RB"), /* Batteries need to be replaced */ + info_lkp_sentinel +}; + +static info_lkp_t cyberpower_ups_alarm_info[] = { + info_lkp_default(1, ""), /* Normal */ + info_lkp_default(2, "Temperature too high!"), /* Overheat */ + info_lkp_default(3, "Internal UPS fault!"), /* Hardware Fault */ + info_lkp_sentinel +}; + +static info_lkp_t cyberpower_transfer_reasons[] = { + info_lkp_default(1, "noTransfer"), + info_lkp_default(2, "highLineVoltage"), + info_lkp_default(3, "brownout"), + info_lkp_default(4, "selfTest"), + info_lkp_sentinel +}; + +static info_lkp_t cyberpower_testdiag_results[] = { + info_lkp_default(1, "Ok"), + info_lkp_default(2, "Failed"), + info_lkp_default(3, "InvalidTest"), + info_lkp_default(4, "TestInProgress"), + info_lkp_sentinel }; /* Snmp2NUT lookup table for CyberPower MIB */ static snmp_info_t cyberpower_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, - - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "CYBERPOWER", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CYBERPOWER_OID_MODEL_NAME, - "CyberPower", SU_FLAG_STATIC, NULL }, - - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.3.0", - "", SU_FLAG_STATIC, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.1.0", - "", SU_FLAG_STATIC, NULL }, - { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.2.0", "", - 0, NULL }, - - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.4.1.1.0", "", - SU_FLAG_OK | SU_STATUS_PWR, &cyberpower_power_status[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.1.1.0", "", - SU_FLAG_OK | SU_STATUS_BATT, &cyberpower_battery_status[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.7.2.7.0", "", - SU_FLAG_OK | SU_STATUS_CAL, &cyberpower_cal_status[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.2.5.0", "", - SU_FLAG_OK | SU_STATUS_RB, &cyberpower_battrepl_status[0] }, - { "ups.load", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.4.2.3.0", "", - 0, NULL }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "CYBERPOWER", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, CYBERPOWER_OID_MODEL_NAME, + "CyberPower", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.3808.1.1.1.1.1.2.0", + "", SU_FLAG_OK | SU_FLAG_STATIC, NULL), + + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.3.0", + "", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.1.0", + "", SU_FLAG_STATIC, NULL), + snmp_info_default("ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.2.0", "", + 0, NULL), + + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.4.1.1.0", "", + SU_FLAG_OK | SU_STATUS_PWR, &cyberpower_power_status[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.1.1.0", "", + SU_FLAG_OK | SU_STATUS_BATT, &cyberpower_battery_status[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.7.2.7.0", "", + SU_FLAG_OK | SU_STATUS_CAL, &cyberpower_cal_status[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.2.5.0", "", + SU_FLAG_OK | SU_STATUS_RB, &cyberpower_battrepl_status[0]), + snmp_info_default("ups.load", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.4.2.3.0", "", + 0, NULL), + + snmp_info_default("ups.temperature", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.10.2.0", "", SU_FLAG_OK, NULL), + + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.10.1.0", "", + SU_FLAG_OK, &cyberpower_ups_alarm_info[0]), /* Battery runtime is expressed in seconds */ - { "battery.runtime", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.4.0", "", - 0, NULL }, + snmp_info_default("battery.runtime", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.4.0", "", + 0, NULL), /* The elapsed time in seconds since the * UPS has switched to battery power */ - { "battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.1.2.0", "", - 0, NULL }, + snmp_info_default("battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.1.2.0", "", + 0, NULL), /* Different generations/models reported "battery.voltage" by different OIDs: */ - { "battery.voltage", 0, 0.1, ".1.3.6.1.2.1.33.1.2.5.0", "", - 0, NULL }, - { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.2.0", "", - 0, NULL }, - { "battery.voltage.nominal", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.8.0", "", - 0, NULL }, + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.2.1.33.1.2.5.0", "", + 0, NULL), + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.2.0", "", + 0, NULL), + snmp_info_default("battery.voltage.nominal", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.8.0", "", + 0, NULL), /* Different generations/models reported "battery.current" by different OIDs: */ - { "battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", - 0, NULL }, - { "battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.7.0", "", - 0, NULL }, - { "battery.charge", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.1.0", "", - 0, NULL }, - { "battery.temperature", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.3.0", "", - 0, NULL }, - - { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.1.0", "", - 0, NULL }, - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.4.0", "", - 0, NULL }, - - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.1.0", "", - 0, NULL }, - { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.2.0", "", - 0, NULL }, - { "output.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", - 0, NULL }, + snmp_info_default("battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", + 0, NULL), + snmp_info_default("battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.7.0", "", + 0, NULL), + snmp_info_default("battery.charge", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.1.0", "", + 0, NULL), + snmp_info_default("battery.temperature", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.3.0", "", + 0, NULL), + /* upsBaseBatteryLastReplaceDate */ + snmp_info_default("battery.date", ST_FLAG_STRING, 8, ".1.3.6.1.4.1.3808.1.1.1.2.1.3.0", "", + SU_FLAG_OK | SU_FLAG_SEMI_STATIC, NULL), + + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.1.0", "", + 0, NULL), + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.4.0", "", + 0, NULL), + /* upsAdvanceInputLineFailCause */ + snmp_info_default("input.transfer.reason", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.3808.1.1.1.3.2.5.0", "", + SU_TYPE_INT | SU_FLAG_OK, &cyberpower_transfer_reasons[0]), + + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.1.0", "", + 0, NULL), + snmp_info_default("output.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.2.0", "", + 0, NULL), + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", + 0, NULL), /* Delays affecting instant commands */ /* upsAdvanceConfigReturnDelay */ - { "ups.delay.start", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.9.0", "0", - SU_FLAG_OK | SU_TYPE_TIME, NULL }, + snmp_info_default("ups.delay.start", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.9.0", "0", + SU_FLAG_OK | SU_TYPE_TIME, NULL), /* Not provided by CPS-MIB */ - { "ups.delay.reboot", 0, 1.0, NULL, "0", - SU_FLAG_OK | SU_FLAG_ABSENT, NULL }, + snmp_info_default("ups.delay.reboot", 0, 1.0, NULL, "0", + SU_FLAG_OK | SU_FLAG_ABSENT, NULL), /* upsAdvanceConfigSleepDelay */ - { "ups.delay.shutdown", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.11.0", "60", - SU_FLAG_OK | SU_TYPE_TIME, NULL }, + snmp_info_default("ups.delay.shutdown", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.11.0", "60", + SU_FLAG_OK | SU_TYPE_TIME, NULL), /* instant commands. */ /* upsAdvanceControlUpsOff */ - { "load.off", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.1.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("load.off", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.2.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceControlTurnOnUPS */ - { "load.on", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("load.on", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceControlUpsOff */ - { "shutdown.stayoff", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("shutdown.stayoff", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), +#if 0 + /* upsBaseControlConserveBattery - note that this command + * is not suitable here because it puts ups to sleep only + * in battery mode. If power is restored during the shutdown + * process, the command is not executed by ups hardware. */ + snmp_info_default("shutdown.return", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), +#endif /* upsAdvanceControlUpsSleep */ - { "shutdown.return", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.6.2.3.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("shutdown.return", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.2.3.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceControlSimulatePowerFail */ - { "test.failure.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.4.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("test.failure.start", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.6.2.4.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceTestIndicators */ - { "test.panel.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.5.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("test.panel.start", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.7.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceTestDiagnostics */ - { "test.battery.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.2.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("test.battery.start", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.7.2.2.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), /* upsAdvanceTestRuntimeCalibration */ - { "calibrate.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "calibrate.stop", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("calibrate.start", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_default("calibrate.stop", 0, 1, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL), + + /* upsAdvanceTestLastDiagnosticsDate */ + snmp_info_default("ups.test.date", ST_FLAG_STRING, 8, ".1.3.6.1.4.1.3808.1.1.1.7.2.4.0", "", + SU_FLAG_OK | SU_FLAG_SEMI_STATIC, NULL), + /* upsAdvanceTestDiagnosticsResults */ + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.7.2.3.0", "", + SU_FLAG_OK, &cyberpower_testdiag_results[0]), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel } ; mib2nut_info_t cyberpower = { "cyberpower", CYBERPOWER_MIB_VERSION, NULL, CYBERPOWER_OID_MODEL_NAME, cyberpower_mib, CYBERPOWER_SYSOID, NULL }; + +mib2nut_info_t cyberpower2 = { "cyberpower", CYBERPOWER_MIB_VERSION, NULL, + CYBERPOWER_OID_MODEL_NAME, cyberpower_mib, CYBERPOWER_SYSOID2, NULL }; diff --git a/drivers/cyberpower-mib.h b/drivers/cyberpower-mib.h index 9db422250e..f8a44d24bd 100644 --- a/drivers/cyberpower-mib.h +++ b/drivers/cyberpower-mib.h @@ -5,5 +5,6 @@ #include "snmp-ups.h" extern mib2nut_info_t cyberpower; +extern mib2nut_info_t cyberpower2; #endif /* CYBERPOWER_MIB_H */ diff --git a/drivers/delta_ups-mib.c b/drivers/delta_ups-mib.c index 9e408b749f..91e21492f9 100644 --- a/drivers/delta_ups-mib.c +++ b/drivers/delta_ups-mib.c @@ -6,7 +6,7 @@ * Note: this subdriver was initially generated as a "stub" by the * gen-snmp-subdriver.sh script. It must be customized! * - * MIB reference: http://www.networkupstools.org/ups-protocols/snmp/DeltaUPSv4.mib + * MIB reference: https://www.networkupstools.org/ups-protocols/snmp/DeltaUPSv4.mib * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,119 +25,49 @@ #include "delta_ups-mib.h" -#define DELTA_UPS_MIB_VERSION "0.4" +#define DELTA_UPS_MIB_VERSION "0.51" #define DELTA_UPS_SYSOID ".1.3.6.1.4.1.2254.2.4" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t delta_onbatt_info[] = { - * { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ static info_lkp_t delta_ups_upstype_info[] = { - { 1, "on-line" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "off-line" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "line-interactive" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "3phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "splite-phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "on-line"), + info_lkp_default(2, "off-line"), + info_lkp_default(3, "line-interactive"), + info_lkp_default(4, "3phase"), + info_lkp_default(5, "split-phase"), + info_lkp_sentinel }; static info_lkp_t delta_ups_pwr_info[] = { - { 0, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* normal */ - { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery */ - { 2, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* bypass */ - { 3, "TRIM" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* reducing */ - { 4, "BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* boosting */ - { 5, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* manualBypass */ + info_lkp_default(0, "OL"), /* normal */ + info_lkp_default(1, "OB"), /* battery */ + info_lkp_default(2, "BYPASS"), /* bypass */ + info_lkp_default(3, "TRIM"), /* reducing */ + info_lkp_default(4, "BOOST"), /* boosting */ + info_lkp_default(5, "BYPASS"), /* manualBypass */ + /* - { 6, "NULL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ /* other */ - { 7, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* none */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -} ; + info_lkp_default(6, "NULL"), +*/ /* other */ + + info_lkp_default(7, "OFF"), /* none */ + info_lkp_sentinel +}; /* DELTA_UPS Snmp2NUT lookup table */ static snmp_info_t delta_ups_mib[] = { /* Data format: - * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * snmp_info_default(info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar), * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo @@ -149,286 +79,291 @@ static snmp_info_t delta_ups_mib[] = { * oid2info: lookup table between OID and NUT values * * Example: - * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, - * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, delta_ups_onbatt_info }, + * snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL), + * snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, delta_ups_onbatt_info), * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t delta_ups_onbatt_info[] = { - * { 1, "OB" }, - * { 2, "OL" }, - * { 0, NULL } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL), /* dupsIdentModel.0 = STRING: "NETYS RT 1/1 UPS" */ - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.2.0", NULL, SU_FLAG_OK, NULL), /* dupsIdentAgentSoftwareVersion.0 = STRING: "2.0h " */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.4.0", NULL, SU_FLAG_OK, NULL), /* dupsIdentUPSSoftwareVersion.0 = STRING: "1.1" */ - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.3.0", NULL, SU_FLAG_OK, NULL), /* dupsType.0 = INTEGER: on-line(1) */ - { "ups.type", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.19.0", NULL, SU_FLAG_OK, delta_ups_upstype_info }, + snmp_info_default("ups.type", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.19.0", NULL, SU_FLAG_OK, delta_ups_upstype_info), /* dupsOutputLoad1.0 = INTEGER: 29 */ - { "ups.load", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.load", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.7.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingOutputVA.0 = INTEGER: 2200 */ - { "ups.power", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.power", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.7.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingOutputVoltage.0 = INTEGER: 230 */ - { "output.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.8.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputVoltage1.0 = INTEGER: 2300 */ - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.4.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingOutputFrequency.0 = INTEGER: 50 */ - { "output.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.9.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputCurrent1.0 = INTEGER: 23 */ - { "output.current", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.5.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingInputVoltage.0 = INTEGER: 230 */ - { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.10.0", NULL, SU_FLAG_OK, NULL), /* dupsInputVoltage1.0 = INTEGER: 2280 */ - { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.3.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingInputFrequency.0 = INTEGER: 50 */ - { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.11.0", NULL, SU_FLAG_OK, NULL), /* dupsInputFrequency1.0 = INTEGER: 499 */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.2.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputSource.0 = INTEGER: normal(0) */ - { "ups.status", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.1.0", NULL, SU_FLAG_OK, delta_ups_pwr_info }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.1.0", NULL, SU_FLAG_OK, delta_ups_pwr_info), /* Remaining unmapped variables. * Mostly the first field (string) is to be changed * Check docs/nut-names.txt for the right variable names */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* dupsIdentName.0 = "" */ - { "unmapped.dupsIdentName", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsIdentName", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.5.0", NULL, SU_FLAG_OK, NULL), /* dupsAttachedDevices.0 = "" */ - { "unmapped.dupsAttachedDevices", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAttachedDevices", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.6.0", NULL, SU_FLAG_OK, NULL), /* dupsRatingBatteryVoltage.0 = INTEGER: 0 */ - { "unmapped.dupsRatingBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsRatingBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.12.0", NULL, SU_FLAG_OK, NULL), /* dupsLowTransferVoltUpBound.0 = INTEGER: 0 Volt */ - { "unmapped.dupsLowTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsLowTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.13.0", NULL, SU_FLAG_OK, NULL), /* dupsLowTransferVoltLowBound.0 = INTEGER: 0 Volt */ - { "unmapped.dupsLowTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsLowTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.14.0", NULL, SU_FLAG_OK, NULL), /* dupsHighTransferVoltUpBound.0 = INTEGER: 0 Volt */ - { "unmapped.dupsHighTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.15.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsHighTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.15.0", NULL, SU_FLAG_OK, NULL), /* dupsHighTransferVoltLowBound.0 = INTEGER: 0 Volt */ - { "unmapped.dupsHighTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.16.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsHighTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.16.0", NULL, SU_FLAG_OK, NULL), /* dupsLowBattTime.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsLowBattTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.17.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsLowBattTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.17.0", NULL, SU_FLAG_OK, NULL), /* dupsOutletRelays.0 = INTEGER: 2 */ - { "unmapped.dupsOutletRelays", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.18.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutletRelays", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.18.0", NULL, SU_FLAG_OK, NULL), /* dupsShutdownType.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsShutdownType", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsShutdownType", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.1.0", NULL, SU_FLAG_OK, NULL), /* dupsAutoReboot.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAutoReboot", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAutoReboot", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.2.0", NULL, SU_FLAG_OK, NULL), /* dupsShutdownAction.0 = INTEGER: 0 */ - { "unmapped.dupsShutdownAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsShutdownAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.3.0", NULL, SU_FLAG_OK, NULL), /* dupsRestartAction.0 = INTEGER: 0 */ - { "unmapped.dupsRestartAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsRestartAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.4.0", NULL, SU_FLAG_OK, NULL), /* dupsSetOutletRelay.0 = INTEGER: 1 */ - { "unmapped.dupsSetOutletRelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsSetOutletRelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.5.0", NULL, SU_FLAG_OK, NULL), /* dupsRelayOffDelay.0 = INTEGER: 0 */ - { "unmapped.dupsRelayOffDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsRelayOffDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.6.0", NULL, SU_FLAG_OK, NULL), /* dupsRelayOnDelay.0 = INTEGER: 0 */ - { "unmapped.dupsRelayOnDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsRelayOnDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.7.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigBuzzerAlarm.0 = INTEGER: alarm(1) */ - { "unmapped.dupsConfigBuzzerAlarm", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigBuzzerAlarm", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.1.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigBuzzerState.0 = INTEGER: disable(2) */ - { "unmapped.dupsConfigBuzzerState", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigBuzzerState", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.2.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigSensitivity.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsConfigSensitivity", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigSensitivity", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.3.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigLowVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsConfigLowVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigLowVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.4.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigHighVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsConfigHighVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigHighVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.5.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigShutdownOSDelay.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsConfigShutdownOSDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigShutdownOSDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.6.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigUPSBootDelay.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsConfigUPSBootDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigUPSBootDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.7.0", NULL, SU_FLAG_OK, NULL), /* dupsConfigExternalBatteryPack.0 = INTEGER: 0 */ - { "unmapped.dupsConfigExternalBatteryPack", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsConfigExternalBatteryPack", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.8.0", NULL, SU_FLAG_OK, NULL), /* dupsInputNumLines.0 = INTEGER: 1 */ - { "unmapped.dupsInputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.1.0", NULL, SU_FLAG_OK, NULL), /* dupsInputCurrent1.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.4.0", NULL, SU_FLAG_OK, NULL), /* dupsInputFrequency2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputFrequency2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputFrequency2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.5.0", NULL, SU_FLAG_OK, NULL), /* dupsInputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.6.0", NULL, SU_FLAG_OK, NULL), /* dupsInputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.7.0", NULL, SU_FLAG_OK, NULL), /* dupsInputFrequency3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputFrequency3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputFrequency3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.8.0", NULL, SU_FLAG_OK, NULL), /* dupsInputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.9.0", NULL, SU_FLAG_OK, NULL), /* dupsInputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsInputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsInputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.10.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputFrequency.0 = INTEGER: 499 0.1 Hertz */ - { "unmapped.dupsOutputFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.2.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputNumLines.0 = INTEGER: 1 */ - { "unmapped.dupsOutputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.3.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputPower1.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.6.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.8.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.9.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputPower2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.10.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputLoad2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputLoad2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputLoad2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.11.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.12.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.13.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputPower3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.14.0", NULL, SU_FLAG_OK, NULL), /* dupsOutputLoad3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsOutputLoad3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.15.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsOutputLoad3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.15.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassFrequency.0 = INTEGER: 499 0.1 Hertz */ - { "unmapped.dupsBypassFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.1.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassNumLines.0 = INTEGER: 1 */ - { "unmapped.dupsBypassNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.2.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassVoltage1.0 = INTEGER: 2280 */ - { "unmapped.dupsBypassVoltage1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassVoltage1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.3.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassCurrent1.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.4.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassPower1.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.5.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassVoltage2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.6.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassCurrent2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.7.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassPower2.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.8.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassVoltage3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.9.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassCurrent3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.10.0", NULL, SU_FLAG_OK, NULL), /* dupsBypassPower3.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBypassPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypassPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.11.0", NULL, SU_FLAG_OK, NULL), /* dupsBypass.12.0 = NULL */ - { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.12.0", NULL, SU_FLAG_OK, NULL), /* dupsBypass.13.0 = NULL */ - { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.13.0", NULL, SU_FLAG_OK, NULL), /* dupsBypass.14.0 = NULL */ - { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.14.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryCondiction.0 = INTEGER: good(0) */ - { "unmapped.dupsBatteryCondiction", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryCondiction", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.1.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryStatus.0 = INTEGER: ok(0) */ - { "unmapped.dupsBatteryStatus", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryStatus", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.2.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryCharge.0 = INTEGER: charging(1) */ - { "unmapped.dupsBatteryCharge", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryCharge", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.3.0", NULL, SU_FLAG_OK, NULL), /* dupsSecondsOnBattery.0 = INTEGER: 0 seconds */ - { "unmapped.dupsSecondsOnBattery", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsSecondsOnBattery", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.4.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryEstimatedTime.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBatteryEstimatedTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryEstimatedTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.5.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryVoltage.0 = INTEGER: 550 0.1 Volt DC */ - { "unmapped.dupsBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.6.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryCurrent.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsBatteryCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.7.0", NULL, SU_FLAG_OK, NULL), /* dupsBatteryCapacity.0 = INTEGER: 100 percent */ - { "unmapped.dupsBatteryCapacity", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsBatteryCapacity", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.8.0", NULL, SU_FLAG_OK, NULL), /* dupsTemperature.0 = INTEGER: 32 degrees Centigrade */ - { "unmapped.dupsTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.9.0", NULL, SU_FLAG_OK, NULL), /* dupsLastReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ - { "unmapped.dupsLastReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsLastReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.10.0", NULL, SU_FLAG_OK, NULL), /* dupsNextReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ - { "unmapped.dupsNextReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsNextReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.11.0", NULL, SU_FLAG_OK, NULL), /* dupsTestType.0 = INTEGER: abort(0) */ - { "unmapped.dupsTestType", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsTestType", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.1.0", NULL, SU_FLAG_OK, NULL), /* dupsTestResultsSummary.0 = INTEGER: noTestsInitiated(0) */ - { "unmapped.dupsTestResultsSummary", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsTestResultsSummary", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.2.0", NULL, SU_FLAG_OK, NULL), /* dupsTestResultsDetail.0 = Wrong Type (should be OCTET STRING): NULL */ - { "unmapped.dupsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.8.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.8.3.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmDisconnect.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmDisconnect", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmDisconnect", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.1.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmPowerFail.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmPowerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmPowerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.2.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmBatteryLow.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmBatteryLow", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmBatteryLow", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.3.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmLoadWarning.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmLoadWarning", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmLoadWarning", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.4.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmLoadSeverity.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmLoadSeverity", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmLoadSeverity", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.5.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmLoadOnBypass.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmLoadOnBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmLoadOnBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.6.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmUPSFault.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmUPSFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmUPSFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.7.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmBatteryGroundFault.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmBatteryGroundFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmBatteryGroundFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.8.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmTestInProgress.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmTestInProgress", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmTestInProgress", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.9.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmBatteryTestFail.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmBatteryTestFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmBatteryTestFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.10.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmFuseFailure.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmFuseFailure", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmFuseFailure", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.11.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOutputOverload.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOutputOverload", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOutputOverload", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.12.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOutputOverCurrent.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmOutputOverCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOutputOverCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.13.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmInverterAbnormal.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmInverterAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.14.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmInverterAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.14.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmRectifierAbnormal.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmRectifierAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.15.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmRectifierAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.15.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmReserveAbnormal.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmReserveAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.16.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmReserveAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.16.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmLoadOnReserve.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsAlarmLoadOnReserve", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.17.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmLoadOnReserve", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.17.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOverTemperature.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOverTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.18.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOverTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.18.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOutputBad.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOutputBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.19.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOutputBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.19.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmBypassBad.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmBypassBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.20.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmBypassBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.20.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmUPSOff.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmUPSOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.21.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmUPSOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.21.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmChargerFail.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmChargerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.22.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmChargerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.22.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmFanFail.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmFanFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.23.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmFanFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.23.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEconomicMode.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEconomicMode", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.24.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmEconomicMode", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.24.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOutputOff.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOutputOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.25.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOutputOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.25.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmSmartShutdown.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmSmartShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.26.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmSmartShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.26.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEmergencyPowerOff.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEmergencyPowerOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.27.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmEmergencyPowerOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.27.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmUPSShutdown.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmUPSShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.28.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmUPSShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.28.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvTemperature.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.1.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvHumidity.0 = Wrong Type (should be INTEGER): NULL */ - { "unmapped.dupsEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.2.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetTemperatureLimit.0 = INTEGER: 40 degrees Centigrade */ - { "unmapped.dupsEnvSetTemperatureLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetTemperatureLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.3.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetHumidityLimit.0 = INTEGER: 90 percentage */ - { "unmapped.dupsEnvSetHumidityLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetHumidityLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.4.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetEnvRelay1.0 = INTEGER: normalOpen(0) */ - { "unmapped.dupsEnvSetEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.5.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetEnvRelay2.0 = INTEGER: normalOpen(0) */ - { "unmapped.dupsEnvSetEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.6.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetEnvRelay3.0 = INTEGER: normalOpen(0) */ - { "unmapped.dupsEnvSetEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.7.0", NULL, SU_FLAG_OK, NULL), /* dupsEnvSetEnvRelay4.0 = INTEGER: normalOpen(0) */ - { "unmapped.dupsEnvSetEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.8.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsEnvSetEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.8.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOverEnvTemperature.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOverEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOverEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.9.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmOverEnvHumidity.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmOverEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmOverEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.10.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEnvRelay1.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.11.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEnvRelay2.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.12.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEnvRelay3.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.13.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dupsAlarmEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.13.0", NULL, SU_FLAG_OK, NULL), /* dupsAlarmEnvRelay4.0 = INTEGER: off(0) */ - { "unmapped.dupsAlarmEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.14.0", NULL, SU_FLAG_OK, NULL }, -#endif /* #if 0 */ + snmp_info_default("unmapped.dupsAlarmEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.14.0", NULL, SU_FLAG_OK, NULL), +#endif /* #if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t delta_ups = { "delta_ups", DELTA_UPS_MIB_VERSION, NULL, NULL, delta_ups_mib, DELTA_UPS_SYSOID, NULL }; diff --git a/drivers/dstate.c b/drivers/dstate.c index 50e4e2283b..e91ef7ac4d 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -23,12 +23,17 @@ #include "config.h" /* must be the first header */ #include +#ifndef WIN32 #include #include #include #include #include #include +#else +#include +#include "wincompat.h" +#endif #include "common.h" #include "dstate.h" @@ -37,8 +42,14 @@ #include "attribute.h" #include "nut_stdint.h" - static int sockfd = -1, stale = 1, alarm_active = 0, ignorelb = 0; + static TYPE_FD sockfd = ERROR_FD; +#ifndef WIN32 static char *sockfn = NULL; +#else + static OVERLAPPED connect_overlapped; + static char *pipename = NULL; +#endif + static int stale = 1, alarm_active = 0, ignorelb = 0; static char status_buf[ST_MAX_VALUE_LEN], alarm_buf[ST_MAX_VALUE_LEN]; static st_tree_t *dtree_root = NULL; static conn_t *connhead = NULL; @@ -46,6 +57,7 @@ struct ups_handler upsh; +#ifndef WIN32 /* this may be a frequent stumbling point for new users, so be verbose here */ static void sock_fail(const char *fn) __attribute__((noreturn)); @@ -53,7 +65,7 @@ static void sock_fail(const char *fn) static void sock_fail(const char *fn) { int sockerr; - struct passwd *user; + struct passwd *pwuser; /* save this so it doesn't get overwritten */ sockerr = errno; @@ -63,9 +75,9 @@ static void sock_fail(const char *fn) printf("\nFatal error: unable to create listener socket\n\n"); printf("bind %s failed: %s\n", fn, strerror(sockerr)); - user = getpwuid(getuid()); + pwuser = getpwuid(getuid()); - if (!user) { + if (!pwuser) { fatal_with_errno(EXIT_FAILURE, "getpwuid"); } @@ -74,7 +86,7 @@ static void sock_fail(const char *fn) { case EACCES: printf("\nCurrent user: %s (UID %d)\n\n", - user->pw_name, (int)user->pw_uid); + pwuser->pw_name, (int)pwuser->pw_uid); printf("Things to try:\n\n"); printf(" - set different owners or permissions on %s\n\n", @@ -103,15 +115,21 @@ static void sock_fail(const char *fn) printf("\n"); fatalx(EXIT_FAILURE, "Exiting."); } +#endif -static int sock_open(const char *fn) +static TYPE_FD sock_open(const char *fn) { - int ret, fd; + TYPE_FD fd; + +#ifndef WIN32 + int ret; struct sockaddr_un ssaddr; + check_unix_socket_filename(fn); + fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { + if (INVALID_FD(fd)) { fatal_with_errno(EXIT_FAILURE, "Can't create a unix domain socket"); } @@ -144,12 +162,55 @@ static int sock_open(const char *fn) fatal_with_errno(EXIT_FAILURE, "listen(%d, %d) failed", fd, DS_LISTEN_BACKLOG); } +#else /* WIN32 */ + + fd = CreateNamedPipe( + fn, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + ST_SOCK_BUF_LEN, /* output buffer size */ + ST_SOCK_BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (INVALID_FD(fd)) { + fatal_with_errno(EXIT_FAILURE, + "Can't create a state socket (windows named pipe)"); + } + + /* Prepare an async wait on a connection on the pipe */ + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(fd,&connect_overlapped); +#endif + return fd; } static void sock_disconnect(conn_t *conn) { +#ifndef WIN32 close(conn->fd); +#else + /* FIXME not sure if this is the right way to close a connection */ + if( conn->read_overlapped.hEvent != INVALID_HANDLE_VALUE) { + CloseHandle(conn->read_overlapped.hEvent); + conn->read_overlapped.hEvent = INVALID_HANDLE_VALUE; + } + DisconnectNamedPipe(conn->fd); +#endif pconf_finish(&conn->ctx); @@ -209,12 +270,54 @@ static void send_to_all(const char *fmt, ...) for (conn = connhead; conn; conn = cnext) { cnext = conn->next; + if (conn->nobroadcast) + continue; +#ifndef WIN32 ret = write(conn->fd, buf, buflen); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (conn->fd, buf, buflen, &bytesWritten, NULL); + if( result == 0 ) { + upsdebugx(2, "write failed on handle %p, disconnecting", conn->fd); + sock_disconnect(conn); + continue; + } + else { + ret = (ssize_t)bytesWritten; + } +#endif if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); +#ifndef WIN32 + upsdebugx(0, "WARNING: %s: write %" PRIiSIZE " bytes to " + "socket %d failed (ret=%" PRIiSIZE "), disconnecting: %s", + __func__, buflen, (int)conn->fd, ret, strerror(errno)); +#else + upsdebugx(0, "WARNING: %s: write %" PRIiSIZE " bytes to " + "handle %p failed (ret=%" PRIiSIZE "), disconnecting: %s", + __func__, buflen, conn->fd, ret, strerror(errno)); +#endif + 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 %" PRIiSIZE " bytes to socket %d succeeded " + "(ret=%" PRIiSIZE "): %s", + __func__, buflen, conn->fd, ret, buf); } } } @@ -225,6 +328,10 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) va_list ap; char buf[ST_SOCK_BUF_LEN]; size_t buflen; +#ifdef WIN32 + DWORD bytesWritten = 0; + BOOL result = FALSE; +#endif va_start(ap, fmt); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL @@ -258,21 +365,105 @@ 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 %" PRIiSIZE " bytes to socket %d: %s", + __func__, buflen, conn->fd, buf); +*/ + +#ifndef WIN32 + ret = write(conn->fd, buf, buflen); +#else + result = WriteFile (conn->fd, buf, buflen, &bytesWritten, NULL); + if( result == 0 ) { + ret = 0; + } + else { + ret = (ssize_t)bytesWritten; + } +#endif + + if (ret < 0) { + /* Hacky bugfix: throttle down for upsd to read that */ +#ifndef WIN32 + upsdebugx(1, "%s: had to throttle down to retry " + "writing %" PRIiSIZE " bytes to socket %d " + "(ret=%" PRIiSIZE ", errno=%d, strerror=%s): %s", + __func__, buflen, (int)conn->fd, + ret, errno, strerror(errno), + buf); +#else + upsdebugx(1, "%s: had to throttle down to retry " + "writing %" PRIiSIZE " bytes to handle %p " + "(ret=%" PRIiSIZE ", errno=%d, strerror=%s): %s", + __func__, buflen, conn->fd, + ret, errno, strerror(errno), + buf); +#endif + usleep(200); +#ifndef WIN32 + ret = write(conn->fd, buf, buflen); +#else + result = WriteFile (conn->fd, buf, buflen, &bytesWritten, NULL); + if( result == 0 ) { + ret = 0; + } + else { + ret = (ssize_t)bytesWritten; + } +#endif + 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); +#ifndef WIN32 + upsdebugx(0, "WARNING: %s: write %" PRIiSIZE " bytes to " + "socket %d failed (ret=%" PRIiSIZE "), disconnecting: %s", + __func__, buflen, (int)conn->fd, ret, strerror(errno)); +#else + upsdebugx(0, "WARNING: %s: write %" PRIiSIZE " bytes to " + "handle %p failed (ret=%" PRIiSIZE "), disconnecting: %s", + __func__, buflen, conn->fd, ret, strerror(errno)); +#endif + 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 { +#ifndef WIN32 + upsdebugx(6, "%s: write %" PRIiSIZE " bytes to socket %d succeeded " + "(ret=%" PRIiSIZE "): %s", + __func__, buflen, conn->fd, ret, buf); +#else + upsdebugx(6, "%s: write %" PRIiSIZE " bytes to handle %p succeeded " + "(ret=%" PRIiSIZE "): %s", + __func__, buflen, conn->fd, ret, buf); +#endif } return 1; /* OK */ } -static void sock_connect(int sock) +static void sock_connect(TYPE_FD sock) { - int fd, ret; conn_t *conn; + +#ifndef WIN32 + int ret; + int fd; + struct sockaddr_un sa; #if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int salen; @@ -282,13 +473,20 @@ static void sock_connect(int sock) salen = sizeof(sa); fd = accept(sock, (struct sockaddr *) &sa, &salen); - if (fd < 0) { + if (INVALID_FD(fd)) { upslog_with_errno(LOG_ERR, "accept on unix fd failed"); 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,10 +503,72 @@ 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; +#else /* WIN32 */ + + /* We have detected a connection on the opened pipe. + * So we start by saving its handle and creating + * a new pipe for future connection */ + conn = xcalloc(1, sizeof(*conn)); + conn->fd = sock; + + /* sockfd is the handle of the connection pending pipe */ + sockfd = CreateNamedPipe( + pipename, /* pipe name */ + PIPE_ACCESS_DUPLEX | /* read/write access */ + FILE_FLAG_OVERLAPPED, /* async IO */ + PIPE_TYPE_BYTE | + PIPE_READMODE_BYTE | + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, /* max. instances */ + ST_SOCK_BUF_LEN, /* output buffer size */ + ST_SOCK_BUF_LEN, /* input buffer size */ + 0, /* client time-out */ + NULL); /* FIXME: default security attribute */ + + if (INVALID_FD(sockfd)) { + fatal_with_errno(EXIT_FAILURE, + "Can't create a state socket (windows named pipe)"); + } + + /* Prepare a new async wait for a connection on the pipe */ + CloseHandle(connect_overlapped.hEvent); + memset(&connect_overlapped,0,sizeof(connect_overlapped)); + connect_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(connect_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + /* Wait for a connection */ + ConnectNamedPipe(sockfd,&connect_overlapped); + + /* A new pipe waiting for new client connection has been created. We could manage the current connection now */ + /* Start a read operation on the newly connected pipe so we could wait on the event associated to this IO */ + memset(&conn->read_overlapped,0,sizeof(conn->read_overlapped)); + memset(conn->buf,0,sizeof(conn->buf)); + conn->read_overlapped.hEvent = CreateEvent(NULL, /*Security*/ + FALSE, /* auto-reset*/ + FALSE, /* inital state = non signaled*/ + NULL /* no name*/); + if(conn->read_overlapped.hEvent == NULL ) { + fatal_with_errno(EXIT_FAILURE, "Can't create event"); + } + + ReadFile (conn->fd, conn->buf, + sizeof(conn->buf) - 1, /* -1 to be sure to have a trailling 0 */ + NULL, &(conn->read_overlapped)); +#endif + + conn->nobroadcast = 0; pconf_init(&conn->ctx, NULL); if (connhead) { @@ -318,7 +578,12 @@ static void sock_connect(int sock) connhead = conn; +#ifndef WIN32 upsdebugx(3, "new connection on fd %d", fd); +#else + upsdebugx(3, "new connection on handle %p", sock); +#endif + } static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) @@ -414,13 +679,20 @@ static void send_tracking(conn_t *conn, const char *id, int value) static int sock_arg(conn_t *conn, size_t numarg, char **arg) { +#ifdef WIN32 + char *sockfn = pipename; /* Just for the report below; not a global var in WIN32 builds */ +#endif + + upsdebugx(6, "Driver on %s is now handling %s with %" PRIuSIZE " args", + sockfn, numarg ? arg[0] : "", numarg); + if (numarg < 1) { return 0; } if (!strcasecmp(arg[0], "DUMPALL")) { - /* first thing: the staleness flag */ + /* first thing: the staleness flag (see also below) */ if ((stale == 1) && !send_to_one(conn, "DATASTALE\n")) { return 1; } @@ -446,6 +718,40 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) return 1; } + if (!strcasecmp(arg[0], "NOBROADCAST")) { + char buf[SMALLBUF]; + conn->nobroadcast = 1; +#ifndef WIN32 + snprintf(buf, sizeof(buf), "socket %d", conn->fd); +#else + snprintf(buf, sizeof(buf), "handle %p", conn->fd); +#endif + upsdebugx(1, "%s: %s requested NOBROADCAST mode", + __func__, buf); + return 1; + } + + /* BROADCAST <0|1> */ + if (!strcasecmp(arg[0], "BROADCAST")) { + int i; + char buf[SMALLBUF]; + conn->nobroadcast = 0; + if (numarg > 1 && str_to_int(arg[1], &i, 10)) { + if (i < 1) + conn->nobroadcast = 1; + } +#ifndef WIN32 + snprintf(buf, sizeof(buf), "socket %d", conn->fd); +#else + snprintf(buf, sizeof(buf), "handle %p", conn->fd); +#endif + upsdebugx(1, + "%s: %s requested %sBROADCAST mode", + __func__, buf, + conn->nobroadcast ? "NO" : ""); + return 1; + } + if (numarg < 2) { return 0; } @@ -473,17 +779,34 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) if (cmdid) upsdebugx(3, "%s: TRACKING = %s", __func__, cmdid); - /* try the new handler first if present */ + /* try the handler shared by all drivers first */ + ret = main_instcmd(cmdname, cmdparam, conn); + if (ret != STAT_INSTCMD_UNKNOWN) { + /* The command was acknowledged by shared handler, and + * either handled successfully, or failed, or was not + * valid in current circumstances - in any case, we do + * not pass to driver-provided logic. */ + + /* send back execution result if requested */ + if (cmdid) + send_tracking(conn, cmdid, ret); + + /* The command was handled, status is a separate consideration */ + return 1; + } /* else try other handler(s) */ + + /* try the driver-provided handler if present */ if (upsh.instcmd) { ret = upsh.instcmd(cmdname, cmdparam); - /* send back execution result */ + /* send back execution result if requested */ if (cmdid) send_tracking(conn, cmdid, ret); /* The command was handled, status is a separate consideration */ return 1; } + upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); return 1; } @@ -510,11 +833,27 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) upsdebugx(3, "%s: TRACKING = %s", __func__, setid); } - /* try the new handler first if present */ + /* try the handler shared by all drivers first */ + ret = main_setvar(arg[1], arg[2], conn); + if (ret != STAT_SET_UNKNOWN) { + /* The command was acknowledged by shared handler, and + * either handled successfully, or failed, or was not + * valid in current circumstances - in any case, we do + * not pass to driver-provided logic. */ + + /* send back execution result if requested */ + if (setid) + send_tracking(conn, setid, ret); + + /* The command was handled, status is a separate consideration */ + return 1; + } /* else try other handler(s) */ + + /* try the driver-provided handler if present */ if (upsh.setvar) { ret = upsh.setvar(arg[1], arg[2]); - /* send back execution result */ + /* send back execution result if requested */ if (setid) send_tracking(conn, setid, ret); @@ -533,6 +872,8 @@ static int sock_arg(conn_t *conn, size_t numarg, char **arg) static void sock_read(conn_t *conn) { ssize_t ret, i; + +#ifndef WIN32 char buf[SMALLBUF]; ret = read(conn->fd, buf, sizeof(buf)); @@ -549,6 +890,24 @@ static void sock_read(conn_t *conn) return; } } +#else + char *buf = conn->buf; + DWORD bytesRead; + BOOL res; + res = GetOverlappedResult(conn->fd, &conn->read_overlapped, &bytesRead, FALSE); + if( res == 0 ) { + upslogx(LOG_INFO, "Read error : %d",(int)GetLastError()); + sock_disconnect(conn); + return; + } + ret = bytesRead; + + /* Special case for signals */ + if (!strncmp(conn->buf, COMMAND_STOP, sizeof(COMMAND_STOP))) { + set_exit_flag(1); + return; + } +#endif for (i = 0; i < ret; i++) { @@ -574,21 +933,33 @@ static void sock_read(conn_t *conn) return; } } + +#ifdef WIN32 + /* Restart async read */ + memset(conn->buf,0,sizeof(conn->buf)); + ReadFile(conn->fd,conn->buf,sizeof(conn->buf)-1,NULL,&(conn->read_overlapped)); /* -1 to be sure to have a trailling 0 */ +#endif } static void sock_close(void) { conn_t *conn, *cnext; - if (sockfd != -1) { + if (VALID_FD(sockfd)) { +#ifndef WIN32 close(sockfd); - sockfd = -1; if (sockfn) { unlink(sockfn); free(sockfn); sockfn = NULL; } +#else + FlushFileBuffers(sockfd); + CloseHandle(sockfd); +#endif + + sockfd = ERROR_FD; } for (conn = connhead; conn; conn = cnext) { @@ -602,10 +973,11 @@ 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]; +#ifndef WIN32 /* do this here for now */ #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) # pragma GCC diagnostic push @@ -621,30 +993,47 @@ void dstate_init(const char *prog, const char *devname) } else { snprintf(sockname, sizeof(sockname), "%s/%s", dflt_statepath(), prog); } +#else + /* upsname (and so devname) is now mandatory so no need to test it */ + snprintf(sockname, sizeof(sockname), "\\\\.\\pipe\\%s-%s", prog, devname); + pipename = xstrdup(sockname); +#endif sockfd = sock_open(sockname); +#ifndef WIN32 upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, sockfd); +#else + upsdebugx(2, "dstate_init: sock %s open on handle %p", sockname, sockfd); +#endif + + /* NOTE: Caller must free this string */ + return xstrdup(sockname); } /* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ -int dstate_poll_fds(struct timeval timeout, int extrafd) +int dstate_poll_fds(struct timeval timeout, TYPE_FD arg_extrafd) { - int ret, maxfd, overrun = 0; - fd_set rfds; + int maxfd = 0; /* Unidiomatic use vs. "sockfd" below, which is "int" on non-WIN32 */ + int overrun = 0; + conn_t *conn; struct timeval now; - conn_t *conn, *cnext; + +#ifndef WIN32 + int ret; + fd_set rfds; + conn_t *cnext; FD_ZERO(&rfds); FD_SET(sockfd, &rfds); maxfd = sockfd; - if (extrafd != -1) { - FD_SET(extrafd, &rfds); + if (VALID_FD(arg_extrafd)) { + FD_SET(arg_extrafd, &rfds); - if (extrafd > maxfd) { - maxfd = extrafd; + if (arg_extrafd > maxfd) { + maxfd = arg_extrafd; } } @@ -707,13 +1096,101 @@ int dstate_poll_fds(struct timeval timeout, int extrafd) } /* tell the caller if that fd woke up */ - if ((extrafd != -1) && (FD_ISSET(extrafd, &rfds))) { + if (VALID_FD(arg_extrafd) && (FD_ISSET(arg_extrafd, &rfds))) { return 1; } +#else /* WIN32 */ + + DWORD ret; + HANDLE rfds[32]; + DWORD timeout_ms; + + /* FIXME: Should such table (and limit) be used in reality? */ + NUT_UNUSED_VARIABLE(arg_extrafd); +/* + if (VALID_FD(arg_extrafd)) { + rfds[maxfd] = arg_extrafd; + maxfd++; + } +*/ + + gettimeofday(&now, NULL); + + /* number of microseconds should always be positive */ + if (timeout.tv_usec < now.tv_usec) { + timeout.tv_sec -= 1; + timeout.tv_usec += 1000000; + } + + if (timeout.tv_sec < now.tv_sec) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + overrun = 1; /* no time left */ + } else { + timeout.tv_sec -= now.tv_sec; + timeout.tv_usec -= now.tv_usec; + } + + timeout_ms = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000); + + /* Wait on the read IO of each connections */ + for (conn = connhead; conn; conn = conn->next) { + rfds[maxfd] = conn->read_overlapped.hEvent; + maxfd++; + } + /* Add the connect event */ + rfds[maxfd] = connect_overlapped.hEvent; + maxfd++; + + ret = WaitForMultipleObjects( + maxfd, /* number of objects in array */ + rfds, /* array of objects */ + FALSE, /* wait for any object */ + timeout_ms); /* timeout in millisecond */ + + if (ret == WAIT_TIMEOUT) { + return 1; /* timer expired */ + } + + if (ret == WAIT_FAILED) { + upslog_with_errno(LOG_ERR, "waitfor failed"); + return overrun; + } + + /* Retrieve the signaled connection */ + for (conn = connhead; conn != NULL; conn = conn->next) { + if( conn->read_overlapped.hEvent == rfds[ret-WAIT_OBJECT_0]) { + break; + } + } + + /* the connection event handle has been signaled */ + if (rfds[ret] == connect_overlapped.hEvent) { + sock_connect(sockfd); + } + /* one of the read event handle has been signaled */ + else { + if (conn != NULL) { + sock_read(conn); + } + } + + /* tell the caller if that fd woke up */ +/* + if (VALID_FD(arg_extrafd) && (ret == arg_extrafd)) { + return 1; + } +*/ +#endif + return overrun; } +/****************************************************************** + * COMMON + ******************************************************************/ + int dstate_setinfo(const char *var, const char *fmt, ...) { int ret; @@ -923,6 +1400,20 @@ int dstate_delinfo(const char *var) return ret; } +int dstate_delinfo_olderthan(const char *var, const st_tree_timespec_t *cutoff) +{ + int ret; + + ret = state_delinfo_olderthan(&dtree_root, var, cutoff); + + /* update listeners */ + if (ret == 1) { + send_to_all("DELINFO %s\n", var); + } + + return ret; +} + int dstate_delenum(const char *var, const char *val) { int ret; @@ -1101,11 +1592,13 @@ void alarm_set(const char *buf) * Note: LARGEBUF was the original limit mismatched vs alarm_buf * size before PR #986. */ - char alarm_tmp[LARGEBUF]; + char alarm_tmp[LARGEBUF]; + int ibuflen; + size_t buflen; + memset(alarm_tmp, 0, sizeof(alarm_tmp)); /* A bit of complexity to keep both (int)snprintf(...) and (size_t)sizeof(...) happy */ - int ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); - size_t buflen; + ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); if (ibuflen < 0) { alarm_tmp[0] = 'N'; alarm_tmp[1] = '/'; @@ -1141,10 +1634,12 @@ void alarm_set(const char *buf) upslogx(LOG_ERR, "%s: error setting alarm_buf to: %s%s", __func__, alarm_tmp, ( (buflen < sizeof(alarm_tmp)) ? "" : "..." ) ); } else if ((size_t)ret > sizeof(alarm_buf)) { - char alarm_tmp[LARGEBUF]; + char alarm_tmp[LARGEBUF]; + int ibuflen; + size_t buflen; + memset(alarm_tmp, 0, sizeof(alarm_tmp)); - int ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); - size_t buflen; + ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); if (ibuflen < 0) { alarm_tmp[0] = 'N'; alarm_tmp[1] = '/'; @@ -1171,7 +1666,7 @@ void alarm_set(const char *buf) } } upslogx(LOG_WARNING, "%s: result was truncated while setting or appending " - "alarm_buf (limited to %zu bytes), with message: %s%s", + "alarm_buf (limited to %" PRIuSIZE " bytes), with message: %s%s", __func__, sizeof(alarm_buf), alarm_tmp, ( (buflen < sizeof(alarm_tmp)) ? "" : "..." )); } @@ -1449,9 +1944,11 @@ static int dstate_tree_dump(const st_tree_t *node) /* Public interface */ void dstate_dump(void) { + const st_tree_t *node; + upsdebugx(3, "Entering %s", __func__); - const st_tree_t *node = (const st_tree_t *)dstate_getroot(); + node = (const st_tree_t *)dstate_getroot(); dstate_tree_dump(node); } diff --git a/drivers/dstate.h b/drivers/dstate.h index 76a645de3c..2fa754defa 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -22,6 +22,7 @@ #ifndef DSTATE_H_SEEN #define DSTATE_H_SEEN 1 +#include "common.h" #include "timehead.h" #include "state.h" #include "attribute.h" @@ -29,6 +30,10 @@ #include "parseconf.h" #include "upshandler.h" +#ifdef WIN32 +# include "wincompat.h" +#endif + #define DS_LISTEN_BACKLOG 16 #define DS_MAX_READ 256 /* don't read forever from upsd */ @@ -38,20 +43,27 @@ /* track client connections */ typedef struct conn_s { - int fd; + TYPE_FD fd; +#ifdef WIN32 + char buf[LARGEBUF]; + OVERLAPPED read_overlapped; +#endif PCONF_CTX_t ctx; struct conn_s *prev; struct conn_s *next; + int nobroadcast; /* connections can request to ignore send_to_all() updates */ } conn_t; +#include "main.h" /* for set_exit_flag(); uses conn_t itself */ + extern struct ups_handler upsh; /* asynchronous (nonblocking) Vs synchronous (blocking) I/O * Defaults to nonblocking, for backward compatibility */ extern int do_synchronous; -void dstate_init(const char *prog, const char *devname); -int dstate_poll_fds(struct timeval timeout, int extrafd); +char * dstate_init(const char *prog, const char *devname); +int dstate_poll_fds(struct timeval timeout, TYPE_FD extrafd); int dstate_setinfo(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int dstate_addenum(const char *var, const char *fmt, ...) @@ -63,6 +75,7 @@ void dstate_delflags(const char *var, const int delflags); void dstate_setaux(const char *var, long aux); const char *dstate_getinfo(const char *var); void dstate_addcmd(const char *cmdname); +int dstate_delinfo_olderthan(const char *var, const st_tree_timespec_t *cutoff); int dstate_delinfo(const char *var); int dstate_delenum(const char *var, const char *val); int dstate_delrange(const char *var, const int min, const int max); diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 857f5d5e23..7c3a435f38 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -2,6 +2,7 @@ Copyright (C) 2005 - 2015 Arnaud Quette + 2014 - 2023 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 @@ -31,18 +32,23 @@ #include "config.h" /* must be the first header */ +#ifndef WIN32 #include #include #include +#endif + +#include #include #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include "upsclient.h" #include "dummy-ups.h" #define DRIVER_NAME "Device simulation and repeater driver" -#define DRIVER_VERSION "0.15" +#define DRIVER_VERSION "0.18" /* driver description structure */ upsdrv_info_t upsdrv_info = @@ -54,22 +60,46 @@ 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, + + /* 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 int mode = MODE_NONE; +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 static int setvar(const char *varname, const char *val); static int instcmd(const char *cmdname, const char *extra); -static int parse_data_file(int upsfd); +static int parse_data_file(TYPE_FD arg_upsfd); static dummy_info_t *find_info(const char *varname); static int is_valid_data(const char* varname); static int is_valid_value(const char* varname, const char *value); @@ -79,7 +109,10 @@ 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; + +/* repeater mode parameters */ +static int repeater_disable_strict_start = 0; /* Driver functions */ @@ -89,7 +122,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++ ) { @@ -100,7 +134,7 @@ void upsdrv_initinfo(void) /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING) - dstate_setaux(item->info_type, item->info_len); + dstate_setaux(item->info_type, (long)item->info_len); } } @@ -113,6 +147,7 @@ void upsdrv_initinfo(void) dstate_dataok(); break; + case MODE_META: case MODE_REPEATER: /* Obtain the target name */ @@ -124,7 +159,17 @@ void upsdrv_initinfo(void) ups = xmalloc(sizeof(*ups)); if (upscli_connect(ups, hostname, port, UPSCLI_CONN_TRYSSL) < 0) { - fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); + if(repeater_disable_strict_start == 1) + { + upslogx(LOG_WARNING, "Warning: %s", upscli_strerror(ups)); + } + else + { + fatalx(EXIT_FAILURE, "Error: %s. " + "Any errors encountered starting the repeater mode result in driver termination, " + "perhaps you want to set the 'repeater_disable_strict_start' option?" + , upscli_strerror(ups)); + } } else { @@ -137,19 +182,86 @@ void upsdrv_initinfo(void) { fatalx(EXIT_FAILURE, "Error: upsd is too old to support this query"); } - fatalx(EXIT_FAILURE, "Error: %s", upscli_strerror(ups)); + + if(repeater_disable_strict_start == 1) + { + upslogx(LOG_WARNING, "Warning: %s", upscli_strerror(ups)); + } + else + { + fatalx(EXIT_FAILURE, "Error: %s. " + "Any errors encountered starting the repeater mode result in driver termination, " + "perhaps you want to set the 'repeater_disable_strict_start' option?" + , upscli_strerror(ups)); + } } /* 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; dstate_addcmd("load.off"); } +static int prepare_filepath(char *fn, size_t buflen) +{ + /* Note: device_path is a global variable, + * the "port=..." value parsed in main.c */ + if (device_path[0] == '/' +#ifdef WIN32 + || device_path[1] == ':' /* "C:\..." */ +#endif + ) { + /* absolute path */ + return snprintf(fn, buflen, "%s", device_path); + } else if (device_path[0] == '.') { + /* "./" or "../" e.g. via CLI, relative to current working + * directory of the driver process... at this moment */ + if (getcwd(fn, buflen)) { + return snprintf(fn + strlen(fn), buflen - strlen(fn), "/%s", device_path); + } else { + return snprintf(fn, buflen, "%s", device_path); + } + } else { + /* assumed to be a filename in NUT config file path + * (possibly under, with direct use of dirname without dots) + * Note that we do not fiddle with file-path separator, + * modern Windows (at least via MinGW/MSYS2) supports + * the POSIX slash. + */ + return snprintf(fn, buflen, "%s/%s", confpath(), device_path); + } +} + void upsdrv_updateinfo(void) { upsdebugx(1, "upsdrv_updateinfo..."); @@ -158,11 +270,64 @@ 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]; + + prepare_filepath(fn, sizeof(fn)); + + /* Determine if file modification timestamp has changed + * since last use (so we would want to re-read it) */ +#ifndef WIN32 + /* Either successful stat (zero return) is OK to + * fill the "fs" struct. Note that currently + * "upsfd" is a no-op for files, they are re-opened + * and re-parsed every time so callers can modify + * the data without complications. + */ + if ( (INVALID_FD(upsfd) || 0 != fstat (upsfd, &fs)) && 0 != stat (fn, &fs)) +#else + /* Consider GetFileAttributesEx() for WIN32_FILE_ATTRIBUTE_DATA? + * https://stackoverflow.com/questions/8991192/check-the-file-size-without-opening-file-in-c/8991228#8991228 + */ + if (0 != stat (fn, &fs)) +#endif + { + upsdebugx(2, "%s: MODE_DUMMY_ONCE: Can't stat %s currently", __func__, fn); + /* retry ASAP until we get a file */ + memset(&datafile_stat, 0, sizeof(struct stat)); + next_update = 1; + } else { + if (datafile_stat.st_mtime != fs.st_mtime) { + upsdebugx(2, + "%s: MODE_DUMMY_ONCE: input file was already read once " + "to the end, but changed later - re-reading: %s", + __func__, fn); + /* updated file => retry ASAP */ + next_update = 1; + datafile_stat = fs; + } + } + } + + if (ctx == NULL && next_update == -1) { + upsdebugx(2, "%s: MODE_DUMMY_ONCE: NO-OP: input file was already read once to the end", __func__); + 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,18 +348,43 @@ void upsdrv_updateinfo(void) } } break; + case MODE_NONE: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } static int instcmd(const char *cmdname, const char *extra) @@ -220,13 +410,31 @@ 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 */ + addvar(VAR_FLAG, "repeater_disable_strict_start", "Do not terminate the driver encountering errors when starting the repeater mode"); } 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 +442,113 @@ 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 + } + + prepare_filepath(fn, sizeof(fn)); + + /* Update file modification timestamp (and other data) */ +#ifndef WIN32 + /* Either successful stat (zero return) is OK to fill the + * "datafile_stat" struct. Note that currently "upsfd" is + * a no-op for files, they are re-opened and re-parsed + * every time so callers can modify the data without + * complications. + */ + if ( (INVALID_FD(upsfd) || 0 != fstat (upsfd, &datafile_stat)) && 0 != stat (fn, &datafile_stat)) +#else + /* Consider GetFileAttributesEx() for WIN32_FILE_ATTRIBUTE_DATA? + * https://stackoverflow.com/questions/8991192/check-the-file-size-without-opening-file-in-c/8991228#8991228 + */ + if (0 != stat (fn, &datafile_stat)) +#endif + { + upsdebugx(2, "%s: Can't stat %s (%s) currently", __func__, device_path, fn); + } else { + upsdebugx(2, "Located %s for device simulation data: %s", device_path, fn); + } + } + if (testvar("repeater_disable_strict_start")) + { + repeater_disable_strict_start = 1; } } @@ -311,7 +623,7 @@ static int setvar(const char *varname, const char *val) /* Set max length for strings, if needed */ if (item->info_flags & ST_FLAG_STRING) - dstate_setaux(item->info_type, item->info_len); + dstate_setaux(item->info_type, (long)item->info_len); } else { @@ -351,7 +663,7 @@ static int upsclient_update_vars(void) /* VAR */ if (numa < 4) { - upsdebugx(1, "Error: insufficient data (got %zu args, need at least 4)", numa); + upsdebugx(1, "Error: insufficient data (got %" PRIuSIZE " args, need at least 4)", numa); } upsdebugx(5, "Received: %s %s %s %s", @@ -375,7 +687,7 @@ static dummy_info_t *find_info(const char *varname) return item; } - upsdebugx(2, "find_info: unknown variable: %s\n", varname); + upsdebugx(2, "find_info: unknown variable: %s", varname); return NULL; } @@ -429,7 +741,7 @@ static void upsconf_err(const char *errmsg) /* for dummy mode * parse the definition file and process its content */ -static int parse_data_file(int arg_upsfd) +static int parse_data_file(TYPE_FD arg_upsfd) { char fn[SMALLBUF]; char *ptr, var_value[MAX_STRING_SIZE]; @@ -452,11 +764,7 @@ static int parse_data_file(int arg_upsfd) { ctx = (PCONF_CTX_t *)xmalloc(sizeof(PCONF_CTX_t)); - if (device_path[0] == '/') - snprintf(fn, sizeof(fn), "%s", device_path); - else - snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); - + prepare_filepath(fn, sizeof(fn)); pconf_init(ctx, upsconf_err); if (!pconf_file_begin(ctx, fn)) diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c index 36b1da0e00..868ce3bfe4 100644 --- a/drivers/eaton-ats16-nm2-mib.c +++ b/drivers/eaton-ats16-nm2-mib.c @@ -4,6 +4,7 @@ * Copyright (C) * 2011-2012 Arnaud Quette * 2016-2020 Eaton (author: Arnaud Quette ) + * 2024 Jim Klimov * * Note: this subdriver was initially generated as a "stub" by the * gen-snmp-subdriver script. It must be customized! @@ -29,177 +30,57 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define EATON_ATS16_NM2_MIB_VERSION "0.20" +#define EATON_ATS16_NM2_MIB_VERSION "0.23" #define EATON_ATS16_NM2_SYSOID ".1.3.6.1.4.1.534.10.2" /* newer Network-M2 */ #define EATON_ATS16_NM2_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" static info_lkp_t eaton_ats16_nm2_source_info[] = { - { 1, "init" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "diagnosis" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "safe" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "fault" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "init"), + info_lkp_default(2, "diagnosis"), + info_lkp_default(3, "off"), + info_lkp_default(4, "1"), + info_lkp_default(5, "2"), + info_lkp_default(6, "safe"), + info_lkp_default(7, "fault"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_sensitivity_info[] = { - { 1, "normal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "normal"), + info_lkp_default(2, "high"), + info_lkp_default(3, "low"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_input_frequency_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "out-of-range"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_input_voltage_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "derated-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Voltage derated */ - { 3, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Voltage out of range triggered */ - { 4, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* "missing" */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "derated-range"), /* Voltage derated */ + info_lkp_default(3, "out-of-range"), /* Voltage out of range triggered */ + info_lkp_default(4, "unknown"), /* "missing" */ + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_test_result_info[] = { - { 1, "done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "done and warning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "aborted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "in progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "no test initiated" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "done and passed"), + info_lkp_default(2, "done and warning"), + info_lkp_default(3, "done and error"), + info_lkp_default(4, "aborted"), + info_lkp_default(5, "in progress"), + info_lkp_default(6, "no test initiated"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_output_status_info[] = { - { 1, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Output not powered */ - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Output powered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OFF"), /* Output not powered */ + info_lkp_default(2, "OL"), /* Output powered */ + info_lkp_sentinel }; /* Note: all the below *emp002* info should be shared with marlin and powerware! */ @@ -210,7 +91,7 @@ static info_lkp_t eaton_ats16_nm2_output_status_info[] = { * Future work for DMF might provide same-named routines via LUA-C gateway. */ -#if WITH_SNMP_LKP_FUN_DUMMY +# 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*) */ @@ -223,464 +104,348 @@ 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_ats16_nm2_sensor_temperature_unit_info[] = { - { 0, "dummy" -#if WITH_SNMP_LKP_FUN - , eaton_sensor_temperature_unit_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(0, "dummy", eaton_sensor_temperature_unit_fun), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_sensor_temperature_read_info[] = { - { 0, "dummy" -#if WITH_SNMP_LKP_FUN - , su_temperature_read_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(0, "dummy", su_temperature_read_fun), + info_lkp_sentinel }; -#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. */ static info_lkp_t eaton_ats16_nm2_sensor_temperature_unit_info[] = { - { 0, "kelvin" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "celsius" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "fahrenheit" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "kelvin"), + info_lkp_default(1, "celsius"), + info_lkp_default(2, "fahrenheit"), + info_lkp_sentinel }; -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ static info_lkp_t eaton_ats16_nm2_ambient_drycontacts_polarity_info[] = { - { 0, "normal-opened" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "normal-closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "normal-opened"), + info_lkp_default(1, "normal-closed"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_ambient_drycontacts_state_info[] = { - { 0, "inactive" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "active" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "inactive"), + info_lkp_default(1, "active"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nm2_emp002_ambient_presence_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationOK */ - { 3, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationLost */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(2, "yes"), /* communicationOK */ + info_lkp_default(3, "no"), /* communicationLost */ + info_lkp_sentinel }; /* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ static info_lkp_t eaton_ats16_nm2_threshold_status_info[] = { - { 0, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "warning-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "critical-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "warning-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "critical-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "good"), /* No threshold triggered */ + info_lkp_default(1, "warning-low"), /* Warning low threshold triggered */ + info_lkp_default(2, "critical-low"), /* Critical low threshold triggered */ + info_lkp_default(3, "warning-high"), /* Warning high threshold triggered */ + info_lkp_default(4, "critical-high"), /* Critical high threshold triggered */ + info_lkp_sentinel }; /* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ static info_lkp_t eaton_ats16_nm2_threshold_temperature_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low temperature warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low temperature critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high temperature warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high temperature critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; + static info_lkp_t eaton_ats16_nm2_threshold_humidity_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low humidity warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low humidity critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high humidity warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high humidity critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +static info_lkp_t eaton_ats16_nm2_ambient_drycontacts_info[] = { + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "opened"), + info_lkp_default(2, "closed"), + info_lkp_default(3, "opened"), /* openWithNotice */ + info_lkp_default(4, "closed"), /* closedWithNotice */ + info_lkp_sentinel }; /* EATON_ATS Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nm2_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* ats2IdentManufacturer.0 = STRING: EATON */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentModel.0 = STRING: Eaton ATS */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware! */ /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ /* ats2IdentFWVersion.0 = STRING: 00.00.0009 */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware.aux! */ /* ats2IdentRelease.0 = STRING: 1.7.5 */ - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL), /* ats2IdentSerialNumber.0 = STRING: GA04F23009 */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentPartNumber.0 = STRING: EATS16N */ - { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentAgentVersion.0 = STRING: 301F23C28 */ - /* { "unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + /* snmp_info_default("unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL), */ /* ats2InputDephasing.0 = INTEGER: 2 degrees */ - /* { "unmapped.ats2InputDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + /* snmp_info_default("unmapped.ats2InputDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL), */ /* Input collection */ /* ats2InputIndex.source1 = INTEGER: source1(1) */ - { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2InputIndex.source2 = INTEGER: source2(2) */ - { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2InputVoltage.source1 = INTEGER: 2292 0.1 V */ - { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* ats2InputVoltage.source2 = INTEGER: 2432 0.1 V */ - { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusVoltage.source1 = INTEGER: normalRange(1) */ - { "input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info }, + snmp_info_default("input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info), /* ats2InputStatusVoltage.source2 = INTEGER: normalRange(1) */ - { "input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info }, + snmp_info_default("input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info), /* ats2InputFrequency.source1 = INTEGER: 500 0.1 Hz */ - { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* ats2InputFrequency.source2 = INTEGER: 500 0.1 Hz */ - { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusFrequency.source1 = INTEGER: good(1) */ - { "input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info }, + snmp_info_default("input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info), /* ats2InputStatusFrequency.source2 = INTEGER: good(1) */ - { "input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info }, + snmp_info_default("input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info), /* ats2ConfigSensitivity.0 = INTEGER: normal(1) */ - { "input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nm2_sensitivity_info[0] }, + snmp_info_default("input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nm2_sensitivity_info[0]), /* ats2OperationMode.0 = INTEGER: source1(4) */ - { "input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_source_info }, + snmp_info_default("input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_source_info), /* ats2ConfigPreferred.0 = INTEGER: source1(1) */ - { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL), /* ats2InputDephasing = INTEGER: 181 */ - { "input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL), /* Output collection */ /* ats2OutputVoltage.0 = INTEGER: 2304 0.1 V */ - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigOutputVoltage.0 = INTEGER: 230 1 V */ - { "output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL), /* ats2OutputCurrent.0 = INTEGER: 5 0.1 A */ - { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL), /* UPS collection */ /* FIXME: RFC for device.test.result! */ /* ats2ConfigTransferTest.0 = INTEGER: noTestInitiated(6) */ - { "ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_test_result_info }, + snmp_info_default("ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_test_result_info), /* FIXME: RFC for device.status! */ /* ats2StatusOutput.0 = INTEGER: outputPowered(2) */ - { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_output_status_info }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_output_status_info), /* Ambient collection */ /* EMP001 (legacy) mapping for EMP002 * Note that NM2 should only be hooked with EMP002, but if any EMP001 was to be * connected, the value may be off by a factor 10 (to be proven) */ /* ats2EnvRemoteTemp.0 = INTEGER: 0 degrees Centigrade */ - { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature", 0, 1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteTempLowerLimit.0 = INTEGER: 5 degrees Centigrade */ - { "ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteTempUpperLimit.0 = INTEGER: 40 degrees Centigrade */ - { "ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteHumidity.0 = INTEGER: 0 percent */ - { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.humidity", 0, 1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteHumidityLowerLimit.0 = INTEGER: 5 percent */ - { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("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 }, + snmp_info_default("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) */ + snmp_info_default("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) */ + snmp_info_default("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'! */ /* sensorCount.0 */ - { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, + snmp_info_default("ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL), /* CommunicationStatus.n */ - { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", - NULL, SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_emp002_ambient_presence_info[0] }, + snmp_info_default("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, &eaton_ats16_nm2_emp002_ambient_presence_info[0]), /* sensorName.n: OctetString EMPDT1H1C2 @1 */ - { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorManufacturer.n */ - { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorModel.n */ - { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorSerialNumber.n */ - { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorUuid.n */ - { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorAddress.n */ - { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* sensorFirmwareVersion.n */ - { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL), /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_sensor_temperature_unit_info[0] }, + snmp_info_default("ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_sensor_temperature_unit_info[0]), + /* temperatureValue.n.1 */ - { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, #if WITH_SNMP_LKP_FUN - &eaton_ats16_nm2_sensor_temperature_read_info[0] + snmp_info_default("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, + &eaton_ats16_nm2_sensor_temperature_read_info[0]), #else - NULL + snmp_info_default("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, + NULL), #endif - }, - { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + + snmp_info_default("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, &eaton_ats16_nm2_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_threshold_status_info[0]), + snmp_info_default("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, &eaton_ats16_nm2_threshold_temperature_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_threshold_temperature_alarms_info[0]), /* FIXME: ambient.n.temperature.{minimum,maximum} */ /* temperatureThresholdLowCritical.n.1 */ - { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* temperatureThresholdLowWarning.n.1 */ - { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* temperatureThresholdHighWarning.n.1 */ - { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* temperatureThresholdHighCritical.n.1 */ - { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* humidityValue.n.1 */ - { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("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, &eaton_ats16_nm2_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_threshold_status_info[0]), + snmp_info_default("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, &eaton_ats16_nm2_threshold_humidity_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_threshold_humidity_alarms_info[0]), /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ /* humidityThresholdLowCritical.n.1 */ - { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* humidityThresholdLowWarning.n.1 */ - { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* humidityThresholdHighWarning.n.1 */ - { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* humidityThresholdHighCritical.n.1 */ - { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), /* digitalInputName.n.{1,2} */ - { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, + snmp_info_default("ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL), /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &eaton_ats16_nm2_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, &eaton_ats16_nm2_ambient_drycontacts_polarity_info[0] }, + snmp_info_default("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, &eaton_ats16_nm2_ambient_drycontacts_polarity_info[0]), + snmp_info_default("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, &eaton_ats16_nm2_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, &eaton_ats16_nm2_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, &eaton_ats16_nm2_ambient_drycontacts_state_info[0] }, + snmp_info_default("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, &eaton_ats16_nm2_ambient_drycontacts_state_info[0]), + snmp_info_default("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, &eaton_ats16_nm2_ambient_drycontacts_state_info[0]), -#if 0 /* FIXME: Remaining data to be processed */ +#if WITH_UNMAPPED_DATA_POINTS /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ - { "unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusIndex.source1 = INTEGER: source1(1) */ - { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusIndex.source2 = INTEGER: source2(2) */ - { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusGood.source1 = INTEGER: voltageAndFreqNormalRange(2) */ - { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusGood.source2 = INTEGER: voltageAndFreqNormalRange(2) */ - { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusInternalFailure.source1 = INTEGER: good(1) */ - { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusInternalFailure.source2 = INTEGER: good(1) */ - { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusUsed.source1 = INTEGER: poweringLoad(2) */ - { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusUsed.source2 = INTEGER: notPoweringLoad(1) */ - { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL), /* ats2StatusInternalFailure.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusOverload.0 = INTEGER: noOverload(1) */ - { "unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusOverTemperature.0 = INTEGER: noOverTemperature(1) */ - { "unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusShortCircuit.0 = INTEGER: noShortCircuit(1) */ - { "unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusCommunicationLost.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusConfigurationFailure.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeRTC.0 = Wrong Type (should be Counter32): Gauge32: 19191036 */ - { "unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeTextDate.0 = STRING: 08/11/1970 */ - { "unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeTextTime.0 = STRING: 02/50/36 */ - { "unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigInputVoltageRating.0 = INTEGER: 1 1 V */ - { "unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigInputFrequencyRating.0 = INTEGER: 50 Hz */ - { "unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTransferMode.0 = INTEGER: standard(1) */ - { "unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutLow.0 = INTEGER: 202 1 V */ - { "unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutLowDerated.0 = INTEGER: 189 1 V */ - { "unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutHigh.0 = INTEGER: 258 1 V */ - { "unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigHysteresisVoltage.0 = INTEGER: 5 1 V */ - { "unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL), /* Ambient collection */ /* ats2EnvNumContacts.0 = INTEGER: 2 */ - { "unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ContactIndex.1 = INTEGER: 1 */ - { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactIndex.2 = INTEGER: 2 */ - { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL), /* ats2ContactType.1 = INTEGER: notUsed(4) */ - { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactType.2 = INTEGER: notUsed(4) */ - { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL), /* ats2ContactState.1 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactState.2 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL), /* ats2ContactDescr.1 = STRING: Input #1 */ - { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactDescr.2 = STRING: Input #2 */ - { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL }, -#endif /* if 0 */ + snmp_info_default("unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ + /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; /* Note: keep the legacy definition intact, to avoid breaking compatibility */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index 9d5403fed8..f4a083ad58 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -25,232 +25,92 @@ #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" static info_lkp_t eaton_ats16_nmc_source_info[] = { - { 1, "init" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "diagnosis" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "safe" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "fault" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "init"), + info_lkp_default(2, "diagnosis"), + info_lkp_default(3, "off"), + info_lkp_default(4, "1"), + info_lkp_default(5, "2"), + info_lkp_default(6, "safe"), + info_lkp_default(7, "fault"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_sensitivity_info[] = { - { 1, "normal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "normal"), + info_lkp_default(2, "high"), + info_lkp_default(3, "low"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_input_frequency_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "out-of-range"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_input_voltage_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "derated-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Voltage derated */ - { 3, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Voltage out of range triggered */ - { 4, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* "missing" */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "derated-range"), /* Voltage derated */ + info_lkp_default(3, "out-of-range"), /* Voltage out of range triggered */ + info_lkp_default(4, "unknown"), /* "missing" */ + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_test_result_info[] = { - { 1, "done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "done and warning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "aborted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "in progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "no test initiated" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "done and passed"), + info_lkp_default(2, "done and warning"), + info_lkp_default(3, "done and error"), + info_lkp_default(4, "aborted"), + info_lkp_default(5, "in progress"), + info_lkp_default(6, "no test initiated"), + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_output_status_info[] = { - { 1, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Output not powered */ - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Output powered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OFF"), /* Output not powered */ + info_lkp_default(2, "OL"), /* Output powered */ + info_lkp_sentinel }; static info_lkp_t eaton_ats16_nmc_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" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* openWithNotice */ - { 4, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* closedWithNotice */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "opened"), + info_lkp_default(2, "closed"), + info_lkp_default(3, "opened"), /* openWithNotice */ + info_lkp_default(4, "closed"), /* closedWithNotice */ + info_lkp_sentinel }; /* EATON_ATS_NMC Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nmc_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* ats2IdentManufacturer.0 = STRING: EATON */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentModel.0 = STRING: Eaton ATS */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware! */ /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ /* ats2IdentFWVersion.0 = STRING: 00.00.0009 */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL), /* FIXME: RFC for device.firmware.aux! */ /* ats2IdentRelease.0 = STRING: JF */ - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.4.0", NULL, SU_FLAG_OK, NULL), /* ats2IdentSerialNumber.0 = STRING: GA04F23009 */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentPartNumber.0 = STRING: EATS16N */ - { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2IdentAgentVersion.0 = STRING: 301F23C28 */ /* { "unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, */ /* ats2InputDephasing.0 = INTEGER: 2 degrees */ @@ -258,148 +118,150 @@ static snmp_info_t eaton_ats16_nmc_mib[] = { /* Input collection */ /* ats2InputIndex.source1 = INTEGER: source1(1) */ - { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2InputIndex.source2 = INTEGER: source2(2) */ - { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* ats2InputVoltage.source1 = INTEGER: 2292 0.1 V */ - { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* ats2InputVoltage.source2 = INTEGER: 2432 0.1 V */ - { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusVoltage.source1 = INTEGER: normalRange(1) */ - { "input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info }, + snmp_info_default("input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info), /* ats2InputStatusVoltage.source2 = INTEGER: normalRange(1) */ - { "input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info }, + snmp_info_default("input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info), /* ats2InputFrequency.source1 = INTEGER: 500 0.1 Hz */ - { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* ats2InputFrequency.source2 = INTEGER: 500 0.1 Hz */ - { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusFrequency.source1 = INTEGER: good(1) */ - { "input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info }, + snmp_info_default("input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info), /* ats2InputStatusFrequency.source2 = INTEGER: good(1) */ - { "input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info }, + snmp_info_default("input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info), /* ats2ConfigSensitivity.0 = INTEGER: normal(1) */ - { "input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nmc_sensitivity_info[0] }, + snmp_info_default("input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nmc_sensitivity_info[0]), /* ats2OperationMode.0 = INTEGER: source1(4) */ - { "input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_source_info }, + snmp_info_default("input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_source_info), /* ats2ConfigPreferred.0 = INTEGER: source1(1) */ - { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL), /* ats2InputDephasing = INTEGER: 181 */ - { "input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL), /* Output collection */ /* ats2OutputVoltage.0 = INTEGER: 2304 0.1 V */ - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigOutputVoltage.0 = INTEGER: 230 1 V */ - { "output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL), /* ats2OutputCurrent.0 = INTEGER: 5 0.1 A */ - { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL), /* UPS collection */ /* FIXME: RFC for device.test.result! */ /* ats2ConfigTransferTest.0 = INTEGER: noTestInitiated(6) */ - { "ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_test_result_info }, + snmp_info_default("ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_test_result_info), /* FIXME: RFC for device.status! */ /* ats2StatusOutput.0 = INTEGER: outputPowered(2) */ - { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_output_status_info }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_output_status_info), /* Ambient collection */ /* ats2EnvRemoteTemp.0 = INTEGER: 0 degrees Centigrade */ - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteTempLowerLimit.0 = INTEGER: 5 degrees Centigrade */ - { "ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteTempUpperLimit.0 = INTEGER: 40 degrees Centigrade */ - { "ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteHumidity.0 = INTEGER: 0 percent */ - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL), /* ats2EnvRemoteHumidityLowerLimit.0 = INTEGER: 5 percent */ - { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("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 }, + snmp_info_default("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, + snmp_info_default("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_nmc_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, &eaton_ats16_nmc_ambient_drycontacts_info[0]), /* ats2ContactState.2 = INTEGER: open(1) */ - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("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_nmc_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, &eaton_ats16_nmc_ambient_drycontacts_info[0]), -#if 0 /* FIXME: Remaining data to be processed */ +#if WITH_UNMAPPED_DATA_POINTS /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ - { "unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusIndex.source1 = INTEGER: source1(1) */ - { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusIndex.source2 = INTEGER: source2(2) */ - { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusGood.source1 = INTEGER: voltageAndFreqNormalRange(2) */ - { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusGood.source2 = INTEGER: voltageAndFreqNormalRange(2) */ - { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusInternalFailure.source1 = INTEGER: good(1) */ - { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusInternalFailure.source2 = INTEGER: good(1) */ - { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusUsed.source1 = INTEGER: poweringLoad(2) */ - { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* ats2InputStatusUsed.source2 = INTEGER: notPoweringLoad(1) */ - { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL), /* ats2StatusInternalFailure.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusOverload.0 = INTEGER: noOverload(1) */ - { "unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusOverTemperature.0 = INTEGER: noOverTemperature(1) */ - { "unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusShortCircuit.0 = INTEGER: noShortCircuit(1) */ - { "unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusCommunicationLost.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL), /* ats2StatusConfigurationFailure.0 = INTEGER: good(1) */ - { "unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeRTC.0 = Wrong Type (should be Counter32): Gauge32: 19191036 */ - { "unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeTextDate.0 = STRING: 08/11/1970 */ - { "unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTimeTextTime.0 = STRING: 02/50/36 */ - { "unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigInputVoltageRating.0 = INTEGER: 1 1 V */ - { "unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigInputFrequencyRating.0 = INTEGER: 50 Hz */ - { "unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigTransferMode.0 = INTEGER: standard(1) */ - { "unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutLow.0 = INTEGER: 202 1 V */ - { "unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutLowDerated.0 = INTEGER: 189 1 V */ - { "unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigBrownoutHigh.0 = INTEGER: 258 1 V */ - { "unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL), /* ats2ConfigHysteresisVoltage.0 = INTEGER: 5 1 V */ - { "unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL), /* Ambient collection */ /* ats2EnvNumContacts.0 = INTEGER: 2 */ - { "unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL), /* ats2ContactIndex.1 = INTEGER: 1 */ - { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactIndex.2 = INTEGER: 2 */ - { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL), /* ats2ContactType.1 = INTEGER: notUsed(4) */ - { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactType.2 = INTEGER: notUsed(4) */ - { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL), /* ats2ContactDescr.1 = STRING: Input #1 */ - { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL), /* ats2ContactDescr.2 = STRING: Input #2 */ - { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL }, -#endif /* if 0 */ + snmp_info_default("unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ + /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; /* Note: keep the legacy definition intact, to avoid breaking compatibility */ diff --git a/drivers/eaton-ats30-mib.c b/drivers/eaton-ats30-mib.c index 3e26d14626..14dec194a0 100644 --- a/drivers/eaton-ats30-mib.c +++ b/drivers/eaton-ats30-mib.c @@ -30,64 +30,20 @@ #define EATON_ATS30_MODEL ".1.3.6.1.4.1.534.10.1.2.1.0" static info_lkp_t eaton_ats30_source_info[] = { - { 1, "init" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "diagnosis" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "safe" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "fault" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "init"), + info_lkp_default(2, "diagnosis"), + info_lkp_default(3, "off"), + info_lkp_default(4, "1"), + info_lkp_default(5, "2"), + info_lkp_default(6, "safe"), + info_lkp_default(7, "fault"), + info_lkp_sentinel }; static info_lkp_t eaton_ats30_input_sensitivity[] = { - { 1, "high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "high"), + info_lkp_default(2, "low"), + info_lkp_sentinel }; /* @@ -99,386 +55,318 @@ static info_lkp_t eaton_ats30_input_sensitivity[] = { * 4 atsFailureOverTemperature N/A */ static info_lkp_t eaton_ats30_status_info[] = { - { 0, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* SwitchFault */ - { 2, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* NoOutput */ - { 3, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* SwitchFault + NoOutput */ - { 4, "OL OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OutputOC */ - { 5, "OL OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OutputOC + SwitchFault */ - { 6, "OFF OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OutputOC + NoOutput */ - { 7, "OFF OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OutputOC + SwitchFault + NoOutput */ - { 8, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature */ - { 9, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + SwitchFault */ - { 10, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + NoOutput */ - { 11, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + SwitchFault + NoOutput */ - { 12, "OL OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + OutputOC */ - { 13, "OL OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + OutputOC + SwitchFault */ - { 14, "OFF OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + OutputOC + NoOutput */ - { 15, "OFF OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* OverTemperature + OutputOC + SwitchFault + NoOutput */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "OL"), + info_lkp_default(1, "OL"), /* SwitchFault */ + info_lkp_default(2, "OFF"), /* NoOutput */ + info_lkp_default(3, "OFF"), /* SwitchFault + NoOutput */ + info_lkp_default(4, "OL OVER"), /* OutputOC */ + info_lkp_default(5, "OL OVER"), /* OutputOC + SwitchFault */ + info_lkp_default(6, "OFF OVER"), /* OutputOC + NoOutput */ + info_lkp_default(7, "OFF OVER"), /* OutputOC + SwitchFault + NoOutput */ + info_lkp_default(8, "OL"), /* OverTemperature */ + info_lkp_default(9, "OL"), /* OverTemperature + SwitchFault */ + info_lkp_default(10, "OFF"), /* OverTemperature + NoOutput */ + info_lkp_default(11, "OFF"), /* OverTemperature + SwitchFault + NoOutput */ + info_lkp_default(12, "OL OVER"), /* OverTemperature + OutputOC */ + info_lkp_default(13, "OL OVER"), /* OverTemperature + OutputOC + SwitchFault */ + info_lkp_default(14, "OFF OVER"), /* OverTemperature + OutputOC + NoOutput */ + info_lkp_default(15, "OFF OVER"), /* OverTemperature + OutputOC + SwitchFault + NoOutput */ + info_lkp_sentinel }; /* EATON_ATS30 Snmp2NUT lookup table */ static snmp_info_t eaton_ats30_mib[] = { /* device type: ats */ - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* standard MIB items */ - { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.1.1.0 = STRING: "Eaton" */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* enterprises.534.10.1.1.2.0 = STRING: "01.12.13b" -- SNMP agent version */ - /* { "device.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.2.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("device.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.2.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.1.3.1.0 = INTEGER: 1 */ - /* { "unmapped.enterprise", 0, 1, ".1.3.6.1.4.1.534.10.1.1.3.1.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.enterprise", 0, 1, ".1.3.6.1.4.1.534.10.1.1.3.1.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.2.1.0 = STRING: "STS30002SR10019 " */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* enterprises.534.10.1.2.2.0 = STRING: "1A0003AR00.00.00" -- Firmware */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.2.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.2.3.0 = STRING: "2014-09-17 " -- Release date */ - /* { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.3.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.3.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.2.4.0 = STRING: "JA00E52021 " */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.4.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.4.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.2.5.0 = STRING: " " -- Device ID codes */ - /* { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.5.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.5.0", NULL, SU_FLAG_OK, NULL), */ /* ats measure */ /* =========== */ /* enterprises.534.10.1.3.1.1.1.1 = INTEGER: 1 */ - { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.1.1.1.2 = INTEGER: 2 */ - { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.1.1.2.1 = INTEGER: 2379 */ - { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.1.1.2.2 = INTEGER: 0 */ - { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.1.1.3.1 = INTEGER: 500 */ - { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.1.1.3.2 = INTEGER: 0 */ - { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.2.1.0 = INTEGER: 2375 */ - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.1.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.2.2.0 = INTEGER: 0 */ - { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.2.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.3.0 = INTEGER: 25 -- internal temperature in celsius */ - { "ups.temperature", 0, 1, ".1.3.6.1.4.1.534.10.1.3.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ups.temperature", 0, 1, ".1.3.6.1.4.1.534.10.1.3.3.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.4.0 = INTEGER: 77 -- internal temperature in fahrenheit */ - /* { "ups.temperatureF", 0, 1, ".1.3.6.1.4.1.534.10.1.3.4.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("ups.temperatureF", 0, 1, ".1.3.6.1.4.1.534.10.1.3.4.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.3.5.0 = INTEGER: 37937541 */ - { "device.uptime", 0, 1, ".1.3.6.1.4.1.534.10.1.3.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.uptime", 0, 1, ".1.3.6.1.4.1.534.10.1.3.5.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.3.6.0 = INTEGER: 284 */ - /* { "unmapped.atsMessureTransferedTimes", 0, 1, ".1.3.6.1.4.1.534.10.1.3.6.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.atsMessureTransferedTimes", 0, 1, ".1.3.6.1.4.1.534.10.1.3.6.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.3.7.0 = INTEGER: 4 */ - { "input.source", 0, 1, ".1.3.6.1.4.1.534.10.1.3.7.0", NULL, SU_FLAG_OK, eaton_ats30_source_info }, + snmp_info_default("input.source", 0, 1, ".1.3.6.1.4.1.534.10.1.3.7.0", NULL, SU_FLAG_OK, eaton_ats30_source_info), /* atsStatus */ /* ========= */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* NOTE: Unused OIDs are left as comments for potential future improvements */ /* enterprises.534.10.1.4.1.0 = INTEGER: 7 */ - { "unmapped.atsInputFlowIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.1.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.1.1 = INTEGER: 1 -- atsInputFlowTable start */ - { "unmapped.atsInputFlowIndex.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowIndex.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.1.2 = INTEGER: 2 */ - { "unmapped.atsInputFlowIndex.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowIndex.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.2.1 = INTEGER: 1 */ - { "unmapped.atsInputFlowRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.2.2 = INTEGER: 2 */ - { "unmapped.atsInputFlowRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.3.1 = INTEGER: 1 */ - { "unmapped.atsInputFlowSCR.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowSCR.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.3.2 = INTEGER: 2 */ - { "unmapped.atsInputFlowSCR.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowSCR.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.4.1 = INTEGER: 1 */ - { "unmapped.atsInputFlowParallelRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowParallelRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.2.1.4.2 = INTEGER: 2 */ - { "unmapped.atsInputFlowParallelRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFlowParallelRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.3.0 = INTEGER: 58720256 */ - { "unmapped.atsInputFailureIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsInputFailureIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.3.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.1.1 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.1.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.2.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.2.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.3.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.3.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.4.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.4.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.5.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.5.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.6.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.6.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.7.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.7.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.8.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.8.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.9.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.9.2 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.10.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.10.2 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.11.1 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.4.1.11.2 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.2", NULL, SU_FLAG_OK, NULL }, -#endif /* 0 */ + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.2", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* enterprises.atsFailureIndicator = INTEGER: 0 */ - { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.1.4.5.0", NULL, SU_FLAG_OK, eaton_ats30_status_info }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.534.10.1.4.5.0", NULL, SU_FLAG_OK, eaton_ats30_status_info), -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* enterprises.534.10.1.4.6.1.0 = INTEGER: 2 -- atsFailure start */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.1.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.6.2.0 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.2.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.6.3.0 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.3.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.4.6.4.0 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.4.0", NULL, SU_FLAG_OK, NULL }, -#endif /* 0 */ + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.4.0", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* atsLog */ /* ====== */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* We are not interested in log */ /* enterprises.534.10.1.5.1.0 = INTEGER: 272 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.1.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.1 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.3 = INTEGER: 3 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.3", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.4 = INTEGER: 4 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.4", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.5 = INTEGER: 5 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.5", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.6 = INTEGER: 6 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.6", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.7 = INTEGER: 7 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.7", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.8 = INTEGER: 8 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.8", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.9 = INTEGER: 9 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.9", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.1.10 = INTEGER: 10 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.10", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.1 = INTEGER: 1482323677 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.2 = INTEGER: 1480076955 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.3 = INTEGER: 1480069128 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.3", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.4 = INTEGER: 1480069093 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.4", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.5 = INTEGER: 1478693745 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.5", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.6 = INTEGER: 1478693741 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.6", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.7 = INTEGER: 1466604406 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.7", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.8 = INTEGER: 1466604386 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.8", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.9 = INTEGER: 1466604386 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.9", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.2.10 = INTEGER: 1463038288 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.10", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.1 = INTEGER: 41 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.2 = INTEGER: 41 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.3 = INTEGER: 44 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.3", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.4 = INTEGER: 44 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.4", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.5 = INTEGER: 44 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.5", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.6 = INTEGER: 41 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.6", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.7 = INTEGER: 41 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.7", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.8 = INTEGER: 46 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.8", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.9 = INTEGER: 45 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.9", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.3.10 = INTEGER: 41 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.10", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.10", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.1 = STRING: "12:34:37 12/21/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.2 = STRING: "12:29:15 11/25/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.3 = STRING: "10:18:48 11/25/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.3", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.3", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.4 = STRING: "10:18:13 11/25/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.4", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.5 = STRING: "12:15:45 11/09/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.5", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.6 = STRING: "12:15:41 11/09/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.6", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.7 = STRING: "14:06:46 06/22/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.7", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.8 = STRING: "14:06:26 06/22/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.8", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.9 = STRING: "14:06:26 06/22/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.9", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.9", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.5.2.1.4.10 = STRING: "07:31:28 05/12/2016" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.10", NULL, SU_FLAG_OK, NULL }, -#endif /* 0 */ + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.10", NULL, SU_FLAG_OK, NULL), +#endif /* WITH_UNMAPPED_DATA_POINTS */ /* atsConfig */ /* ========= */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* enterprises.534.10.1.6.1.1.0 = INTEGER: 538562409 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.1.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.1.1.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.1.2.0 = STRING: "01/24/2017" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.2.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.2.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.1.3.0 = STRING: "08:40:09" */ - { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.3.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.1.1 = INTEGER: 1 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.1.2 = INTEGER: 2 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.2.1 = INTEGER: 1700 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.2.2 = INTEGER: 1700 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.3.1 = INTEGER: 1800 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.3.2 = INTEGER: 1800 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.4.1 = INTEGER: 2640 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.4.2 = INTEGER: 2640 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.5.1 = INTEGER: 3000 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.5.2 = INTEGER: 3000 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.6.1 = INTEGER: 50 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.6.2 = INTEGER: 50 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.7.1 = INTEGER: 40 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.1", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.2.1.7.2 = INTEGER: 40 */ - { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.2", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.2", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.3.0 = INTEGER: 2640 */ - { "unmapped.atsConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.1.6.3.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.atsConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.1.6.3.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.4.0 = INTEGER: 26 */ - { "unmapped.atsConfigRandomTime", 0, 1, ".1.3.6.1.4.1.534.10.1.6.4.0", NULL, SU_FLAG_OK, NULL }, -#endif /* 0 */ + snmp_info_default("unmapped.atsConfigRandomTime", 0, 1, ".1.3.6.1.4.1.534.10.1.6.4.0", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* enterprises.534.10.1.6.5.0 = INTEGER: 1 */ - { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.5.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.5.0", NULL, SU_FLAG_OK, NULL), /* enterprises.534.10.1.6.6.0 = INTEGER: 2 */ - { "input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.6.0", NULL, SU_FLAG_OK, eaton_ats30_input_sensitivity }, + snmp_info_default("input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.6.0", NULL, SU_FLAG_OK, eaton_ats30_input_sensitivity), /* enterprises.534.10.1.6.7.0 = INTEGER: 2 */ - /* { "unmapped.atsConfigTest", 0, 1, ".1.3.6.1.4.1.534.10.1.6.7.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.atsConfigTest", 0, 1, ".1.3.6.1.4.1.534.10.1.6.7.0", NULL, SU_FLAG_OK, NULL), */ /* atsUpgrade */ /* ========== */ -#if 0 +#if WITH_UNMAPPED_DATA_POINTS /* We are not interested in atsUpgrade */ /* enterprises.534.10.1.7.1.0 = INTEGER: 1 */ - /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.1.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.1.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.7.2.0 = INTEGER: 1 */ - /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.2.0", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.2.0", NULL, SU_FLAG_OK, NULL), */ /* enterprises.534.10.1.7.3.0 = INTEGER: 0 */ - /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.3.0", NULL, SU_FLAG_OK, NULL }, */ -#endif /* 0 */ + /* snmp_info_default("unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.3.0", NULL, SU_FLAG_OK, NULL), */ +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t eaton_ats30 = { "eaton_ats30", EATON_ATS30_MIB_VERSION, NULL, EATON_ATS30_MODEL, eaton_ats30_mib, EATON_ATS30_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-genesis2-mib.c b/drivers/eaton-pdu-genesis2-mib.c index 24e881f1cf..2030792671 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,41 +48,47 @@ /* Snmp2NUT lookup table for GenesisII MIB */ static snmp_info_t eaton_aphel_genesisII_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, - "Eaton Powerware ePDU Monitored", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_UNIT_MACADDR, "unknown", - 0, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, + "Eaton Powerware ePDU Monitored", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_UNIT_MACADDR, "unknown", + 0, NULL), /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_FIRMREV, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_FIRMREV, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* Outlet page */ /* we can't use template since there is no counterpart to outlet.count */ - { "outlet.1.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".1.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.2.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".2.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.3.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".3.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.4.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".4.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.5.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".5.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.6.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".6.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.7.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".7.0", NULL, SU_FLAG_NEGINVALID, NULL }, - { "outlet.8.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".8.0", NULL, SU_FLAG_NEGINVALID, NULL }, + snmp_info_default("outlet.1.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".1.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.2.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".2.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.3.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".3.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.4.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".4.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.5.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".5.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.6.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".6.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.7.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".7.0", NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("outlet.8.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".8.0", NULL, SU_FLAG_NEGINVALID, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index a386e11095..d9f91e4815 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -30,21 +30,36 @@ #include "config.h" /* must be the first header */ -#include -#include -#include +#if defined WITH_SNMP_LKP_FUN && WITH_SNMP_LKP_FUN && defined WITH_SNMP_LKP_FUN_DUMMY && WITH_SNMP_LKP_FUN_DUMMY + /* Just avoid an "empty translation unit" */ + void dummy_method(void); + void dummy_method(void) {} +#else +# include +# include -#include "eaton-pdu-marlin-helpers.h" -#include "dstate.h" +# include "nut_stdint.h" +# include "eaton-pdu-marlin-helpers.h" +# include "dstate.h" +# include "common.h" /* Allow access to temperature_unit */ -#include "snmp-ups.h" +# include "snmp-ups.h" +/* Shunt the debugging calls when building self-test DMF driver code */ +/* FIXME: Go the next mile to pull common.o etc? We would rather not... */ +#ifdef WITH_DMFMIB_SELFTEST +# ifdef upsdebugx +# undef upsdebugx +# endif +# define upsdebugx(...) {while(0);} +#endif /* 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 +70,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; } @@ -85,3 +103,4 @@ const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) } return "celsius"; } +#endif /* WITH_SNMP_LKP_FUN && WITH_SNMP_LKP_FUN_DUMMY */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 4ad9a2383c..7bbcd83569 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,569 +36,169 @@ /* 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" static info_lkp_t marlin_outlet_status_info[] = { - { 0, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "pendingOff" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 3, "pendingOn" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "off"), + info_lkp_default(1, "on"), + info_lkp_default(2, "pendingOff"), /* transitional status */ + info_lkp_default(3, "pendingOn"), /* transitional status */ + info_lkp_sentinel }; static info_lkp_t marlin_outletgroups_status_info[] = { - { 0, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "rebooting" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 3, "mixed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status, not sure what it means! */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "off"), + info_lkp_default(1, "on"), + info_lkp_default(2, "rebooting"), /* transitional status */ + info_lkp_default(3, "mixed"), /* transitional status, not sure what it means! */ + info_lkp_sentinel }; /* Ugly hack for older G2 ePDU: * having the matching OID present means that the outlet/unit is * switchable. So, it should not require this value lookup */ static info_lkp_t g2_unit_outlet_switchability_info[] = { - { -1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "yes"), + info_lkp_default(0, "yes"), + info_lkp_sentinel }; static info_lkp_t marlin_outlet_switchability_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* switchable */ - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* notSwitchable */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), /* switchable */ + info_lkp_default(2, "no"), /* notSwitchable */ + info_lkp_sentinel }; /* Overall outlets switchability info for the unit. * This is refined per-outlet, depending on user configuration, * possibly disabling switchability of some outlets */ static info_lkp_t marlin_unit_switchability_info[] = { - { 0, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* switched */ - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* advancedMonitored */ - { 3, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* managed */ - { 4, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* monitored */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "no"), /* unknown */ + info_lkp_default(1, "yes"), /* switched */ + info_lkp_default(2, "no"), /* advancedMonitored */ + info_lkp_default(3, "yes"), /* managed */ + info_lkp_default(4, "no"), /* monitored */ + info_lkp_sentinel }; /* The physical type of outlet */ static info_lkp_t marlin_outlet_type_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "iecC13" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "iecC19" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "uk" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 11, "french" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 12, "schuko" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 20, "nema515" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 21, "nema51520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 22, "nema520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 23, "nemaL520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 24, "nemaL530" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 25, "nema615" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 26, "nema620" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 27, "nemaL620" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 28, "nemaL630" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 29, "nemaL715" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 30, "rf203p277" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(1, "iecC13"), + info_lkp_default(2, "iecC19"), + info_lkp_default(10, "uk"), + info_lkp_default(11, "french"), + info_lkp_default(12, "schuko"), + info_lkp_default(20, "nema515"), + info_lkp_default(21, "nema51520"), + info_lkp_default(22, "nema520"), + info_lkp_default(23, "nemaL520"), + info_lkp_default(24, "nemaL530"), + info_lkp_default(25, "nema615"), + info_lkp_default(26, "nema620"), + info_lkp_default(27, "nemaL620"), + info_lkp_default(28, "nemaL630"), + info_lkp_default(29, "nemaL715"), + info_lkp_default(30, "rf203p277"), + info_lkp_sentinel }; static info_lkp_t marlin_ambient_presence_info[] = { - { -1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* disconnected */ - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* connected */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(0, "no"), /* disconnected */ + info_lkp_default(1, "yes"), /* connected */ + info_lkp_sentinel }; static info_lkp_t marlin_emp002_ambient_presence_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationOK */ - { 3, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationLost */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(2, "yes"), /* communicationOK */ + info_lkp_default(3, "no"), /* communicationLost */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_status_info[] = { - { 0, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "warning-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "critical-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "warning-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "critical-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "good"), /* No threshold triggered */ + info_lkp_default(1, "warning-low"), /* Warning low threshold triggered */ + info_lkp_default(2, "critical-low"), /* Critical low threshold triggered */ + info_lkp_default(3, "warning-high"), /* Warning high threshold triggered */ + info_lkp_default(4, "critical-high"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_frequency_status_info[] = { - { 0, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "good"), /* No threshold triggered */ + info_lkp_default(1, "out-of-range"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_ambient_drycontacts_info[] = { - { -1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "open" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(0, "opened"), + info_lkp_default(1, "closed"), + info_lkp_sentinel }; static info_lkp_t marlin_threshold_voltage_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low voltage warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low voltage critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high voltage warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high voltage critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low voltage warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low voltage critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high voltage warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high voltage critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_current_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low current warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low current critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high current warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high current critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low current warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low current critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high current warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high current critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_frequency_alarm_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "frequency out of range!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "frequency out of range!"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_temperature_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low temperature warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low temperature critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high temperature warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high temperature critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_threshold_humidity_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low humidity warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low humidity critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high humidity warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high humidity critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t marlin_outlet_group_type_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "breaker1pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "breaker2pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "breaker3pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "outlet-section" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "user-defined" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(1, "breaker1pole"), + info_lkp_default(2, "breaker2pole"), + info_lkp_default(3, "breaker3pole"), + info_lkp_default(4, "outlet-section"), + info_lkp_default(5, "user-defined"), + info_lkp_sentinel }; static info_lkp_t marlin_input_type_info[] = { - { 1, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* singlePhase */ - { 2, "2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* splitPhase */ - { 3, "3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* threePhaseDelta */ - { 4, "3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* threePhaseWye */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "1"), /* singlePhase */ + info_lkp_default(2, "2"), /* splitPhase */ + info_lkp_default(3, "3"), /* threePhaseDelta */ + info_lkp_default(4, "3"), /* threePhaseWye */ + info_lkp_sentinel }; /* /// From opensource/master: @@ -641,65 +241,21 @@ static const char *marlin_outlet_group_phase_fun(void *raw_outlet_group_nb) } static info_lkp_t marlin_outlet_group_phase_info[] = { - { 1, "dummy" -#if WITH_SNMP_LKP_FUN - , marlin_outlet_group_phase_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(1, "dummy"), marlin_outlet_group_phase_fun), + info_lkp_sentinel }; */ static info_lkp_t marlin_outlet_group_phase_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 1, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* singlePhase */ - { 2, "1-N" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase1toN */ - { 3, "2-N" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase2toN */ - { 4, "3-N" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase3toN */ - { 5, "1-2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase1to2 */ - { 6, "2-3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase2to3 */ - { 7, "3-1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase3to1 */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), /* unknown */ + info_lkp_default(1, "1"), /* singlePhase */ + info_lkp_default(2, "1-N"), /* phase1toN */ + info_lkp_default(3, "2-N"), /* phase2toN */ + info_lkp_default(4, "3-N"), /* phase3toN */ + info_lkp_default(5, "1-2"), /* phase1to2 */ + info_lkp_default(6, "2-3"), /* phase2to3 */ + info_lkp_default(7, "3-1"), /* phase3to1 */ + info_lkp_sentinel }; #if WITH_SNMP_LKP_FUN @@ -708,7 +264,7 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { * Future work for DMF might provide same-named routines via LUA-C gateway. */ -#if WITH_SNMP_LKP_FUN_DUMMY +# 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*) */ @@ -721,99 +277,43 @@ 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" -#if WITH_SNMP_LKP_FUN - , eaton_sensor_temperature_unit_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(0, "dummy", eaton_sensor_temperature_unit_fun), + info_lkp_sentinel }; static info_lkp_t eaton_sensor_temperature_read_info[] = { - { 0, "dummy" -#if WITH_SNMP_LKP_FUN - , su_temperature_read_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(0, "dummy", su_temperature_read_fun), + info_lkp_sentinel }; -#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. */ static info_lkp_t eaton_sensor_temperature_unit_info[] = { - { 0, "kelvin" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "celsius" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "fahrenheit" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "kelvin"), + info_lkp_default(1, "celsius"), + info_lkp_default(2, "fahrenheit"), + info_lkp_sentinel }; -#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[] = { - { 0, "normal-opened" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "normal-closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "normal-opened"), + info_lkp_default(1, "normal-closed"), + info_lkp_sentinel }; static info_lkp_t marlin_ambient_drycontacts_state_info[] = { - { 0, "inactive" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "active" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "inactive"), + info_lkp_default(1, "active"), + info_lkp_sentinel }; #if WITH_SNMP_LKP_FUN @@ -821,53 +321,61 @@ static info_lkp_t marlin_ambient_drycontacts_state_info[] = { * Future work for DMF might provide a same-named routine via LUA-C gateway. */ -#if WITH_SNMP_LKP_FUN_DUMMY -long marlin_device_count_fun(const char *daisy_dev_list) - { return 1; } -#endif // WITH_SNMP_LKP_FUN_DUMMY +# if WITH_SNMP_LKP_FUN_DUMMY +long marlin_device_count_fun(const char *daisy_dev_list) { + NUT_UNUSED_VARIABLE(daisy_dev_list); + return 1; +} +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ static info_lkp_t marlin_device_count_info[] = { - { 1, "dummy" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, marlin_device_count_fun, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_s2l(1, "dummy", marlin_device_count_fun), + info_lkp_sentinel }; -#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 */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.6.0", + NULL, SU_FLAG_OK, NULL), + /* Device collection */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("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, + "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.part", ST_FLAG_STRING, SU_INFOSIZE, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("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 }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* For daisychain, there is only 1 physical interface! */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* Daisychained devices support */ /* FIXME : Should this be a static value, or can we expect the amount of @@ -882,48 +390,57 @@ 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", + snmp_info_default("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 */ }, + &marlin_device_count_info[0] /* devices_count */), #endif + /* Notes: this older/fallback definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", - "1", SU_FLAG_STATIC #if WITH_SNMP_LKP_FUN - | SU_FLAG_UNIQUE + snmp_info_default("device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "1", SU_FLAG_STATIC, + NULL /* devices_count */), +#else + snmp_info_default("device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + NULL /* devices_count */), #endif - , NULL /* devices_count */ }, /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("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 }, + "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: use unitName.0 (ePDU)? - * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, */ - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, + * snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), */ + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: this entry should be SU_FLAG_SEMI_STATIC */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.%i", - "", SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - /* FIXME: needs a date reformating callback + "", SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + /* FIXME: needs a date reformatting callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 - * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, + * snmp_info_default("ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + * snmp_info_default("ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), */ /* Input collection */ @@ -940,462 +457,593 @@ 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", - NULL, SU_FLAG_STATIC, NULL }, */ + snmp_info_default("input.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", + NULL, SU_FLAG_STATIC, NULL), */ /* Note: for daisychain mode, we must handle phase(s) per device, * not as a whole. In case of daisychain, support of the UNIQUE * field is not yet implemented (FIXME) so the last resolved OID * value wins. If a more-preferable OID is not implemented by device, * this is ok - the previous available value remains in place. */ /* inputType.%i.1 = INTEGER: singlePhase (1) */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", - NULL, SU_FLAG_STATIC, &marlin_input_type_info[0] }, + snmp_info_default("input.phases", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", + NULL, SU_FLAG_STATIC, + &marlin_input_type_info[0]), /* Frequency is measured globally */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", - NULL, 0, NULL }, - { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + NULL, 0, NULL), + snmp_info_default("input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_status_info[0]), + snmp_info_default("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", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("input.L3.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* FIXME: * - Voltage is only measured per phase, as mV! * so input.voltage == input.L1.voltage for both single and 3phase - * - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) + * - As per NUT namespace (https://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) - * => RFC input.Lx.voltage.context */ - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", - NULL, 0, NULL }, - { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + * => RFC input.Lx.voltage.context */ + snmp_info_default("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), + snmp_info_default("input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, - { "input.voltage.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.voltage", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", - NULL, 0, NULL }, - { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, - { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.voltage", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.2", - NULL, 0, NULL }, - { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, - { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.voltage", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.3", - NULL, 0, NULL }, - { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, - { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* FIXME: * - input.current is mapped on input.L1.current for both single and 3phase !!! */ - { "input.current", 0, 0.001, + snmp_info_default("input.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", - NULL, 0, NULL }, - { "input.current.nominal", 0, 0.001, + NULL, 0, NULL), + snmp_info_default("input.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.1", - NULL, 0, NULL }, - { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, - { "input.current.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0]), + snmp_info_default("input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.current.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.current.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.current.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", - NULL, 0, NULL }, - { "input.L1.current.nominal", 0, 0.001, + NULL, 0, NULL), + snmp_info_default("input.L1.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.1", - NULL, 0, NULL }, - { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, - { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0]), + snmp_info_default("input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L1.current.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L1.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.1", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.2", - NULL, 0, NULL }, - { "input.L2.current.nominal", 0, 0.001, + NULL, 0, NULL), + snmp_info_default("input.L2.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.2", - NULL, 0, NULL }, - { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, - { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0]), + snmp_info_default("input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L2.current.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L2.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.2", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current", 0, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.3", - NULL, 0, NULL }, - { "input.L3.current.nominal", 0, 0.001, + NULL, 0, NULL), + snmp_info_default("input.L3.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.3", - NULL, 0, NULL }, - { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, 0, NULL), + snmp_info_default("input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, - { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0]), + snmp_info_default("input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, - { "input.L3.current.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID, NULL), + snmp_info_default("input.L3.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + snmp_info_default("input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + snmp_info_default("input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + snmp_info_default("input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + snmp_info_default("input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("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 }, */ + /* snmp_info_default("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 }, - */ - { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, +/* + snmp_info_default("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), +*/ + snmp_info_default("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.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 */ }, +/* + snmp_info_default("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), +*/ + snmp_info_default("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 */ }, + snmp_info_default("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, + snmp_info_default("ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_presence_info[0] }, - { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_ambient_presence_info[0]), + snmp_info_default("ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_temperature_alarms_info[0] }, - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", - NULL, SU_FLAG_OK, NULL }, + NULL, SU_FLAG_OK, + &marlin_threshold_temperature_alarms_info[0]), + snmp_info_default("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, + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.low.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.low.warning", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.high", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.high.warning", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.high.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_humidity_alarms_info[0] }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", - NULL, SU_FLAG_OK, NULL }, + NULL, SU_FLAG_OK, + &marlin_threshold_humidity_alarms_info[0]), + snmp_info_default("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, + snmp_info_default("ambient.humidity.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.low.warning", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.low.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.high", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.%i.1", NULL, - SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.high.warning", ST_FLAG_RW, 0.1, + SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.high.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + /* Dry contacts on TH module */ - { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0]), + snmp_info_default("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 }, + snmp_info_default("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] }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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] }, + snmp_info_default("ambient.%i.temperature.unit", 0, 1.0, + ".1.3.6.1.4.1.534.6.8.1.2.5.0", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &eaton_sensor_temperature_unit_info[0]), /* temperatureValue.n.1 */ - { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, #if WITH_SNMP_LKP_FUN - &eaton_sensor_temperature_read_info[0] + snmp_info_default("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, + &eaton_sensor_temperature_read_info[0]), #else - NULL + snmp_info_default("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, + NULL), #endif - }, - { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0]), + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL), /* humidityValue.n.1 */ - { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, - { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("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), + snmp_info_default("ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0]), + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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 }, + snmp_info_default("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), + snmp_info_default("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] }, + snmp_info_default("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]), + snmp_info_default("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] }, + snmp_info_default("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]), + snmp_info_default("ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0]), + /* Outlet collection */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", - "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "outlet.id", 0, 1, NULL, - "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("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), + snmp_info_default("outlet.id", 0, 1, + NULL, + "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("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, + snmp_info_default("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, + snmp_info_default("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", - NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", - NULL, 0, NULL }, - { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", - NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", - NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", - NULL, 0, NULL }, + snmp_info_default("outlet.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + NULL, 0, NULL), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("outlet.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + NULL, 0, NULL), /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ @@ -1403,14 +1051,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 */ - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ + snmp_info_default("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, + NULL), + snmp_info_default("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. @@ -1421,101 +1072,113 @@ static snmp_info_t eaton_marlin_mib[] = { * the outlet number (represented as string) and is a read-only string * outletID.0.8 = Value (OctetString): "8" */ - { "outlet.%i.id", 0, 1, + snmp_info_default("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 */ - { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL), + + /* The fallback in firmwares issued before Sep 2017 (outletID), see details above: */ + snmp_info_default("outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, - NULL }, + NULL), /* Preferred: Outlet physical name OID in new G3 firmware (02.00.0051) * is named outletDesignator (other MIBs outletPhysicalName) * and is a read-only string provided by firmware * outletDesignator.0.1 = Value (OctetString): "A1" * outletPhysicalName.0.16 = Value (OctetString): "A16" */ - { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, - NULL }, + NULL), /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.1", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.2", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.3", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.4", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.5", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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), + snmp_info_default("outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, - { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0]), + snmp_info_default("outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_current_alarms_info[0] }, - { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0]), + snmp_info_default("outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.6.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, - { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0]), + snmp_info_default("outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_voltage_alarms_info[0] }, - { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.5.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.6.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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, + snmp_info_default("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, + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, - { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0]), + snmp_info_default("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,143 +1186,145 @@ 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", - "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL), /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ /* groupID.0.1 = OctetString: A */ - { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* User-friendly (writeable) description of the outlet group: * groupName.0.1 = OctetString: Factory Group 1 * groupName.0.2 = OctetString: Branch Circuit B */ - /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ - { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ + snmp_info_default("outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", NULL, SU_FLAG_SEMI_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL }, + NULL), /* Outlet-group physical name, a read-only string, * is named groupDesignator (other MIBs groupPhysicalName) * groupPhysicalName.0.1 = Value (OctetString): A * groupDesignator.0.2 = Value (OctetString): B */ - { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL }, + NULL), /* Outlet-group color: groupColor (other MIBs groupBkgColor) * groupColor.0.1 = Value (Gauge32): 16051527 (0xF4ED47) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.7.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL }, + NULL), /* groupType.0.1 = Integer: outletSection (4) */ - { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_type_info[0] }, + &marlin_outlet_group_type_info[0]), /* Phase to which an outlet-group is connected: * We use the following OID, which gives the voltage measurement type * groupVoltageMeasType.0.1; Value (Integer): singlePhase (1) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ - { "outlet.group.%i.phase", 0, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", + snmp_info_default("outlet.group.%i.phase", 0, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase_info[0] }, + &marlin_outlet_group_phase_info[0]), /* groupControlStatus.0.1 = Integer: on (1) */ - { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outletgroups_status_info[0] }, + &marlin_outletgroups_status_info[0]), /* groupChildCount.0.1 = Integer: 12 */ - { "outlet.group.%i.count", 0, 1, + snmp_info_default("outlet.group.%i.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.1.1.6.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupVoltage.0.1 = Integer: 243080 */ - { "outlet.group.%i.voltage", 0, 0.001, + snmp_info_default("outlet.group.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.3.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupVoltageThStatus.0.1 = Integer: good (0) */ - { "outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_threshold_status_info[0] }, - { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + &marlin_threshold_status_info[0]), + snmp_info_default("outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_threshold_voltage_alarms_info[0] }, - { "outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + &marlin_threshold_voltage_alarms_info[0]), + snmp_info_default("outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.5.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - { "outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.6.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - { "outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - { "outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.3.1.8.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrent.0.1 = Integer: 0 */ - { "outlet.group.%i.current", 0, 0.001, + snmp_info_default("outlet.group.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.3.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentCapacity.0.1 = Integer: 16000 */ - { "outlet.group.%i.current.nominal", 0, 0.001, + snmp_info_default("outlet.group.%i.current.nominal", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.2.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentThStatus.0.1 = Integer: good (0) */ - { "outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_threshold_status_info[0] }, - { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + &marlin_threshold_status_info[0]), + snmp_info_default("outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_threshold_current_alarms_info[0] }, + &marlin_threshold_current_alarms_info[0]), /* groupCurrentPercentLoad.0.1 = Integer: 0 */ - { "outlet.group.%i.load", 0, 1.0, + snmp_info_default("outlet.group.%i.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.4.1.10.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentThLowerWarning.0.1 = Integer: 0 */ - { "outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.5.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentThLowerCritical.0.1 = Integer: -1 */ - { "outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.6.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentThUpperWarning.0.1 = Integer: 12800 */ - { "outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupCurrentThUpperCritical.0.1 = Integer: 16000 */ - { "outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.5.4.1.8.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupWatts.0.1 = Integer: 2670 */ - { "outlet.group.%i.realpower", 0, 1.0, + snmp_info_default("outlet.group.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.3.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupVA.0.1 = Integer: 3132 */ - { "outlet.group.%i.power", 0, 1.0, + snmp_info_default("outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL }, + NULL), /* Input to which an outlet-group is connected * groupInputIndex.0.1 = Integer: 1 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "outlet.group.%i.input", 0, 1, + snmp_info_default("outlet.group.%i.input", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL }, + NULL), /* instant commands. */ /* Notes: @@ -1669,84 +1334,90 @@ static snmp_info_t eaton_marlin_mib[] = { * -1 : Cancel * we currently use "0", so instant On | Off | Reboot... */ /* no counterpart found! - { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, */ + snmp_info_default("outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL), */ /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* Per-outlet shutdown / startup delay (configuration point, not the timers) * outletControlShutoffDelay.0.3 = INTEGER: 120 * outletControlSequenceDelay.0.8 = INTEGER: 8 * (by default each output socket startup is delayed by its number in seconds) */ - { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, + snmp_info_default("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 */ }, - { "outlet.%i.delay.start", ST_FLAG_RW, 1, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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)! */ + snmp_info_default("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), + snmp_info_default("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), + snmp_info_default("outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* Per-outlet shutdown / startup timers * outletControlOffCmd.0.1 = INTEGER: -1 * outletControlOnCmd.0.1 = INTEGER: -1 */ - { "outlet.%i.timer.shutdown", 0, 1, + snmp_info_default("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 */ }, - { "outlet.%i.timer.start", 0, 1, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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 * -1:Cancel a pending group-level Off/On/Reboot command */ /* groupControlOffCmd.0.1 = Integer: -1 */ - { "outlet.group.%i.load.off", 0, 1, + snmp_info_default("outlet.group.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.3.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupControl0nCmd.0.1 = Integer: -1 */ - { "outlet.group.%i.load.on", 0, 1, + snmp_info_default("outlet.group.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.4.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupControlRebootCmd.0.1 = Integer: -1 */ - { "outlet.group.%i.load.cycle", 0, 1, + snmp_info_default("outlet.group.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.5.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.group.%i.load.off.delay", 0, 1, + snmp_info_default("outlet.group.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.3.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupControl0nCmd.0.1 = Integer: -1 */ - { "outlet.group.%i.load.on.delay", 0, 1, + snmp_info_default("outlet.group.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.4.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* groupControlRebootCmd.0.1 = Integer: -1 */ - { "outlet.group.%i.load.cycle.delay", 0, 1, + snmp_info_default("outlet.group.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.6.1.5.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; diff --git a/drivers/eaton-pdu-nlogic-mib.c b/drivers/eaton-pdu-nlogic-mib.c new file mode 100644 index 0000000000..10d88a4e37 --- /dev/null +++ b/drivers/eaton-pdu-nlogic-mib.c @@ -0,0 +1,3393 @@ +/* eaton-pdu-nlogic-mib.c - subdriver to monitor eaton-pdu-nlogic SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2022 Eaton (author: Arnaud Quette ) + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "eaton-pdu-nlogic-mib.h" + +#define EATON_PDU_NLOGIC_MIB_VERSION "0.10" + +#define EATON_PDU_NLOGIC_SYSOID ".1.3.6.1.4.1.534.7.1" + +static info_lkp_t eaton_nlogic_unit_switchability_info[] = { + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel +}; + +static info_lkp_t eaton_nlogic_outlet_status_info[] = { + info_lkp_default(1, "off"), + info_lkp_default(2, "on"), + info_lkp_default(3, "pendingOff"), /* transitional status */ + info_lkp_default(4, "pendingOn"), /* transitional status */ + info_lkp_sentinel +}; + +/* Note: same as marlin_outlet_type_info + i5-20R */ +static info_lkp_t eaton_nlogic_outlet_type_info[] = { + info_lkp_default(0, "unknown"), + info_lkp_default(1, "iecC13"), + info_lkp_default(2, "iecC19"), + info_lkp_default(3, "i5-20R"), + info_lkp_default(10, "uk"), + info_lkp_default(11, "french"), + info_lkp_default(12, "schuko"), + info_lkp_default(20, "nema515"), + info_lkp_default(21, "nema51520"), + info_lkp_default(22, "nema520"), + info_lkp_default(23, "nemaL520"), + info_lkp_default(24, "nemaL530"), + info_lkp_default(25, "nema615"), + info_lkp_default(26, "nema620"), + info_lkp_default(27, "nemaL620"), + info_lkp_default(28, "nemaL630"), + info_lkp_default(29, "nemaL715"), + info_lkp_default(30, "rf203p277"), + info_lkp_sentinel +}; + + +/* EATON_PDU_NLOGIC Snmp2NUT lookup table */ +static snmp_info_t eaton_pdu_nlogic_mib[] = { + +/* Data format: + * { info_type, info_flags, info_len, OID, dfl, flags, oid2info }, + * + * info_type: NUT INFO_ or CMD_ element name + * info_flags: flags to set in addinfo + * info_len: length of strings if ST_FLAG_STRING, multiplier otherwise + * OID: SNMP OID or NULL + * dfl: default value + * flags: snmp-ups internal flags (FIXME: ...) + * oid2info: lookup table between OID and NUT values + */ + + +/* standard MIB items; if the vendor MIB contains better OIDs for + * this (e.g. with daisy-chain support), consider adding those here + */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 */ + /* pduManufacturer.1 = STRING: "EATON" */ + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.4.1", + "EATON", SU_FLAG_STATIC, NULL), + /* pduModel.1 = STRING: "200-240V, 24A, 5.0kVA, 50/60Hz" */ + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.3.1", + "Eaton ePDU", SU_FLAG_STATIC, NULL), + /* pduSerialNumber.1 = STRING: "WMEL0046" */ + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.8.1", + NULL, SU_FLAG_STATIC, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + /* pduPartNumber.1 = STRING: "EMSV0001" */ + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.7.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + /* For daisychain, there is only 1 physical interface! */ + /* pduMACAddress.1 = Hex-STRING: 43 38 2D 34 35 2D 34 34 2D 33 30 2D 39 34 2D 31 */ + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.14.1", + "", SU_FLAG_STATIC, NULL), + + /* Number of daisychained units is processed according to present units + * in the chain with new G3 firmware (02.00.0051, since autumn 2017): + * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ + /* FIXME: inline func */ + /* pduNumberPDU.0 = INTEGER: 1 */ + snmp_info_default("device.count", 0, 1, + ".1.3.6.1.4.1.534.7.1.1.1.0", + "0", SU_FLAG_STATIC, + NULL /* &marlin_device_count_info[0] */ /* devices_count */), + + /* FIXME: this entry should be SU_FLAG_SEMI_STATIC */ + /* pduFirmwareVersion.1 = STRING: "1.0.6" */ + snmp_info_default("device.firmware", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.5.1", + "", SU_FLAG_OK, NULL), + /* pduFirmwareVersionTimeStamp.1 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + /* snmp_info_default("unmapped.pduFirmwareVersionTimeStamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.1.2.1.6.1", NULL, SU_FLAG_OK, NULL), */ + /* pduIdentIndex.1 = INTEGER: 1 */ + /* snmp_info_default("unmapped.pduIdentIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.1.2.1.1.1", NULL, SU_FLAG_OK, NULL), */ + /* pduName.1 = "" */ + /* FIXME: RFC device.name? */ + snmp_info_default("device.name", ST_FLAG_STRING | ST_FLAG_RW, 63, + ".1.3.6.1.4.1.534.7.1.1.2.1.2.1", + NULL, SU_FLAG_OK, NULL), + /* pduStatus.1 = INTEGER: 4 */ + /* snmp_info_default("unmapped.pduStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.1.2.1.9.1", NULL, SU_FLAG_OK, NULL), */ + + /* Input collection */ + /* pduInputPhaseCount.1 = INTEGER: 1 */ + snmp_info_default("input.phases", 0, 1, + ".1.3.6.1.4.1.534.7.1.1.2.1.11.1", + NULL, SU_FLAG_OK, NULL), + /* pduInputType.1 = INTEGER: 1 */ + /* snmp_info_default("unmapped.pduInputType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), */ + /* pduInputFrequency.1 = INTEGER: 499 */ + snmp_info_default("input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.7.1.2.1.1.2.1", + NULL, 0, NULL), + /* pduInputTotalCurrent.1 = INTEGER: 0 */ + snmp_info_default("input.current", 0, 0.01, ".1.3.6.1.4.1.534.7.1.2.1.1.11.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltage.1.1 = INTEGER: 2418 */ + snmp_info_default("input.voltage", 0, 0.1, + ".1.3.6.1.4.1.534.7.1.2.2.1.3.1.1", + NULL, SU_FLAG_OK, NULL), + + /* Outlet groups collection */ + /* pduGroupCount.1 = INTEGER: 1 */ + snmp_info_default("outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.7.1.1.2.1.12.1", + NULL, SU_FLAG_STATIC, NULL), + /* pduGroupVoltage.1.1 = INTEGER: 2418 */ + snmp_info_default("outlet.group.%i.voltage", 0, 0.1, + ".1.3.6.1.4.1.534.7.1.3.1.1.5.1.%i", + NULL, SU_OUTLET_GROUP, NULL), + /* pduGroupCurrent.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.group.%i.current", 0, 1, + ".1.3.6.1.4.1.534.7.1.3.1.1.12.1.%i", + NULL, SU_OUTLET_GROUP, NULL), + + /* Outlet collection */ + /* pduOutletCount.1 = INTEGER: 6 */ + snmp_info_default("outlet.count", 0, 1, + ".1.3.6.1.4.1.534.7.1.1.2.1.13.1", + NULL, SU_FLAG_OK, NULL), + /* pduControllable.1 = INTEGER: 1 */ + snmp_info_default("outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.1.2.1.10.1", + "no", SU_FLAG_STATIC, + &eaton_nlogic_unit_switchability_info[0]), + /* pduOutletControlSwitchable.1.1 = INTEGER: 2 */ + snmp_info_default("outlet.%i.switchable", ST_FLAG_RW |ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.5.2.1.8.1.%i", + "no", SU_OUTLET, + &eaton_nlogic_unit_switchability_info[0]), + /* pduOutletControlStatus.1.1 = INTEGER: 2 */ + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.5.2.1.1.1.%i", + NULL, SU_OUTLET, + &eaton_nlogic_outlet_status_info[0]), + /* pduOutletName.1.1 = STRING: "OUTLET 1" */ + snmp_info_default("outlet.%i.name", ST_FLAG_RW |ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.5.1.1.2.1.%i", + NULL, SU_OUTLET, NULL), + /* pduOutletType.1.1 = INTEGER: 2 */ + snmp_info_default("outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.7.1.5.1.1.3.1.%i", + "unknown", SU_FLAG_STATIC | SU_OUTLET, + &eaton_nlogic_outlet_type_info[0]), + /* pduOutletCurrentRating.1.1 = INTEGER: 1600 */ + snmp_info_default("outlet.%i.current.nominal", 0, 0.01, + ".1.3.6.1.4.1.534.7.1.5.1.1.4.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pduOutletCurrent.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.current", 0, 0.01, + ".1.3.6.1.4.1.534.7.1.5.1.1.5.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pduOutletCurrentPercentLoad.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.load", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.1.1.11.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pduOutletVA.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.power", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.1.1.12.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pduOutletWatts.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.realpower", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.1.1.13.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + + /* instant commands. */ + /* pduOutletControlCommand.1.1 = INTEGER: 2 */ + snmp_info_default("outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + "1", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + "2", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + "5", SU_TYPE_CMD | SU_OUTLET, NULL), + + /* Per-outlet shutdown / startup delay (configuration point, not the timers) + * (by default each output socket startup is delayed by its number in seconds) + */ + /* pduOutletControlShutoffDelay.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.delay.shutdown", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.9.1.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL), + /* pduOutletControlSequenceDelay.1.1 = INTEGER: 0 */ + snmp_info_default("outlet.%i.delay.start", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.6.1.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL), + /* FIXME: need RFC! */ + /* pduOutletControlRebootOffTime.1.1 = INTEGER: 5 */ + snmp_info_default("outlet.%i.delay.reboot", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.7.1.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET, NULL), + + +#if 0 + /* FIXME: how to deal with these? + Delays are in 1 OID and command in another. + These versions should take the delay, set the related OID (need the outlet index, so miss context!) and then call the command */ + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), // + set "outlet.%i.delay.shutdown" + set itself to delayedOff (3) + // &eaton_nlogic_outlet_delayed_off_info[0] + snmp_info_default("outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), // + set "outlet.%i.delay.start" + set itself to delayedOn (4), + // &eaton_nlogic_outlet_delayed_on_info[0] + snmp_info_default("outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.7.1.5.2.1.10.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), // + set "outlet.%i.delay.start" + set itself to delayedReboot (6), + // &eaton_nlogic_outlet_delayed_reboot_info[0] +#endif + +#if WITH_UNMAPPED_DATA_POINTS + /* pduIPv4Address.1 = IpAddress: 192.168.1.55 */ + snmp_info_default("unmapped.pduIPv4Address", 0, 1, ".1.3.6.1.4.1.534.7.1.1.2.1.15.1", NULL, SU_FLAG_OK, NULL), + /* pduIPv6Address.1 = STRING: "FE80::CA45:44FF:FE30:9414" */ + snmp_info_default("unmapped.pduIPv6Address", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.1.2.1.16.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigSsh.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduConfigSsh", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.2.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigFtps.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduConfigFtps", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigHttp.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduConfigHttp", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.4.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigHttps.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduConfigHttps", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.5.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigIPv4IPv6Switch.1 = INTEGER: 3 */ + snmp_info_default("unmapped.pduConfigIPv4IPv6Switch", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigRedfishAPI.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduConfigRedfishAPI", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigOledDispalyOrientation.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduConfigOledDispalyOrientation", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.8.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigEnergyReset.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduConfigEnergyReset", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.9.1", NULL, SU_FLAG_OK, NULL), + /* pduConfigNetworkManagementCardReset.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduConfigNetworkManagementCardReset", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.10.1", NULL, SU_FLAG_OK, NULL), + + /* pduConfigDaisyChainStatus.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduConfigDaisyChainStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.1.3.1.11.1", NULL, SU_FLAG_OK, NULL), + + /* pduInputFrequencyStatus.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputFrequencyStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerVA.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerWatts.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.5.1", NULL, SU_FLAG_OK, NULL), + /* pduInputTotalEnergy.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputTotalEnergy", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerWattHourTimer.1 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduInputPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.2.1.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pduInputResettableEnergy.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputResettableEnergy", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.8.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerFactor.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.9.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerVAR.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.2.1.1.10.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseIndex.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + + /* pduInputPhaseVoltage.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltage", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltage.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltage", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThStatus.1.1 = INTEGER: 5 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerWarning.1.1 = INTEGER: 1900 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerCritical.1.1 = INTEGER: 1800 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperWarning.1.1 = INTEGER: 2150 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperCritical.1.1 = INTEGER: 2250 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentRating.1.1 = INTEGER: 2400 */ + snmp_info_default("unmapped.pduInputPhaseCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentRating.1.2 = INTEGER: 2400 */ + snmp_info_default("unmapped.pduInputPhaseCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentRating.1.3 = INTEGER: 2400 */ + snmp_info_default("unmapped.pduInputPhaseCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrent.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrent", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.11.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrent.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrent", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.11.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrent.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrent", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.11.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.12.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.12.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.12.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.13.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.13.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.13.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.14.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.14.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.14.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperWarning.1.1 = INTEGER: 2100 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.15.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.15.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.15.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperCritical.1.1 = INTEGER: 2400 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.16.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.16.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.16.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentPercentLoad.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.17.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentPercentLoad.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.17.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentPercentLoad.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.17.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduInputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.18.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.18.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.18.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVA.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.19.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVA.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.19.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVA.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.19.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWatts.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.20.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWatts.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.20.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWatts.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.20.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHour.1.1 = INTEGER: 30 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.21.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHour.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.21.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHour.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.21.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHourTimer.1.1 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.2.2.1.22.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHourTimer.1.2 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.2.2.1.22.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerWattHourTimer.1.3 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduInputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.2.2.1.22.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerFactor.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.23.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerFactor.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.23.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerFactor.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.23.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVAR.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.24.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVAR.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.24.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhasePowerVAR.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.24.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThResetThld.1.1 = INTEGER: 20 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.25.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThResetThld.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.25.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThResetThld.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.25.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThChangeDelay.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.26.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThChangeDelay.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.26.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThChangeDelay.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.26.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThCtrl.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.27.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.27.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseVoltageThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.27.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThResetThld.1.1 = INTEGER: 100 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.28.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThResetThld.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.28.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThResetThld.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.28.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThChangeDelay.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.29.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThChangeDelay.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.29.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThChangeDelay.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.29.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThCtrl.1.1 = INTEGER: 12 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.30.1.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.30.1.2", NULL, SU_FLAG_OK, NULL), + /* pduInputPhaseCurrentThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPhaseCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.30.1.3", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThLowerWarning.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.31.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThLowerCritical.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.32.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThUpperWarning.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.33.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThUpperCritical.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.34.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThResetThld.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.35.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThChangeDelay.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputPowerThresholdThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.36.1", NULL, SU_FLAG_OK, NULL), + /* pduInputPowerThresholdThCtrl.1 = INTEGER: 15 */ + snmp_info_default("unmapped.pduInputPowerThresholdThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.37.1", NULL, SU_FLAG_OK, NULL), + /* pduInputEnergyThresholdThUpperWarning.1 = INTEGER: 2147483 */ + snmp_info_default("unmapped.pduInputEnergyThresholdThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.38.1", NULL, SU_FLAG_OK, NULL), + /* pduInputEnergyThresholdThUpperCritical.1 = INTEGER: 2147483 */ + snmp_info_default("unmapped.pduInputEnergyThresholdThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.39.1", NULL, SU_FLAG_OK, NULL), + /* pduInputEnergyThresholdThResetThld.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputEnergyThresholdThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.40.1", NULL, SU_FLAG_OK, NULL), + /* pduInputEnergyThresholdThChangeDelay.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduInputEnergyThresholdThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.41.1", NULL, SU_FLAG_OK, NULL), + /* pduInputEnergyThresholdThCtrl.1 = INTEGER: 3 */ + snmp_info_default("unmapped.pduInputEnergyThresholdThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.2.2.1.42.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.3 = INTEGER: 3 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.4 = INTEGER: 4 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.5 = INTEGER: 5 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.6 = INTEGER: 6 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.7 = INTEGER: 7 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.8 = INTEGER: 8 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.9 = INTEGER: 9 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.10 = INTEGER: 10 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.11 = INTEGER: 11 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupIndex.1.12 = INTEGER: 12 */ + snmp_info_default("unmapped.pduGroupIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.1.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.1 = Hex-STRING: 42 31 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.2 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.3 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.4 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.5 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.6 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.7 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.8 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.9 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.10 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.11 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupName.1.12 = Hex-STRING: 00 */ + snmp_info_default("unmapped.pduGroupName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.2.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.1 = INTEGER: 5 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupType.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.3.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageMeasType.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.4.1.12", NULL, SU_FLAG_OK, NULL), + + /* pduGroupVoltageThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.6.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.7.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.8.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.9.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.10.1.12", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.1", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.2", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.3", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.4", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.5", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.6", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.7", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.8", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.9", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.10", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.11", NULL, SU_FLAG_OK, NULL), + /* pdugroupCurrentRating.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdugroupCurrentRating", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.11.1.12", NULL, SU_FLAG_OK, NULL), + + /* pduGroupCurrentThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.13.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.14.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.15.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.16.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.17.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentPercentLoad.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.18.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVA.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVA", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.19.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWatts.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWatts", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.20.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHour.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.21.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.1 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.2 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.3 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.4 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.5 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.6 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.7 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.8 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.9 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.10 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.11 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerWattHourTimer.1.12 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduGroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.3.1.1.22.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.1 = INTEGER: 100 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerFactor.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.23.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupPowerVAR.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupPowerVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.24.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.1 = INTEGER: 6 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupOutletCount.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupOutletCount", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.25.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupBreakerStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.26.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupVoltageThCtrl.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupVoltageThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.27.1.12", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.1", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.2", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.3", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.4", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.5", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.6", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.7", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.8", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.9", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.10", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.11", NULL, SU_FLAG_OK, NULL), + /* pduGroupCurrentThCtrl.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduGroupCurrentThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.3.1.1.28.1.12", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureScale.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureScale", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pduDoorCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDoorCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* pduDryCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDryCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.5.1", NULL, SU_FLAG_OK, NULL), + /* pduSpotCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduSpotCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pduRopeCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduRopeCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pduHidCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidCount", 0, 1, ".1.3.6.1.4.1.534.7.1.4.1.1.10.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureIndex.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.3 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.4 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.5 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureName.1.6 = STRING: " " */ + snmp_info_default("unmapped.pduTemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.2.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.3 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.4 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.5 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureProbeStatus.1.6 = INTEGER: 1 */ + snmp_info_default("unmapped.pduTemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureValue.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pduTemperatureThCtrl.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduTemperatureThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.2.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityIndex.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.3 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.4 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.5 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityName.1.6 = STRING: " " */ + snmp_info_default("unmapped.pduHumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.3.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.3 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.4 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.5 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityProbeStatus.1.6 = INTEGER: 1 */ + snmp_info_default("unmapped.pduHumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityValue.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityValue", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHumidityThCtrl.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHumidityThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.4.3.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pduDoorIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDoorIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDoorIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDoorIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDoorName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduDoorName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.4.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDoorName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduDoorName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.4.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDoorProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduDoorProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDoorProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduDoorProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDoorState.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDoorState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDoorState.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDoorState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.4.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDryIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDryIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDryIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDryIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDryName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduDryName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.5.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDryName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduDryName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.5.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDryProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduDryProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDryProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduDryProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduDryState.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDryState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduDryState.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduDryState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.5.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduSpotIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduSpotIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduSpotIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduSpotIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduSpotName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduSpotName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.6.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduSpotName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduSpotName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.6.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduSpotProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduSpotProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduSpotProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduSpotProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduSpotState.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduSpotState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduSpotState.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduSpotState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.6.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduRopeIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduRopeIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduRopeIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduRopeIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduRopeName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pduRopeName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.7.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduRopeName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pduRopeName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.7.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduRopeProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduRopeProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduRopeProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduRopeProbeStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduRopeState.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduRopeState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduRopeState.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduRopeState", 0, 1, ".1.3.6.1.4.1.534.7.1.4.7.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHIDIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHIDIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHIDIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHIDIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidAisle.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidAisle.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidHandleOperation.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pduHidHandleOperation", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidHandleOperation.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pduHidHandleOperation", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHIDVer.1.1 = Hex-STRING: 02 00 00 00 00 */ + snmp_info_default("unmapped.pduHIDVer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.1.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHIDVer.1.2 = Hex-STRING: 02 00 00 00 00 */ + snmp_info_default("unmapped.pduHIDVer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.1.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduMechanicalLock.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pduMechanicalLock", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduMechanicalLock.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pduMechanicalLock", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.1.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.7", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.8", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.9", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.10", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.11", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.12", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.13", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.14", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.15", NULL, SU_FLAG_OK, NULL), + /* pduHidControlIndex.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.1.1.16", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.1 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.2 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.3 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.4 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.5 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.6 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.7 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.7", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.8 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.8", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.9 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.9", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.10 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.10", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.11 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.11", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.12 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.12", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.13 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.13", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.14 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.14", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.15 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.15", NULL, SU_FLAG_OK, NULL), + /* pduHidControlUserName.1.16 = "" */ + snmp_info_default("unmapped.pduHidControlUserName", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.2.1.16", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.7", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.8", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.9", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.10", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.11", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.12", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.13", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.14", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.15", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardID.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardID", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.3.1.16", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.1 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.2 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.3 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.4 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.5 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.6 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.7 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.7", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.8 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.8", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.9 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.9", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.10 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.10", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.11 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.11", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.12 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.12", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.13 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.13", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.14 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.14", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.15 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.15", NULL, SU_FLAG_OK, NULL), + /* pduHidControlTimestamp.1.16 = STRING: "2000/00/00 00:00:00" */ + snmp_info_default("unmapped.pduHidControlTimestamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.4.10.2.1.4.1.16", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.7", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.8", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.9", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.10", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.11", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.12", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.13", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.14", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.15", NULL, SU_FLAG_OK, NULL), + /* pduHidControlCardAisle.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduHidControlCardAisle", 0, 1, ".1.3.6.1.4.1.534.7.1.4.10.2.1.5.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.3 = INTEGER: 3 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.4 = INTEGER: 4 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.5 = INTEGER: 5 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.6 = INTEGER: 6 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletIndex.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletIndex", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.1.1.48", NULL, SU_FLAG_OK, NULL), + + /* pduOutletActivePowerThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.3 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.4 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.5 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.6 = INTEGER: 1 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThStatus.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.6.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerWarning.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.7.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThLowerCritical.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.8.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperWarning.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.9.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThUpperCritical.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.10.1.48", NULL, SU_FLAG_OK, NULL), + + /* pduOutletWh.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletWh.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletWh", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.14.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.1 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.2 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.3 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.4 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.5 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.6 = Hex-STRING: 32 30 32 31 2F 30 35 2F 32 39 20 30 39 3A 30 36 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.7 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.8 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.9 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.10 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.11 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.12 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.13 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.14 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.15 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.16 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.17 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.18 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.19 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.20 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.21 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.22 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.23 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.24 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.25 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.26 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.27 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.28 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.29 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.30 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.31 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.32 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.33 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.34 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.35 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.36 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.37 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.38 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.39 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.40 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.41 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.42 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.43 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.44 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.45 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.46 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.47 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletWhTimer.1.48 = Hex-STRING: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ + snmp_info_default("unmapped.pduOutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.7.1.5.1.1.15.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.1 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.2 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.3 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.4 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.5 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.6 = INTEGER: 100 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletPowerFactor.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletPowerFactor", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.16.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletVAR.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletVAR", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.17.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletBranch.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletBranch", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.18.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThResetThld.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThResetThld", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.19.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThChangeDelay.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletActivePowerThChangeDelay", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.20.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.1 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.2 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.3 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.4 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.5 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.6 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.7 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.8 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.9 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.10 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.11 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.12 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.13 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.14 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.15 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.16 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.17 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.18 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.19 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.20 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.21 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.22 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.23 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.24 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.25 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.26 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.27 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.28 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.29 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.30 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.31 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.32 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.33 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.34 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.35 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.36 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.37 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.38 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.39 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.40 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.41 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.42 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.43 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.44 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.45 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.46 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.47 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletActivePowerThCtrl.1.48 = INTEGER: 15 */ + snmp_info_default("unmapped.pduOutletActivePowerThCtrl", 0, 1, ".1.3.6.1.4.1.534.7.1.5.1.1.21.1.48", NULL, SU_FLAG_OK, NULL), + + + /* pduOutletControlOffCmd.1.1 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.2 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.3 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.4 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.5 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.6 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOffCmd.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOffCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.2.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.1 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.2 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.3 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.4 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.5 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.6 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlOnCmd.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlOnCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.3.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.1 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.2 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.3 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.4 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.5 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.6 = INTEGER: -1 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlRebootCmd.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlRebootCmd", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.4.1.48", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.3 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.4 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.5 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.6 = INTEGER: 2 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.7", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.8", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.9", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.10", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.11", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.12", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.13", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.14", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.15", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.16", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.17", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.18", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.19", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.20", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.21", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.22", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.23", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.24", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.25", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.26", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.27", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.28", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.29", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.30", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.31", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.32", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.33", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.34", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.35", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.36", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.37", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.38", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.39", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.40", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.41", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.42", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.43", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.44", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.45", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.46", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.47", NULL, SU_FLAG_OK, NULL), + /* pduOutletControlPowerOnState.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pduOutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.534.7.1.5.2.1.5.1.48", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ + + /* end of structure. */ + snmp_info_sentinel +}; + +mib2nut_info_t eaton_pdu_nlogic = { "eaton_pdu_nlogic", EATON_PDU_NLOGIC_MIB_VERSION, NULL, NULL, eaton_pdu_nlogic_mib, EATON_PDU_NLOGIC_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-nlogic-mib.h b/drivers/eaton-pdu-nlogic-mib.h new file mode 100644 index 0000000000..7ac83ec8e2 --- /dev/null +++ b/drivers/eaton-pdu-nlogic-mib.h @@ -0,0 +1,30 @@ +/* eaton-pdu-nlogic-mib.h - subdriver to monitor eaton-pdu-nlogic SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2022 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_PDU_NLOGIC_MIB_H +#define EATON_PDU_NLOGIC_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_pdu_nlogic; + +#endif /* EATON_PDU_NLOGIC_MIB_H */ diff --git a/drivers/eaton-pdu-pulizzi-mib.c b/drivers/eaton-pdu-pulizzi-mib.c index b4a8976fd7..87158ad891 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.50" #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" @@ -53,105 +53,87 @@ static info_lkp_t pulizzi_sw_outlet_status_info[] = { - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "on"), + info_lkp_default(2, "off"), + info_lkp_sentinel }; /* simply remap the above status to "yes" */ static info_lkp_t pulizzi_sw_outlet_switchability_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "yes"), + info_lkp_sentinel }; /* Snmp2NUT lookup table for Eaton Pulizzi Switched ePDU MIB */ static snmp_info_t eaton_pulizzi_switched_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, - "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.2.6.0", - "unknown", 0, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.2.6.0", + "unknown", 0, NULL), /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, - "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: to be moved to the device collection! */ - { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.4.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.3.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.4.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.3.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* Outlet page */ - /* Note: outlet.count is deduced, with guestimate_outlet_count() */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* Note: outlet.count is deduced, with guesstimate_outlet_count() */ + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), - { "outlet.current", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.2.0", NULL, 0, NULL }, - { "outlet.voltage", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.1.0", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.3.0", NULL, 0, NULL }, + snmp_info_default("outlet.current", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.2.0", NULL, 0, NULL), + snmp_info_default("outlet.voltage", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.1.0", NULL, 0, NULL), + snmp_info_default("outlet.power", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.3.0", NULL, 0, NULL), /* outlet template definition * Notes: * - indexes start from 1, ie outlet.1 => .1 * - the first definition is used to determine the base index (ie 0 or 1) * - outlet.count is estimated, based on the below OID iteration capabilities */ - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.1.%i.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", - NULL, SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_status_info[0] }, - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.1.%i.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", + NULL, SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_status_info[0]), + snmp_info_default("outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL), /* we use the same OID as outlet.n.status..., to expose switchability */ - { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", "yes", SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_switchability_info[0] }, + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", "yes", SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_switchability_info[0]), /* FIXME: need to be added to the namespace! */ - { "outlet.%i.delay.reboot", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.5.0", NULL, SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.delay.reboot", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.5.0", NULL, SU_OUTLET, NULL), /* "outlet1SequenceTime" is used for global sequence */ - { "outlet.%i.delay.start", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.4.0", NULL, SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.delay.start", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.4.0", NULL, SU_OUTLET, NULL), /* instant commands. */ /* FIXME: not exposed as "outlet.load...", or otherwise specific processing applies (template instanciation) */ - { "load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "1", SU_TYPE_CMD, NULL }, - { "load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "2", SU_TYPE_CMD, NULL }, - { "load.on.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "3", SU_TYPE_CMD, NULL }, - { "load.off.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "4", SU_TYPE_CMD, NULL }, + snmp_info_default("load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "1", SU_TYPE_CMD, NULL), + snmp_info_default("load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "2", SU_TYPE_CMD, NULL), + snmp_info_default("load.on.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "3", SU_TYPE_CMD, NULL), + snmp_info_default("load.off.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "4", SU_TYPE_CMD, NULL), /* WARNING: outlet 1 => index 2, so SU_CMD_OFFSET! */ - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "1", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "2", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "3", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "1", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL), + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "2", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL), + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "3", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; diff --git a/drivers/eaton-pdu-revelation-mib.c b/drivers/eaton-pdu-revelation-mib.c index 7c4e022048..6a162288f3 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) * ********************************************* */ @@ -55,61 +55,21 @@ #define AR_OID_OUTLET_STATUS AR_BASE_OID ".1.2.2.1.3" static info_lkp_t revelation_outlet_status_info[] = { - { -1, "error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "cycling" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "error"), + info_lkp_default(0, "off"), + info_lkp_default(1, "on"), + info_lkp_default(2, "cycling"), /* transitional status */ + info_lkp_sentinel }; /* Ugly hack: having the matching OID present means that the outlet is * switchable. So, it should not require this value lookup */ static info_lkp_t revelation_outlet_switchability_info[] = { - { -1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "yes"), + info_lkp_default(0, "yes"), + info_lkp_default(1, "yes"), + info_lkp_default(2, "yes"), + info_lkp_sentinel }; #define DO_OFF "0" @@ -126,60 +86,66 @@ 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 */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* Device collection */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, - "Eaton Powerware ePDU Managed", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_UNIT_MACADDR, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, + "Eaton Powerware ePDU Managed", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_UNIT_MACADDR, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* hardwareRev.0 = Integer: 26 */ /* FIXME: not compliant! to be RFC'ed */ - { "device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.6.1.1.7.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.6.1.1.7.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_FIRMREV, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.temperature", 0, 1, AR_OID_UNIT_CPUTEMPERATURE, NULL, 0, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_FIRMREV, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.temperature", 0, 1, AR_OID_UNIT_CPUTEMPERATURE, NULL, 0, NULL), /* Outlet collection */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, AR_OID_OUTLET_COUNT, "0", 0, NULL }, - { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.count", 0, 1, AR_OID_OUTLET_COUNT, "0", 0, NULL), + snmp_info_default("outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL), + snmp_info_default("outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL), + snmp_info_default("outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL), + snmp_info_default("outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL), /* outlet template definition * Caution: the index of the data start at 0, while the name is +1 * ie outlet.1 => .0 */ - { "outlet.%i.switchable", 0, 1, AR_OID_OUTLET_STATUS ".%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &revelation_outlet_switchability_info[0] }, - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_FLAG_OK | SU_OUTLET, &revelation_outlet_status_info[0] }, - { "outlet.%i.current", 0, 0.001, AR_OID_OUTLET_CURRENT ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.current.maximum", 0, 0.001, AR_OID_OUTLET_MAXCURRENT ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.realpower", 0, 1.0, AR_OID_OUTLET_ACTIVEPOWER ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.voltage", 0, 1.0, AR_OID_OUTLET_VOLTAGE ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.powerfactor", 0, 0.01, AR_OID_OUTLET_POWERFACTOR ".%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.power", 0, 1.0, AR_OID_OUTLET_APPARENTPOWER ".%i", NULL, SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.switchable", 0, 1, AR_OID_OUTLET_STATUS ".%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &revelation_outlet_switchability_info[0]), + snmp_info_default("outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_FLAG_OK | SU_OUTLET, &revelation_outlet_status_info[0]), + snmp_info_default("outlet.%i.current", 0, 0.001, AR_OID_OUTLET_CURRENT ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.current.maximum", 0, 0.001, AR_OID_OUTLET_MAXCURRENT ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.realpower", 0, 1.0, AR_OID_OUTLET_ACTIVEPOWER ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.voltage", 0, 1.0, AR_OID_OUTLET_VOLTAGE ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.powerfactor", 0, 0.01, AR_OID_OUTLET_POWERFACTOR ".%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.power", 0, 1.0, AR_OID_OUTLET_APPARENTPOWER ".%i", NULL, SU_OUTLET, NULL), /* FIXME: * - delay for startup/shutdown sequence @@ -191,25 +157,25 @@ static snmp_info_t eaton_aphel_revelation_mib[] = { /* Ambient collection */ /* We use critical levels, for both temperature and humidity, * since warning levels are also available! */ - { "ambient.temperature", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.2.1.3.0", NULL, SU_FLAG_OK, NULL }, - { "ambient.temperature.low", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.6.0", NULL, SU_FLAG_OK, NULL }, - { "ambient.temperature.high", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.7.0", NULL, SU_FLAG_OK, NULL }, - { "ambient.humidity", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, - { "ambient.humidity.low", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.6.0", NULL, SU_FLAG_OK, NULL }, - { "ambient.humidity.high", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.7.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.2.1.3.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.low", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.6.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.high", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.7.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.3.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.low", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.6.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.high", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.7.0", NULL, SU_FLAG_OK, NULL), /* instant commands. */ /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ /* no counterpart found! - { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, - { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, - { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, */ - { "outlet.%i.load.off", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.on", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.cycle", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL), */ + snmp_info_default("outlet.%i.load.off", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.cycle", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; diff --git a/drivers/eaton-ups-pwnm2-mib.c b/drivers/eaton-ups-pwnm2-mib.c new file mode 100644 index 0000000000..5347c06e83 --- /dev/null +++ b/drivers/eaton-ups-pwnm2-mib.c @@ -0,0 +1,810 @@ +/* eaton-ups-pwnm2-mib.c - data to monitor Powerware and Eaton NM2 UPS with NUT + * (using MIBs described in stdupsv1.mib and Xups.mib) + * Previously known as powerware-mib.c for "pw" mapping, + * later split into several subdrivers + * + * Copyright (C) + * 2005-2006 Olli Savia + * 2005-2006 Niels Baggesen + * 2015-2022 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-ups-pwnm2-mib.h" +#if WITH_SNMP_LKP_FUN +/* FIXME: shared helper code, need to be put in common */ +#include "eaton-pdu-marlin-helpers.h" +#endif + +#define PW_MIB_VERSION "0.106" + +/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) + * Eaton Gigabit Network Card (Genepi) */ +#define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" + +/* SNMP OIDs set */ +#define PW_OID_MFR_NAME "1.3.6.1.4.1.534.1.1.1.0" /* XUPS-MIB::xupsIdentManufacturer.0 */ +#define PW_OID_MODEL_NAME "1.3.6.1.4.1.534.1.1.2.0" /* XUPS-MIB::xupsIdentModel.0 */ +#define PW_OID_FIRMREV "1.3.6.1.4.1.534.1.1.3.0" /* XUPS-MIB::xupsIdentSoftwareVersion.0 */ + +#define PW_OID_BATT_RUNTIME "1.3.6.1.4.1.534.1.2.1.0" /* XUPS-MIB::xupsBatTimeRemaining.0 */ +#define PW_OID_BATT_VOLTAGE "1.3.6.1.4.1.534.1.2.2.0" /* XUPS-MIB::xupsBatVoltage.0 */ +#define PW_OID_BATT_CURRENT "1.3.6.1.4.1.534.1.2.3.0" /* XUPS-MIB::xupsBatCurrent.0 */ +#define PW_OID_BATT_CHARGE "1.3.6.1.4.1.534.1.2.4.0" /* XUPS-MIB::xupsBatCapacity.0 */ +#define PW_OID_BATT_STATUS "1.3.6.1.4.1.534.1.2.5.0" /* XUPS-MIB::xupsBatteryAbmStatus.0 */ + +#define PW_OID_IN_FREQUENCY "1.3.6.1.4.1.534.1.3.1.0" /* XUPS-MIB::xupsInputFrequency.0 */ +#define PW_OID_IN_LINE_BADS "1.3.6.1.4.1.534.1.3.2.0" /* XUPS-MIB::xupsInputLineBads.0 */ +#define PW_OID_IN_LINES "1.3.6.1.4.1.534.1.3.3.0" /* XUPS-MIB::xupsInputNumPhases.0 */ +#define PW_OID_IN_VOLTAGE "1.3.6.1.4.1.534.1.3.4.1.2" /* XUPS-MIB::xupsInputVoltage */ +#define PW_OID_IN_CURRENT "1.3.6.1.4.1.534.1.3.4.1.3" /* XUPS-MIB::xupsInputCurrent */ +#define PW_OID_IN_POWER "1.3.6.1.4.1.534.1.3.4.1.4" /* XUPS-MIB::xupsInputWatts */ + +#define PW_OID_OUT_LOAD "1.3.6.1.4.1.534.1.4.1.0" /* XUPS-MIB::xupsOutputLoad.0 */ +#define PW_OID_OUT_FREQUENCY "1.3.6.1.4.1.534.1.4.2.0" /* XUPS-MIB::xupsOutputFrequency.0 */ +#define PW_OID_OUT_LINES "1.3.6.1.4.1.534.1.4.3.0" /* XUPS-MIB::xupsOutputNumPhases.0 */ +#define PW_OID_OUT_VOLTAGE "1.3.6.1.4.1.534.1.4.4.1.2" /* XUPS-MIB::xupsOutputVoltage */ +#define PW_OID_OUT_CURRENT "1.3.6.1.4.1.534.1.4.4.1.3" /* XUPS-MIB::xupsOutputCurrent */ +#define PW_OID_OUT_POWER "1.3.6.1.4.1.534.1.4.4.1.4" /* XUPS-MIB::xupsOutputWatts */ +#define PW_OID_POWER_STATUS "1.3.6.1.4.1.534.1.4.5.0" /* XUPS-MIB::xupsOutputSource.0 */ + +#define PW_OID_BY_FREQUENCY "1.3.6.1.4.1.534.1.5.1.0" /* XUPS-MIB::xupsBypassFrequency.0 */ +#define PW_OID_BY_LINES "1.3.6.1.4.1.534.1.5.2.0" /* XUPS-MIB::xupsBypassNumPhases.0 */ +#define PW_OID_BY_VOLTAGE "1.3.6.1.4.1.534.1.5.3.1.2" /* XUPS-MIB::xupsBypassVoltage */ + +#define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ + +#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ +#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ +#define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ +#define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ +#define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ + +#define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ +#define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ +#define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ +#define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ + +#define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1.0" /* XUPS-MIB::xupsAlarms */ +#define PW_OID_ALARM_OB "1.3.6.1.4.1.534.1.7.3" /* XUPS-MIB::xupsOnBattery */ +#define PW_OID_ALARM_LB "1.3.6.1.4.1.534.1.7.4" /* XUPS-MIB::xupsLowBattery */ + +#define IETF_OID_AGENTREV "1.3.6.1.2.1.33.1.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion.0 */ +#define IETF_OID_IDENT "1.3.6.1.2.1.33.1.1.5.0" /* UPS-MIB::upsIdentName.0 */ +#define IETF_OID_CONF_OUT_VA "1.3.6.1.2.1.33.1.9.5.0" /* UPS-MIB::upsConfigOutputVA.0 */ +#define IETF_OID_CONF_RUNTIME_LOW "1.3.6.1.2.1.33.1.9.7.0" /* UPS-MIB::upsConfigLowBattTime.0 */ +#define IETF_OID_LOAD_LEVEL "1.3.6.1.2.1.33.1.4.4.1.5" /* UPS-MIB::upsOutputPercentLoad */ +#define IETF_OID_AUTO_RESTART "1.3.6.1.2.1.33.1.8.5.0" /* UPS-MIB::upsAutoRestart */ + +/* Delay before powering off in seconds */ +#define DEFAULT_OFFDELAY 30 +/* Delay before powering on in seconds */ +#define DEFAULT_ONDELAY 20 +/* Default shutdown.return delay in seconds */ +#define DEFAULT_SHUTDOWNDELAY 0 + +static info_lkp_t pw_alarm_ob[] = { + info_lkp_default(1, "OB"), + info_lkp_default(2, ""), + info_lkp_sentinel +}; + +static info_lkp_t pw_alarm_lb[] = { + info_lkp_default(1, "LB"), + info_lkp_default(2, ""), + info_lkp_sentinel +}; + +static info_lkp_t pw_pwr_info[] = { + info_lkp_default(1, ""), /* other */ + info_lkp_default(2, "OFF"), /* none */ + info_lkp_default(3, "OL"), /* normal */ + info_lkp_default(4, "BYPASS"), /* bypass */ + info_lkp_default(5, "OB"), /* battery */ + info_lkp_default(6, "OL BOOST"), /* booster */ + info_lkp_default(7, "OL TRIM"), /* reducer */ + info_lkp_default(8, "OL"), /* parallel capacity */ + info_lkp_default(9, "OL"), /* parallel redundancy */ + info_lkp_default(10, "OL"), /* high efficiency */ + + info_lkp_default(11, "BYPASS"), /* maintenanceBypass */ + info_lkp_default(11, "OL"), /* essMode */ + /* FIXME: is "11" correct here or in the line above? */ + + info_lkp_sentinel +}; + +/* FIXME: mapped to (experimental.)ups.type, but + * should be output.source or ups.mode (need RFC) + * to complement the above ups.status + * along with having ups.type as described hereafter*/ +/* FIXME: should be used by ups.mode or output.source (need RFC); + * Note: this define is not set via project options; code was hidden with + * original commit to "snmp-ups: support newer Genepi management cards"; + * un-hidden to make it "experimental.*" namespace during backporting + */ +#ifndef USE_PW_MODE_INFO +# define USE_PW_MODE_INFO 1 +#endif + +#if USE_PW_MODE_INFO +static info_lkp_t pw_mode_info[] = { + info_lkp_default(1, ""), + info_lkp_default(2, ""), + info_lkp_default(3, "normal"), + info_lkp_default(4, ""), + info_lkp_default(5, ""), + info_lkp_default(6, ""), + info_lkp_default(7, ""), + info_lkp_default(8, "parallel capacity"), + info_lkp_default(9, "parallel redundancy"), + info_lkp_default(10, "high efficiency"), + + /* Extended status values, + * FIXME: check for source and completion */ + info_lkp_default(240, ""), /* battery (0xF0) */ + info_lkp_default(100, ""), /* maintenanceBypass (0x64) */ + info_lkp_default(96, ""), /* Bypass (0x60) */ + info_lkp_default(81, "high efficiency"), /* high efficiency (0x51) */ + info_lkp_default(80, "normal"), /* normal (0x50) */ + info_lkp_default(64, ""), /* UPS supporting load, normal degraded mode (0x40) */ + info_lkp_default(16, ""), /* none (0x10) */ + info_lkp_sentinel +}; +#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[] = { + info_lkp_default(0x0000, ""), /* None; use the Table of Elements */ + info_lkp_default(0x0010, "Off-line switcher, Single Phase"), + info_lkp_default(0x0020, "Line-Interactive UPS, Single Phase"), + info_lkp_default(0x0021, "Line-Interactive UPS, Two Phase"), + info_lkp_default(0x0022, "Line-Interactive UPS, Three Phase"), + info_lkp_default(0x0030, "Dual AC Input, On-Line UPS, Single Phase"), + info_lkp_default(0x0031, "Dual AC Input, On-Line UPS, Two Phase"), + info_lkp_default(0x0032, "Dual AC Input, On-Line UPS, Three Phase"), + info_lkp_default(0x0040, "On-Line UPS, Single Phase"), + info_lkp_default(0x0041, "On-Line UPS, Two Phase"), + info_lkp_default(0x0042, "On-Line UPS, Three Phase"), + info_lkp_default(0x0050, "Parallel Redundant On-Line UPS, Single Phase"), + info_lkp_default(0x0051, "Parallel Redundant On-Line UPS, Two Phase"), + info_lkp_default(0x0052, "Parallel Redundant On-Line UPS, Three Phase"), + info_lkp_default(0x0060, "Parallel for Capacity On-Line UPS, Single Phase"), + info_lkp_default(0x0061, "Parallel for Capacity On-Line UPS, Two Phase"), + info_lkp_default(0x0062, "Parallel for Capacity On-Line UPS, Three Phase"), + info_lkp_default(0x0102, "System Bypass Module, Three Phase"), + info_lkp_default(0x0122, "Hot-Tie Cabinet, Three Phase"), + info_lkp_default(0x0200, "Outlet Controller, Single Phase"), + info_lkp_default(0x0222, "Dual AC Input Static Switch Module, 3 Phase"), + info_lkp_sentinel +}; + +/* Legacy implementation */ +static info_lkp_t pw_battery_abm_status[] = { + info_lkp_default(1, "CHRG"), + info_lkp_default(2, "DISCHRG"), +/* + info_lkp_default(3, "Floating"), + info_lkp_default(4, "Resting"), + info_lkp_default(5, "Unknown"), +*/ + info_lkp_sentinel +}; + +static info_lkp_t pw_abm_status_info[] = { + info_lkp_default(1, "charging"), + info_lkp_default(2, "discharging"), + info_lkp_default(3, "floating"), + info_lkp_default(4, "resting"), + info_lkp_default(5, "unknown"), /* Undefined - ABM is not activated */ + info_lkp_default(6, "disabled"), /* ABM Charger Disabled */ + info_lkp_sentinel +}; + +static info_lkp_t pw_batt_test_info[] = { + info_lkp_default(1, "Unknown"), + info_lkp_default(2, "Done and passed"), + info_lkp_default(3, "Done and error"), + info_lkp_default(4, "In progress"), + info_lkp_default(5, "Not supported"), + info_lkp_default(6, "Inhibited"), + info_lkp_default(7, "Scheduled"), + info_lkp_sentinel +}; + +static info_lkp_t pw_yes_no_info[] = { + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel +}; + +static info_lkp_t pw_outlet_status_info[] = { + info_lkp_default(1, "on"), + info_lkp_default(2, "off"), + info_lkp_default(3, "on"), /* pendingOff, transitional status */ + info_lkp_default(4, "off"), /* pendingOn, transitional status */ + /* info_lkp_default(5, ""), // unknown */ + /* info_lkp_default(6, ""), // reserved */ + info_lkp_default(7, "off"), /* Failed in Closed position */ + info_lkp_default(8, "on"), /* Failed in Open position */ + info_lkp_sentinel +}; + +static info_lkp_t pw_ambient_drycontacts_info[] = { + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "opened"), + info_lkp_default(2, "closed"), + info_lkp_default(3, "opened"), /* openWithNotice */ + info_lkp_default(4, "closed"), /* closedWithNotice */ + info_lkp_sentinel +}; + +#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[] = { + info_lkp_fun_vp2s(0, "dummy", eaton_sensor_temperature_unit_fun), + info_lkp_sentinel +}; + +static info_lkp_t pw_sensor_temperature_read_info[] = { + info_lkp_fun_vp2s(0, "dummy", su_temperature_read_fun), + info_lkp_sentinel +}; + +#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[] = { + info_lkp_default(0, "kelvin"), + info_lkp_default(1, "celsius"), + info_lkp_default(2, "fahrenheit"), + info_lkp_sentinel +}; + +#endif /* WITH_SNMP_LKP_FUN */ + +static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { + info_lkp_default(0, "normal-opened"), + info_lkp_default(1, "normal-closed"), + info_lkp_sentinel +}; + +static info_lkp_t pw_ambient_drycontacts_state_info[] = { + info_lkp_default(0, "inactive"), + info_lkp_default(1, "active"), + info_lkp_sentinel +}; + +static info_lkp_t pw_emp002_ambient_presence_info[] = { + info_lkp_default(0, "unknown"), + info_lkp_default(2, "yes"), /* communicationOK */ + info_lkp_default(3, "no"), /* communicationLost */ + info_lkp_sentinel +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ +static info_lkp_t pw_threshold_status_info[] = { + info_lkp_default(0, "good"), /* No threshold triggered */ + info_lkp_default(1, "warning-low"), /* Warning low threshold triggered */ + info_lkp_default(2, "critical-low"), /* Critical low threshold triggered */ + info_lkp_default(3, "warning-high"), /* Warning high threshold triggered */ + info_lkp_default(4, "critical-high"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ +static info_lkp_t pw_threshold_temperature_alarms_info[] = { + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low temperature warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low temperature critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high temperature warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high temperature critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +static info_lkp_t pw_threshold_humidity_alarms_info[] = { + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low humidity warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low humidity critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high humidity warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high humidity critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +/* Snmp2NUT lookup table */ + +static snmp_info_t pw_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 */ + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MFR_NAME, "", + SU_FLAG_STATIC, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MODEL_NAME, "", + SU_FLAG_STATIC, NULL), + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_FIRMREV, "", + 0, NULL), + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", + 0, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", + SU_FLAG_STATIC, NULL), + snmp_info_default("ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", + 0, NULL), + /* FIXME: should be removed in favor of output.power */ + snmp_info_default("ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", + 0, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputWatts.1.0; Value (Integer): 300 */ + snmp_info_default("ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL), + + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", + SU_STATUS_PWR, &pw_pwr_info[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", + SU_STATUS_BATT, &pw_alarm_ob[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_LB, "", + SU_STATUS_BATT, &pw_alarm_lb[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", + SU_STATUS_BATT, &pw_battery_abm_status[0]), +#if USE_PW_MODE_INFO + /* FIXME: should be ups.mode or output.source (need RFC) */ + /* Note: this define is not set via project options; code hidden with + * commit to "snmp-ups: support newer Genepi management cards" */ + snmp_info_default("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 */ + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0]), + /* FIXME: should be removed in favor of their output. equivalent! */ + snmp_info_default("ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL), + /* FIXME: should be removed in favor of output.power.nominal */ + snmp_info_default("ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", + 0, NULL), + /* XUPS-MIB::xupsEnvAmbientTemp.0 */ + snmp_info_default("ups.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.1.0", "", 0, NULL), + /* FIXME: These 2 data needs RFC! */ + /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ + snmp_info_default("ups.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.2.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ + snmp_info_default("ups.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.3.0", "", 0, NULL), + /* XUPS-MIB::xupsTestBatteryStatus */ + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.8.2.0", "", 0, &pw_batt_test_info[0]), + /* UPS-MIB::upsAutoRestart */ + snmp_info_default("ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, &pw_yes_no_info[0]), + /* XUPS-MIB::xupsBatteryAbmStatus.0 */ + snmp_info_default("battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.2.5.0", "", SU_STATUS_BATT, &pw_abm_status_info[0]), + + /* Battery page */ + snmp_info_default("battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "", + 0, NULL), + snmp_info_default("battery.runtime", 0, 1.0, PW_OID_BATT_RUNTIME, "", + 0, NULL), + snmp_info_default("battery.voltage", 0, 1.0, PW_OID_BATT_VOLTAGE, "", + 0, NULL), + snmp_info_default("battery.current", 0, 0.1, PW_OID_BATT_CURRENT, "", + 0, NULL), + snmp_info_default("battery.runtime.low", 0, 60.0, IETF_OID_CONF_RUNTIME_LOW, "", + 0, NULL), + snmp_info_default("battery.date", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.1.2.6.0", NULL, SU_FLAG_OK, &su_convert_to_iso_date_info[FUNMAP_USDATE_TO_ISODATE]), + + /* Output page */ + snmp_info_default("output.phases", 0, 1.0, PW_OID_OUT_LINES, "", 0, NULL), + /* XUPS-MIB::xupsOutputFrequency.0 */ + snmp_info_default("output.frequency", 0, 0.1, "1.3.6.1.4.1.534.1.4.2.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigOutputFreq.0 */ + snmp_info_default("output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL), + /* XUPS-MIB::xupsOutputVoltage.1 */ + snmp_info_default("output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputVoltage.1.0; Value (Integer): 230 */ + snmp_info_default("output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL), + /* XUPS-MIB::xupsConfigOutputVoltage.0 */ + snmp_info_default("output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ + snmp_info_default("output.voltage.low", 0, 1.0, ".1.3.6.1.4.1.534.1.10.6.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigHighOutputVoltageLimit.0 */ + snmp_info_default("output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL), + snmp_info_default("output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", + SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputCurrent.1.0; Value (Integer): 0 */ + snmp_info_default("output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", + SU_OUTPUT_1, NULL), + snmp_info_default("output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", + SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ + snmp_info_default("output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL), + /* Duplicate of "ups.realpower.nominal" + * FIXME: map either ups or output, but not both (or have an auto-remap) */ + snmp_info_default("output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL), + snmp_info_default("output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 1.0, PW_OID_OUT_CURRENT ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 1.0, PW_OID_OUT_CURRENT ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.realpower", 0, 1.0, PW_OID_OUT_POWER ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.realpower", 0, 1.0, PW_OID_OUT_POWER ".3", "", + SU_OUTPUT_3, NULL), + /* FIXME: should better be output.Lx.load */ + snmp_info_default("output.L1.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", + 0, NULL), + + /* Input page */ + snmp_info_default("input.phases", 0, 1.0, PW_OID_IN_LINES, "", + 0, NULL), + snmp_info_default("input.frequency", 0, 0.1, PW_OID_IN_FREQUENCY, "", + 0, NULL), + snmp_info_default("input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", + SU_INPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ + snmp_info_default("input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", + SU_INPUT_1, NULL), + + /* XUPS-MIB::xupsConfigInputVoltage.0 */ + snmp_info_default("input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL), + snmp_info_default("input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", + SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L1.current", 0, 1.0, PW_OID_IN_CURRENT ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 1.0, PW_OID_IN_CURRENT ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 1.0, PW_OID_IN_CURRENT ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L1.realpower", 0, 1.0, PW_OID_IN_POWER ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2.realpower", 0, 1.0, PW_OID_IN_POWER ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3.realpower", 0, 1.0, PW_OID_IN_POWER ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.quality", 0, 1.0, PW_OID_IN_LINE_BADS, "", + 0, NULL), + + /* FIXME: this segfaults! do we assume the same number of bypass phases as input phases? + snmp_info_default("input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", 0, NULL), */ + snmp_info_default("input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL), + snmp_info_default("input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", + SU_INPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsBypassVoltage.1.0; Value (Integer): 244 */ + snmp_info_default("input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", + SU_INPUT_1, NULL), + snmp_info_default("input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", + SU_INPUT_3, NULL), + + /* Outlet page */ + /* Master outlet id always equal to 0 */ + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL), + /* XUPS-MIB:: xupsSwitchable.0 */ + snmp_info_default("outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &pw_yes_no_info[0]), + /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL), + /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL), + /* This MIB does not provide outlets switchability info. So map to a nearby + OID, for data activation, and map all values to "yes" */ + snmp_info_default("outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ + snmp_info_default("outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &pw_outlet_status_info[0]), + + /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* XUPS-MIB::xupsEnvRemoteTemp.0 */ + snmp_info_default("ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.9.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteTempUpperLimit.0 */ + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.10.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidity.0 */ + snmp_info_default("ambient.humidity", 0, 1.0, "1.3.6.1.4.1.534.1.6.6.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidityLowerLimit.0 */ + snmp_info_default("ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ + snmp_info_default("ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL), + /* XUPS-MIB::xupsContactDescr.n */ + snmp_info_default("ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL), + snmp_info_default("ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL), + /* XUPS-MIB::xupsContactState.n */ + snmp_info_default("ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0]), + snmp_info_default("ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0]), + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + snmp_info_default("ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL), + /* CommunicationStatus.n */ + snmp_info_default("ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE, &pw_emp002_ambient_presence_info[0]), + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + snmp_info_default("ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorManufacturer.n */ + snmp_info_default("ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorModel.n */ + snmp_info_default("ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorSerialNumber.n */ + snmp_info_default("ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorUuid.n */ + snmp_info_default("ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorAddress.n */ + snmp_info_default("ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorFirmwareVersion.n */ + snmp_info_default("ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + snmp_info_default("ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0]), + + /* temperatureValue.n.1 */ +#if WITH_SNMP_LKP_FUN + snmp_info_default("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, + &pw_sensor_temperature_read_info[0]), +#else + snmp_info_default("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, + NULL), +#endif + + snmp_info_default("ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_temperature_alarms_info[0]), + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + snmp_info_default("ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdLowWarning.n.1 */ + snmp_info_default("ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdHighWarning.n.1 */ + snmp_info_default("ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdHighCritical.n.1 */ + snmp_info_default("ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityValue.n.1 */ + snmp_info_default("ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_humidity_alarms_info[0]), + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + snmp_info_default("ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdLowWarning.n.1 */ + snmp_info_default("ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdHighWarning.n.1 */ + snmp_info_default("ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdHighCritical.n.1 */ + snmp_info_default("ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* digitalInputName.n.{1,2} */ + snmp_info_default("ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL), + /* digitalInputPolarity.n */ + snmp_info_default("ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0]), + snmp_info_default("ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0]), + /* XUPS-MIB::xupsContactState.n */ + snmp_info_default("ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0]), + snmp_info_default("ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0]), + + /* instant commands */ + snmp_info_default("test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Shed load and restart when line power back on; cannot be canceled */ + snmp_info_default("shutdown.return", 0, DEFAULT_SHUTDOWNDELAY, PW_OID_CONT_LOAD_SHED_AND_RESTART, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ + snmp_info_default("shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* XUPS-MIB::xupsControlOutputOffDelay */ + /* load off after 1 sec, shortest possible delay; 0 cancels */ + snmp_info_default("load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* XUPS-MIB::xupsControlOutputOnDelay */ + /* load on after 1 sec, shortest possible delay; 0 cancels */ + snmp_info_default("load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, + SU_TYPE_CMD | SU_FLAG_OK, NULL), + + /* Delays handling: + * 0-n :Time in seconds until the command is issued + * -1:Cancel a pending Off/On command */ + /* XUPS-MIB::xupsRecepOffDelaySecs.n */ + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + snmp_info_default("outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), + + snmp_info_default("ups.alarms", 0, 1.0, PW_OID_ALARMS, "", + 0, NULL), + + /* end of structure. */ + snmp_info_sentinel +} ; + +static alarms_info_t pw_alarms[] = { + /* xupsLowBattery */ + { PW_OID_ALARM_LB, "LB", NULL }, + /* xupsOutputOverload */ + { ".1.3.6.1.4.1.534.1.7.7", "OVER", "Output overload!" }, + /* xupsInternalFailure */ + { ".1.3.6.1.4.1.534.1.7.8", NULL, "Internal failure!" }, + /* xupsBatteryDischarged */ + { ".1.3.6.1.4.1.534.1.7.9", NULL, "Battery discharged!" }, + /* xupsInverterFailure */ + { ".1.3.6.1.4.1.534.1.7.10", NULL, "Inverter failure!" }, + /* xupsOnBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.11", "BYPASS", "On bypass!" }, + /* xupsBypassNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.12", NULL, "Bypass not available!" }, + /* xupsOutputOff + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.13", "OFF", "Output off!" }, + /* xupsInputFailure + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.14", NULL, "Input failure!" }, + /* xupsBuildingAlarm + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.15", NULL, "Building alarm!" }, + /* xupsShutdownImminent */ + { ".1.3.6.1.4.1.534.1.7.16", NULL, "Shutdown imminent!" }, + /* xupsOnInverter + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.17", NULL, "On inverter!" }, + /* xupsBreakerOpen + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.20", NULL, "Breaker open!" }, + /* xupsAlarmBatteryBad */ + { ".1.3.6.1.4.1.534.1.7.23", "RB", "Battery bad!" }, + /* xupsOutputOffAsRequested + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.24", "OFF", "Output off as requested!" }, + /* xupsDiagnosticTestFailed + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.25", NULL, "Diagnostic test failure!" }, + /* xupsCommunicationsLost */ + { ".1.3.6.1.4.1.534.1.7.26", NULL, "Communication with UPS lost!" }, + /* xupsUpsShutdownPending */ + { ".1.3.6.1.4.1.534.1.7.27", NULL, "Shutdown pending!" }, + /* xupsAmbientTempBad */ + { ".1.3.6.1.4.1.534.1.7.29", NULL, "Bad ambient temperature!" }, + /* xupsLossOfRedundancy */ + { ".1.3.6.1.4.1.534.1.7.30", NULL, "Redundancy lost!" }, + /* xupsAlarmTempBad */ + { ".1.3.6.1.4.1.534.1.7.31", NULL, "Bad temperature!" }, + /* xupsAlarmChargerFailed */ + { ".1.3.6.1.4.1.534.1.7.32", NULL, "Charger failure!" }, + /* xupsAlarmFanFailure */ + { ".1.3.6.1.4.1.534.1.7.33", NULL, "Fan failure!" }, + /* xupsAlarmFuseFailure */ + { ".1.3.6.1.4.1.534.1.7.34", NULL, "Fuse failure!" }, + /* xupsPowerSwitchBad */ + { ".1.3.6.1.4.1.534.1.7.35", NULL, "Powerswitch failure!" }, + /* xupsModuleFailure */ + { ".1.3.6.1.4.1.534.1.7.36", NULL, "Parallel or composite module failure!" }, + /* xupsOnAlternatePowerSource + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.37", NULL, "Using alternative power source!" }, + /* xupsAltPowerNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.38", NULL, "Alternative power source unavailable!" }, + /* xupsRemoteTempBad */ + { ".1.3.6.1.4.1.534.1.7.40", NULL, "Bad remote temperature!" }, + /* xupsRemoteHumidityBad */ + { ".1.3.6.1.4.1.534.1.7.41", NULL, "Bad remote humidity!" }, + /* xupsAlarmOutputBad */ + { ".1.3.6.1.4.1.534.1.7.42", NULL, "Bad output condition!" }, + /* xupsAlarmAwaitingPower + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.43", NULL, "Awaiting power!" }, + /* xupsOnMaintenanceBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? + * FIXME: NUT currently doesn't distinguish between Maintenance and + * Automatic Bypass (both published as "ups.alarm: BYPASS) + * Should we make the distinction? */ + { ".1.3.6.1.4.1.534.1.7.44", "BYPASS", "On maintenance bypass!" }, + + + /* end of structure. */ + { NULL, NULL, NULL } +} ; + +mib2nut_info_t eaton_pw_nm2 = { "eaton_pw_nm2", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, POWERWARE_SYSOID , pw_alarms }; diff --git a/drivers/eaton-ups-pwnm2-mib.h b/drivers/eaton-ups-pwnm2-mib.h new file mode 100644 index 0000000000..e764fa7b19 --- /dev/null +++ b/drivers/eaton-ups-pwnm2-mib.h @@ -0,0 +1,9 @@ +#ifndef EATON_UPS_PWNM2_MIB_H +#define EATON_UPS_PWNM2_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_pw_nm2; + +#endif /* EATON_UPS_PWNM2_MIB_H */ diff --git a/drivers/eaton-ups-pxg-mib.c b/drivers/eaton-ups-pxg-mib.c new file mode 100644 index 0000000000..5d3830930d --- /dev/null +++ b/drivers/eaton-ups-pxg-mib.c @@ -0,0 +1,833 @@ +/* eaton-ups-pxg-mib.c - data to monitor Eaton / Powerware PXG UPS with NUT + * (using MIBs described in stdupsv1.mib and Xups.mib) + * Previously known as powerware-mib.c for "pw" mapping, + * later split into several subdrivers + * + * Copyright (C) + * 2005-2006 Olli Savia + * 2005-2006 Niels Baggesen + * 2015-2022 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-ups-pxg-mib.h" +#if WITH_SNMP_LKP_FUN +/* FIXME: shared helper code, need to be put in common */ +#include "eaton-pdu-marlin-helpers.h" +#endif + +#define EATON_PXG_MIB_VERSION "0.105" + +/* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ +#define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" + +/* SNMP OIDs set */ +#define PW_OID_MFR_NAME "1.3.6.1.4.1.534.1.1.1.0" /* XUPS-MIB::xupsIdentManufacturer.0 */ +#define PW_OID_MODEL_NAME "1.3.6.1.4.1.534.1.1.2.0" /* XUPS-MIB::xupsIdentModel.0 */ +#define PW_OID_FIRMREV "1.3.6.1.4.1.534.1.1.3.0" /* XUPS-MIB::xupsIdentSoftwareVersion.0 */ + +#define PW_OID_BATT_RUNTIME "1.3.6.1.4.1.534.1.2.1.0" /* XUPS-MIB::xupsBatTimeRemaining.0 */ +#define PW_OID_BATT_VOLTAGE "1.3.6.1.4.1.534.1.2.2.0" /* XUPS-MIB::xupsBatVoltage.0 */ +#define PW_OID_BATT_CURRENT "1.3.6.1.4.1.534.1.2.3.0" /* XUPS-MIB::xupsBatCurrent.0 */ +#define PW_OID_BATT_CHARGE "1.3.6.1.4.1.534.1.2.4.0" /* XUPS-MIB::xupsBatCapacity.0 */ +#define PW_OID_BATT_STATUS "1.3.6.1.4.1.534.1.2.5.0" /* XUPS-MIB::xupsBatteryAbmStatus.0 */ + +#define PW_OID_IN_FREQUENCY "1.3.6.1.4.1.534.1.3.1.0" /* XUPS-MIB::xupsInputFrequency.0 */ +#define PW_OID_IN_LINE_BADS "1.3.6.1.4.1.534.1.3.2.0" /* XUPS-MIB::xupsInputLineBads.0 */ +#define PW_OID_IN_LINES "1.3.6.1.4.1.534.1.3.3.0" /* XUPS-MIB::xupsInputNumPhases.0 */ +#define PW_OID_IN_VOLTAGE "1.3.6.1.4.1.534.1.3.4.1.2" /* XUPS-MIB::xupsInputVoltage */ +#define PW_OID_IN_CURRENT "1.3.6.1.4.1.534.1.3.4.1.3" /* XUPS-MIB::xupsInputCurrent */ +#define PW_OID_IN_POWER "1.3.6.1.4.1.534.1.3.4.1.4" /* XUPS-MIB::xupsInputWatts */ + +#define PW_OID_OUT_LOAD "1.3.6.1.4.1.534.1.4.1.0" /* XUPS-MIB::xupsOutputLoad.0 */ +#define PW_OID_OUT_FREQUENCY "1.3.6.1.4.1.534.1.4.2.0" /* XUPS-MIB::xupsOutputFrequency.0 */ +#define PW_OID_OUT_LINES "1.3.6.1.4.1.534.1.4.3.0" /* XUPS-MIB::xupsOutputNumPhases.0 */ +#define PW_OID_OUT_VOLTAGE "1.3.6.1.4.1.534.1.4.4.1.2" /* XUPS-MIB::xupsOutputVoltage */ +#define PW_OID_OUT_CURRENT "1.3.6.1.4.1.534.1.4.4.1.3" /* XUPS-MIB::xupsOutputCurrent */ +#define PW_OID_OUT_POWER "1.3.6.1.4.1.534.1.4.4.1.4" /* XUPS-MIB::xupsOutputWatts */ +#define PW_OID_POWER_STATUS "1.3.6.1.4.1.534.1.4.5.0" /* XUPS-MIB::xupsOutputSource.0 */ + +#define PW_OID_BY_FREQUENCY "1.3.6.1.4.1.534.1.5.1.0" /* XUPS-MIB::xupsBypassFrequency.0 */ +#define PW_OID_BY_LINES "1.3.6.1.4.1.534.1.5.2.0" /* XUPS-MIB::xupsBypassNumPhases.0 */ +#define PW_OID_BY_VOLTAGE "1.3.6.1.4.1.534.1.5.3.1.2" /* XUPS-MIB::xupsBypassVoltage */ + +#define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ + +#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ +#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ +#define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ +#define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ +#define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ + +#define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ +#define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ +#define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ +#define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ + +#define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1.0" /* XUPS-MIB::xupsAlarms */ +#define PW_OID_ALARM_OB "1.3.6.1.4.1.534.1.7.3" /* XUPS-MIB::xupsOnBattery */ +#define PW_OID_ALARM_LB "1.3.6.1.4.1.534.1.7.4" /* XUPS-MIB::xupsLowBattery */ + +#define IETF_OID_AGENTREV "1.3.6.1.2.1.33.1.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion.0 */ +#define IETF_OID_IDENT "1.3.6.1.2.1.33.1.1.5.0" /* UPS-MIB::upsIdentName.0 */ +#define IETF_OID_CONF_OUT_VA "1.3.6.1.2.1.33.1.9.5.0" /* UPS-MIB::upsConfigOutputVA.0 */ +#define IETF_OID_CONF_RUNTIME_LOW "1.3.6.1.2.1.33.1.9.7.0" /* UPS-MIB::upsConfigLowBattTime.0 */ +#define IETF_OID_LOAD_LEVEL "1.3.6.1.2.1.33.1.4.4.1.5" /* UPS-MIB::upsOutputPercentLoad */ +#define IETF_OID_AUTO_RESTART "1.3.6.1.2.1.33.1.8.5.0" /* UPS-MIB::upsAutoRestart */ + +/* Delay before powering off in seconds */ +#define DEFAULT_OFFDELAY 30 +/* Delay before powering on in seconds */ +#define DEFAULT_ONDELAY 20 +/* Default shutdown.return delay in seconds */ +#define DEFAULT_SHUTDOWNDELAY 0 + +static info_lkp_t eaton_pxg_alarm_ob[] = { + info_lkp_default(1, "OB"), + info_lkp_default(2, ""), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_alarm_lb[] = { + info_lkp_default(1, "LB"), + info_lkp_default(2, ""), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_pwr_info[] = { + info_lkp_default(0, "OFF"), /* off */ + info_lkp_default(1, "OL"), /* systemNormal */ + info_lkp_default(2, "OL"), /* systemNormalUPSRedundant */ + info_lkp_default(3, "OL"), /* systemNormalNotRedundant */ + info_lkp_default(4, "OB"), /* systemOnDCSource*/ + info_lkp_default(5, "OB LB"), /* systemOnDCSourceShutdownImminent */ + info_lkp_default(6, "OL"), /* systemNormalBypassNotAvailable */ + info_lkp_default(7, "OL"), /* systemNormalOnLine */ + info_lkp_default(8, "OL"), /* systemNormalEnergySaverSystem */ + info_lkp_default(9, "OL"), /* systemNormalVMMS */ + info_lkp_default(10, "OL"), /* systemNormalHRS */ + info_lkp_default(13, "OL OVER"), /* outputOverload */ + info_lkp_default(14, "OL TRIM"), /* systemNormalOnBuck */ + info_lkp_default(15, "OL BOOST"), /* systemNormalOnBoost */ + info_lkp_default(16, "BYPASS"), /* onBypass */ + info_lkp_default(17, "BYPASS"), /* onBypassStarting */ + info_lkp_default(18, "BYPASS"), /* onBypassReady */ + info_lkp_default(32, "BYPASS"), /* onMaintenanceBypass */ + info_lkp_default(33, "OL BYPASS"), /* onMBSUPSOnLine */ + info_lkp_default(34, "BYPASS"), /* onMBSUPSOnBypass */ + info_lkp_default(35, "OFF BYPASS"), /* onMBSUPSOff */ + info_lkp_default(36, "OB BYPASS"), /* onMBSUPSOnBattery */ + info_lkp_default(37, "OL"), /* onMBSUPSOnLineESS */ + info_lkp_default(38, "OL"), /* onMBSUPSOnLineVMMS */ + info_lkp_default(39, "OL"), /* onMBSUPSOnLineHRS */ + info_lkp_default(40, "OL"), /* onMBSStarting */ + info_lkp_default(41, "OL"), /* onMBSReady */ + info_lkp_default(48, "OFF"), /* loadOff */ + info_lkp_default(49, "OFF"), /* loadOffStarting */ + info_lkp_default(50, "OFF"), /* loadOffReady */ + info_lkp_default(64, "OL"), /* supportingLoad */ + info_lkp_default(80, "OL"), /* systemNormalSP */ + info_lkp_default(81, "OL"), /* systemNormalEnergySaverSystemSP */ + info_lkp_default(96, "BYPASS"), /* systemOnBypassSP */ + info_lkp_default(100, "BYPASS"), /* systemOnManualMaintenanceBypassSP (0x64) */ + info_lkp_default(224, "OL OVER"), /* loadSegmentOverload (0x64) */ + info_lkp_default(240, "OB"), /* systemOnDCSourceSP */ + info_lkp_default(241, "OFF"), /* systemOffSP */ + info_lkp_sentinel +}; + +/* FIXME: mapped to (experimental.)ups.type, but + * should be output.source or ups.mode (need RFC) + * to complement the above ups.status + * along with having ups.type as described hereafter*/ +/* FIXME: should be used by ups.mode or output.source (need RFC); + * Note: this define is not set via project options; code was hidden with + * original commit to "snmp-ups: support newer Genepi management cards"; + * un-hidden to make it "experimental.*" namespace during backporting + */ +#ifndef USE_PW_MODE_INFO +# define USE_PW_MODE_INFO 1 +#endif + +#if USE_PW_MODE_INFO +static info_lkp_t eaton_pxg_mode_info[] = { + info_lkp_default(1, ""), + info_lkp_default(2, ""), + info_lkp_default(3, "normal"), + info_lkp_default(4, ""), + info_lkp_default(5, ""), + info_lkp_default(6, ""), + info_lkp_default(7, ""), + info_lkp_default(8, "parallel capacity"), + info_lkp_default(9, "parallel redundancy"), + info_lkp_default(10, "high efficiency"), + + /* Extended status values, + * FIXME: check for source and completion */ + info_lkp_default(240, ""), /* battery (0xF0) */ + info_lkp_default(100, ""), /* maintenanceBypass (0x64) */ + info_lkp_default(96, ""), /* Bypass (0x60) */ + info_lkp_default(81, "high efficiency"), /* high efficiency (0x51) */ + info_lkp_default(80, "normal"), /* normal (0x50) */ + info_lkp_default(64, ""), /* UPS supporting load, normal degraded mode (0x40) */ + info_lkp_default(16, ""), /* none (0x10) */ + info_lkp_sentinel +}; +#endif /* USE_PW_MODE_INFO */ + +/* FIXME: may be standardized + * extracted from bcmxcp.c->BCMXCP_TOPOLOGY_*, Make some common definitions */ +static info_lkp_t eaton_pxg_topology_info[] = { + info_lkp_default(0x0000, ""), /* None; use the Table of Elements */ + info_lkp_default(0x0010, "Off-line switcher, Single Phase"), + info_lkp_default(0x0020, "Line-Interactive UPS, Single Phase"), + info_lkp_default(0x0021, "Line-Interactive UPS, Two Phase"), + info_lkp_default(0x0022, "Line-Interactive UPS, Three Phase"), + info_lkp_default(0x0030, "Dual AC Input, On-Line UPS, Single Phase"), + info_lkp_default(0x0031, "Dual AC Input, On-Line UPS, Two Phase"), + info_lkp_default(0x0032, "Dual AC Input, On-Line UPS, Three Phase"), + info_lkp_default(0x0040, "On-Line UPS, Single Phase"), + info_lkp_default(0x0041, "On-Line UPS, Two Phase"), + info_lkp_default(0x0042, "On-Line UPS, Three Phase"), + info_lkp_default(0x0050, "Parallel Redundant On-Line UPS, Single Phase"), + info_lkp_default(0x0051, "Parallel Redundant On-Line UPS, Two Phase"), + info_lkp_default(0x0052, "Parallel Redundant On-Line UPS, Three Phase"), + info_lkp_default(0x0060, "Parallel for Capacity On-Line UPS, Single Phase"), + info_lkp_default(0x0061, "Parallel for Capacity On-Line UPS, Two Phase"), + info_lkp_default(0x0062, "Parallel for Capacity On-Line UPS, Three Phase"), + info_lkp_default(0x0102, "System Bypass Module, Three Phase"), + info_lkp_default(0x0122, "Hot-Tie Cabinet, Three Phase"), + info_lkp_default(0x0200, "Outlet Controller, Single Phase"), + info_lkp_default(0x0222, "Dual AC Input Static Switch Module, 3 Phase"), + info_lkp_sentinel +}; + +/* Legacy implementation */ +static info_lkp_t eaton_pxg_battery_abm_status[] = { + info_lkp_default(1, "CHRG"), + info_lkp_default(2, "DISCHRG"), +/* + info_lkp_default(3, "Floating"), + info_lkp_default(4, "Resting"), + info_lkp_default(5, "Unknown"), +*/ + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_abm_status_info[] = { + info_lkp_default(1, "charging"), + info_lkp_default(2, "discharging"), + info_lkp_default(3, "floating"), + info_lkp_default(4, "resting"), + info_lkp_default(5, "unknown"), /* Undefined - ABM is not activated */ + info_lkp_default(6, "disabled"), /* ABM Charger Disabled */ + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_batt_test_info[] = { + info_lkp_default(1, "Unknown"), + info_lkp_default(2, "Done and passed"), + info_lkp_default(3, "Done and error"), + info_lkp_default(4, "In progress"), + info_lkp_default(5, "Not supported"), + info_lkp_default(6, "Inhibited"), + info_lkp_default(7, "Scheduled"), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_yes_no_info[] = { + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_outlet_status_info[] = { + info_lkp_default(1, "on"), + info_lkp_default(2, "off"), + info_lkp_default(3, "on"), /* pendingOff, transitional status */ + info_lkp_default(4, "off"), /* pendingOn, transitional status */ + /* info_lkp_default(5, ""), // unknown */ + /* info_lkp_default(6, ""), // reserved */ + info_lkp_default(7, "off"), /* Failed in Closed position */ + info_lkp_default(8, "on"), /* Failed in Open position */ + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_ambient_drycontacts_info[] = { + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "opened"), + info_lkp_default(2, "closed"), + info_lkp_default(3, "opened"), /* openWithNotice */ + info_lkp_default(4, "closed"), /* closedWithNotice */ + info_lkp_sentinel +}; + +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +}; +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + +static info_lkp_t eaton_pxg_sensor_temperature_unit_info[] = { + info_lkp_fun_vp2s(0, "dummy", eaton_sensor_temperature_unit_fun), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_sensor_temperature_read_info[] = { + info_lkp_fun_vp2s(0, "dummy", su_temperature_read_fun), + info_lkp_sentinel +}; + +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t eaton_pxg_sensor_temperature_unit_info[] = { + info_lkp_default(0, "kelvin"), + info_lkp_default(1, "celsius"), + info_lkp_default(2, "fahrenheit"), + info_lkp_sentinel +}; + +#endif /* WITH_SNMP_LKP_FUN */ + +static info_lkp_t eaton_pxg_ambient_drycontacts_polarity_info[] = { + info_lkp_default(0, "normal-opened"), + info_lkp_default(1, "normal-closed"), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_ambient_drycontacts_state_info[] = { + info_lkp_default(0, "inactive"), + info_lkp_default(1, "active"), + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_emp002_ambient_presence_info[] = { + info_lkp_default(0, "unknown"), + info_lkp_default(2, "yes"), /* communicationOK */ + info_lkp_default(3, "no"), /* communicationLost */ + info_lkp_sentinel +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ +static info_lkp_t eaton_pxg_threshold_status_info[] = { + info_lkp_default(0, "good"), /* No threshold triggered */ + info_lkp_default(1, "warning-low"), /* Warning low threshold triggered */ + info_lkp_default(2, "critical-low"), /* Critical low threshold triggered */ + info_lkp_default(3, "warning-high"), /* Warning high threshold triggered */ + info_lkp_default(4, "critical-high"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ +static info_lkp_t eaton_pxg_threshold_temperature_alarms_info[] = { + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low temperature warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low temperature critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high temperature warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high temperature critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +static info_lkp_t eaton_pxg_threshold_humidity_alarms_info[] = { + info_lkp_default(0, ""), /* No threshold triggered */ + info_lkp_default(1, "low humidity warning!"), /* Warning low threshold triggered */ + info_lkp_default(2, "low humidity critical!"), /* Critical low threshold triggered */ + info_lkp_default(3, "high humidity warning!"), /* Warning high threshold triggered */ + info_lkp_default(4, "high humidity critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel +}; + +/* Snmp2NUT lookup table */ + +static snmp_info_t eaton_pxg_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 */ + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MFR_NAME, "", + SU_FLAG_STATIC, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MODEL_NAME, "", + SU_FLAG_STATIC, NULL), + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_FIRMREV, "", + 0, NULL), + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", + 0, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", + SU_FLAG_STATIC, NULL), + snmp_info_default("ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", + 0, NULL), + /* FIXME: should be removed in favor of output.power */ + snmp_info_default("ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", + 0, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputWatts.1.0; Value (Integer): 300 */ + snmp_info_default("ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL), + + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", + SU_STATUS_PWR, &eaton_pxg_pwr_info[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", + SU_STATUS_BATT, &eaton_pxg_alarm_ob[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_LB, "", + SU_STATUS_BATT, &eaton_pxg_alarm_lb[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", + SU_STATUS_BATT, &eaton_pxg_battery_abm_status[0]), + +#if USE_PW_MODE_INFO + /* FIXME: should be ups.mode or output.source (need RFC) */ + /* Note: this define is not set via project options; code hidden with + * commit to "snmp-ups: support newer Genepi management cards" */ + snmp_info_default("experimental.ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", + SU_FLAG_STATIC | SU_FLAG_OK, &eaton_pxg_mode_info[0]), +#endif /* USE_PW_MODE_INFO */ + /* xupsTopologyType.0; Value (Integer): 32 */ + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, &eaton_pxg_topology_info[0]), + /* FIXME: should be removed in favor of their output. equivalent! */ + snmp_info_default("ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL), + /* FIXME: should be removed in favor of output.power.nominal */ + snmp_info_default("ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", + 0, NULL), + /* XUPS-MIB::xupsEnvAmbientTemp.0 */ + snmp_info_default("ups.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.1.0", "", 0, NULL), + /* FIXME: These 2 data needs RFC! */ + /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ + snmp_info_default("ups.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.2.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ + snmp_info_default("ups.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.3.0", "", 0, NULL), + /* XUPS-MIB::xupsTestBatteryStatus */ + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.8.2.0", "", 0, &eaton_pxg_batt_test_info[0]), + /* UPS-MIB::upsAutoRestart */ + snmp_info_default("ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, &eaton_pxg_yes_no_info[0]), + /* XUPS-MIB::xupsBatteryAbmStatus.0 */ + snmp_info_default("battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.2.5.0", "", SU_STATUS_BATT, &eaton_pxg_abm_status_info[0]), + + /* Battery page */ + snmp_info_default("battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "", + 0, NULL), + snmp_info_default("battery.runtime", 0, 1.0, PW_OID_BATT_RUNTIME, "", + 0, NULL), + snmp_info_default("battery.voltage", 0, 1.0, PW_OID_BATT_VOLTAGE, "", + 0, NULL), + snmp_info_default("battery.current", 0, 0.1, PW_OID_BATT_CURRENT, "", + 0, NULL), + snmp_info_default("battery.runtime.low", 0, 60.0, IETF_OID_CONF_RUNTIME_LOW, "", + 0, NULL), + snmp_info_default("battery.date", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.1.2.6.0", NULL, SU_FLAG_OK, &su_convert_to_iso_date_info[FUNMAP_USDATE_TO_ISODATE]), + + /* Output page */ + snmp_info_default("output.phases", 0, 1.0, PW_OID_OUT_LINES, "", 0, NULL), + /* XUPS-MIB::xupsOutputFrequency.0 */ + snmp_info_default("output.frequency", 0, 0.1, "1.3.6.1.4.1.534.1.4.2.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigOutputFreq.0 */ + snmp_info_default("output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL), + /* XUPS-MIB::xupsOutputVoltage.1 */ + snmp_info_default("output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputVoltage.1.0; Value (Integer): 230 */ + snmp_info_default("output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL), + /* XUPS-MIB::xupsConfigOutputVoltage.0 */ + snmp_info_default("output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ + snmp_info_default("output.voltage.low", 0, 1.0, ".1.3.6.1.4.1.534.1.10.6.0", "", 0, NULL), + /* XUPS-MIB::xupsConfigHighOutputVoltageLimit.0 */ + snmp_info_default("output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL), + snmp_info_default("output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", + SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputCurrent.1.0; Value (Integer): 0 */ + snmp_info_default("output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", + SU_OUTPUT_1, NULL), + snmp_info_default("output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", + SU_OUTPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ + snmp_info_default("output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL), + /* Duplicate of "ups.realpower.nominal" + * FIXME: map either ups or output, but not both (or have an auto-remap) */ + snmp_info_default("output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL), + snmp_info_default("output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 1.0, PW_OID_OUT_CURRENT ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 1.0, PW_OID_OUT_CURRENT ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.realpower", 0, 1.0, PW_OID_OUT_POWER ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.realpower", 0, 1.0, PW_OID_OUT_POWER ".3", "", + SU_OUTPUT_3, NULL), + /* FIXME: should better be output.Lx.load */ + snmp_info_default("output.L1.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".1", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".2", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", + SU_OUTPUT_3, NULL), + snmp_info_default("output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", + 0, NULL), + + /* Input page */ + snmp_info_default("input.phases", 0, 1.0, PW_OID_IN_LINES, "", + 0, NULL), + snmp_info_default("input.frequency", 0, 0.1, PW_OID_IN_FREQUENCY, "", + 0, NULL), + snmp_info_default("input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", + SU_INPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ + snmp_info_default("input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", + SU_INPUT_1, NULL), + + /* XUPS-MIB::xupsConfigInputVoltage.0 */ + snmp_info_default("input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL), + snmp_info_default("input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", + SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L1.current", 0, 1.0, PW_OID_IN_CURRENT ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 1.0, PW_OID_IN_CURRENT ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 1.0, PW_OID_IN_CURRENT ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L1.realpower", 0, 1.0, PW_OID_IN_POWER ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L2.realpower", 0, 1.0, PW_OID_IN_POWER ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.L3.realpower", 0, 1.0, PW_OID_IN_POWER ".3", "", + SU_INPUT_3, NULL), + snmp_info_default("input.quality", 0, 1.0, PW_OID_IN_LINE_BADS, "", + 0, NULL), + + /* FIXME: this segfaults! do we assume the same number of bypass phases as input phases? + snmp_info_default("input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", 0, NULL), */ + snmp_info_default("input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL), + snmp_info_default("input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", + SU_INPUT_1, NULL), + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsBypassVoltage.1.0; Value (Integer): 244 */ + snmp_info_default("input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", + SU_INPUT_1, NULL), + snmp_info_default("input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", + SU_INPUT_3, NULL), + snmp_info_default("input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", + SU_INPUT_3, NULL), + snmp_info_default("input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", + SU_INPUT_3, NULL), + + /* Outlet page */ + /* Master outlet id always equal to 0 */ + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL), + /* XUPS-MIB:: xupsSwitchable.0 */ + snmp_info_default("outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &eaton_pxg_yes_no_info[0]), + /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL), + /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL), + /* This MIB does not provide outlets switchability info. So map to a nearby + OID, for data activation, and map all values to "yes" */ + snmp_info_default("outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ + snmp_info_default("outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &eaton_pxg_outlet_status_info[0]), + + /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* XUPS-MIB::xupsEnvRemoteTemp.0 */ + snmp_info_default("ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.9.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteTempUpperLimit.0 */ + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.10.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidity.0 */ + snmp_info_default("ambient.humidity", 0, 1.0, "1.3.6.1.4.1.534.1.6.6.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidityLowerLimit.0 */ + snmp_info_default("ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL), + /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ + snmp_info_default("ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL), + /* XUPS-MIB::xupsContactDescr.n */ + snmp_info_default("ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL), + snmp_info_default("ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL), + /* XUPS-MIB::xupsContactState.n */ + snmp_info_default("ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &eaton_pxg_ambient_drycontacts_info[0]), + snmp_info_default("ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &eaton_pxg_ambient_drycontacts_info[0]), + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + snmp_info_default("ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL), + /* CommunicationStatus.n */ + snmp_info_default("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, &eaton_pxg_emp002_ambient_presence_info[0]), + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + snmp_info_default("ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorManufacturer.n */ + snmp_info_default("ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorModel.n */ + snmp_info_default("ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorSerialNumber.n */ + snmp_info_default("ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorUuid.n */ + snmp_info_default("ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorAddress.n */ + snmp_info_default("ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* sensorFirmwareVersion.n */ + snmp_info_default("ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + snmp_info_default("ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &eaton_pxg_sensor_temperature_unit_info[0]), + + /* temperatureValue.n.1 */ +#if WITH_SNMP_LKP_FUN + snmp_info_default("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, + &eaton_pxg_sensor_temperature_read_info[0]), +#else + snmp_info_default("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, + NULL), +#endif + + snmp_info_default("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, &eaton_pxg_threshold_status_info[0]), + snmp_info_default("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, &eaton_pxg_threshold_temperature_alarms_info[0]), + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + snmp_info_default("ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdLowWarning.n.1 */ + snmp_info_default("ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdHighWarning.n.1 */ + snmp_info_default("ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* temperatureThresholdHighCritical.n.1 */ + snmp_info_default("ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityValue.n.1 */ + snmp_info_default("ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("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, &eaton_pxg_threshold_status_info[0]), + snmp_info_default("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, &eaton_pxg_threshold_humidity_alarms_info[0]), + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + snmp_info_default("ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdLowWarning.n.1 */ + snmp_info_default("ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdHighWarning.n.1 */ + snmp_info_default("ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* humidityThresholdHighCritical.n.1 */ + snmp_info_default("ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + /* digitalInputName.n.{1,2} */ + snmp_info_default("ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL), + snmp_info_default("ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL), + /* digitalInputPolarity.n */ + snmp_info_default("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, &eaton_pxg_ambient_drycontacts_polarity_info[0]), + snmp_info_default("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, &eaton_pxg_ambient_drycontacts_polarity_info[0]), + /* XUPS-MIB::xupsContactState.n */ + snmp_info_default("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, &eaton_pxg_ambient_drycontacts_state_info[0]), + snmp_info_default("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, &eaton_pxg_ambient_drycontacts_state_info[0]), + + /* instant commands */ + snmp_info_default("test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Shed load and restart when line power back on; cannot be canceled */ + snmp_info_default("shutdown.return", 0, DEFAULT_SHUTDOWNDELAY, PW_OID_CONT_LOAD_SHED_AND_RESTART, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ + snmp_info_default("shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* XUPS-MIB::xupsControlOutputOffDelay */ + /* load off after 1 sec, shortest possible delay; 0 cancels */ + snmp_info_default("load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* XUPS-MIB::xupsControlOutputOnDelay */ + /* load on after 1 sec, shortest possible delay; 0 cancels */ + snmp_info_default("load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", + SU_TYPE_CMD | SU_FLAG_OK, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, + SU_TYPE_CMD | SU_FLAG_OK, NULL), + + /* Delays handling: + * 0-n :Time in seconds until the command is issued + * -1:Cancel a pending Off/On command */ + /* XUPS-MIB::xupsRecepOffDelaySecs.n */ + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL), + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + snmp_info_default("outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL), + + snmp_info_default("ups.alarms", 0, 1.0, PW_OID_ALARMS, "", + 0, NULL), + + /* end of structure. */ + snmp_info_sentinel +} ; + +static alarms_info_t eaton_pxg_alarms[] = { + /* xupsLowBattery */ + { PW_OID_ALARM_LB, "LB", NULL }, + /* xupsOutputOverload */ + { ".1.3.6.1.4.1.534.1.7.7", "OVER", "Output overload!" }, + /* xupsInternalFailure */ + { ".1.3.6.1.4.1.534.1.7.8", NULL, "Internal failure!" }, + /* xupsBatteryDischarged */ + { ".1.3.6.1.4.1.534.1.7.9", NULL, "Battery discharged!" }, + /* xupsInverterFailure */ + { ".1.3.6.1.4.1.534.1.7.10", NULL, "Inverter failure!" }, + /* xupsOnBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.11", "BYPASS", "On bypass!" }, + /* xupsBypassNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.12", NULL, "Bypass not available!" }, + /* xupsOutputOff + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.13", "OFF", "Output off!" }, + /* xupsInputFailure + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.14", NULL, "Input failure!" }, + /* xupsBuildingAlarm + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.15", NULL, "Building alarm!" }, + /* xupsShutdownImminent */ + { ".1.3.6.1.4.1.534.1.7.16", NULL, "Shutdown imminent!" }, + /* xupsOnInverter + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.17", NULL, "On inverter!" }, + /* xupsBreakerOpen + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.20", NULL, "Breaker open!" }, + /* xupsAlarmBatteryBad */ + { ".1.3.6.1.4.1.534.1.7.23", "RB", "Battery bad!" }, + /* xupsOutputOffAsRequested + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.24", "OFF", "Output off as requested!" }, + /* xupsDiagnosticTestFailed + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.25", NULL, "Diagnostic test failure!" }, + /* xupsCommunicationsLost */ + { ".1.3.6.1.4.1.534.1.7.26", NULL, "Communication with UPS lost!" }, + /* xupsUpsShutdownPending */ + { ".1.3.6.1.4.1.534.1.7.27", NULL, "Shutdown pending!" }, + /* xupsAmbientTempBad */ + { ".1.3.6.1.4.1.534.1.7.29", NULL, "Bad ambient temperature!" }, + /* xupsLossOfRedundancy */ + { ".1.3.6.1.4.1.534.1.7.30", NULL, "Redundancy lost!" }, + /* xupsAlarmTempBad */ + { ".1.3.6.1.4.1.534.1.7.31", NULL, "Bad temperature!" }, + /* xupsAlarmChargerFailed */ + { ".1.3.6.1.4.1.534.1.7.32", NULL, "Charger failure!" }, + /* xupsAlarmFanFailure */ + { ".1.3.6.1.4.1.534.1.7.33", NULL, "Fan failure!" }, + /* xupsAlarmFuseFailure */ + { ".1.3.6.1.4.1.534.1.7.34", NULL, "Fuse failure!" }, + /* xupsPowerSwitchBad */ + { ".1.3.6.1.4.1.534.1.7.35", NULL, "Powerswitch failure!" }, + /* xupsModuleFailure */ + { ".1.3.6.1.4.1.534.1.7.36", NULL, "Parallel or composite module failure!" }, + /* xupsOnAlternatePowerSource + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.37", NULL, "Using alternative power source!" }, + /* xupsAltPowerNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.38", NULL, "Alternative power source unavailable!" }, + /* xupsRemoteTempBad */ + { ".1.3.6.1.4.1.534.1.7.40", NULL, "Bad remote temperature!" }, + /* xupsRemoteHumidityBad */ + { ".1.3.6.1.4.1.534.1.7.41", NULL, "Bad remote humidity!" }, + /* xupsAlarmOutputBad */ + { ".1.3.6.1.4.1.534.1.7.42", NULL, "Bad output condition!" }, + /* xupsAlarmAwaitingPower + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.43", NULL, "Awaiting power!" }, + /* xupsOnMaintenanceBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? + * FIXME: NUT currently doesn't distinguish between Maintenance and + * Automatic Bypass (both published as "ups.alarm: BYPASS) + * Should we make the distinction? */ + { ".1.3.6.1.4.1.534.1.7.44", "BYPASS", "On maintenance bypass!" }, + + + /* end of structure. */ + { NULL, NULL, NULL } +} ; + +mib2nut_info_t eaton_pxg_ups = { "eaton_pxg_ups", EATON_PXG_MIB_VERSION, NULL, PW_OID_MODEL_NAME, eaton_pxg_mib, EATON_PXGX_SYSOID , eaton_pxg_alarms }; diff --git a/drivers/powerware-mib.h b/drivers/eaton-ups-pxg-mib.h similarity index 64% rename from drivers/powerware-mib.h rename to drivers/eaton-ups-pxg-mib.h index 7189a247e0..78b0bc8d3f 100644 --- a/drivers/powerware-mib.h +++ b/drivers/eaton-ups-pxg-mib.h @@ -4,7 +4,6 @@ #include "main.h" #include "snmp-ups.h" -extern mib2nut_info_t powerware; -extern mib2nut_info_t pxgx_ups; +extern mib2nut_info_t eaton_pxg_ups; #endif /* POWERWARE_MIB_H */ diff --git a/drivers/emerson-avocent-pdu-mib.c b/drivers/emerson-avocent-pdu-mib.c index 72459c1083..6111cff36b 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.30" #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" @@ -60,149 +60,103 @@ #endif static info_lkp_t avocent_outlet_status_info[] = { - { 1, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, + info_lkp_default(1, "off"), + info_lkp_default(2, "on"), /* - { 3, "offLocked" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "onLocked" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "offCycle" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "onPendingOff" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "offPendingOn" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "onPendingCycle" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "notSet" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "onFixed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 11, "offShutdown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 12, "tripped" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, + info_lkp_default(3, "offLocked"), + info_lkp_default(4, "onLocked"), + info_lkp_default(5, "offCycle"), + info_lkp_default(6, "onPendingOff"), + info_lkp_default(7, "offPendingOn"), + info_lkp_default(8, "onPendingCycle"), + info_lkp_default(9, "notSet"), + info_lkp_default(10, "onFixed"), + info_lkp_default(11, "offShutdown"), + info_lkp_default(12, "tripped"), */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_sentinel }; static snmp_info_t emerson_avocent_pdu_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.5.3.1.5.1.%i", /* EMERSON_AVOCENT_OID_MODEL_NAME */ - "Avocent SNMP PDU", SU_FLAG_ABSENT | SU_FLAG_OK | SU_FLAG_NAINVALID, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.5.3.1.5.1.%i", /* EMERSON_AVOCENT_OID_MODEL_NAME */ + "Avocent SNMP PDU", SU_FLAG_ABSENT | SU_FLAG_OK | SU_FLAG_NAINVALID, NULL), + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* Daisychained devices support * Notes: this definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ - { "device.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.2.1.4.1", - "1", SU_FLAG_STATIC, NULL }, + snmp_info_default("device.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.2.1.4.1", + "1", SU_FLAG_STATIC, NULL), /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, EMERSON_AVOCENT_OID_MODEL_NAME, - "Avocent SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.1.0", - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.7.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AVOCENT_OID_UNIT_MACADDR, - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, EMERSON_AVOCENT_OID_MODEL_NAME, + "Avocent SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.1.0", + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.7.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AVOCENT_OID_UNIT_MACADDR, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.3.1.8.1.%i", "0", SU_FLAG_STATIC | SU_FLAG_ZEROINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.3.1.8.1.%i", "0", SU_FLAG_STATIC | SU_FLAG_ZEROINVALID | SU_FLAG_OK, NULL), /* outlets */ /* NOTE: there is a bug in Avocent FW: * index '0' should not respond (and is not in subtree mode) but answers * to unitary get, since OIDs start at index '1'. *Use the status data below to test since '0' is not a supported value */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.10418.17.2.5.5.1.5.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_ZEROINVALID, &avocent_outlet_status_info[0] }, - { "outlet.%i.id", 0, 1, - ".1.3.6.1.4.1.10418.17.2.5.5.1.3.1.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.10418.17.2.5.5.1.4.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_NAINVALID, NULL }, + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.10418.17.2.5.5.1.5.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_ZEROINVALID, &avocent_outlet_status_info[0]), + snmp_info_default("outlet.%i.id", 0, 1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.3.1.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.10418.17.2.5.5.1.4.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_NAINVALID, NULL), /* pmPowerMgmtOutletsTableCurrentValue.1.1.1; Value (Integer): 0 */ - { "outlet.%i.current", 0, 0.1, - ".1.3.6.1.4.1.10418.17.2.5.5.1.50.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.%i.current", 0, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.50.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTableCurrentHighCritical.1.1.1; Value (Integer): 160 */ - { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.1, + snmp_info_default("outlet.%i.current.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.10418.17.2.5.5.1.100.1.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTableCurrentHighWarning.1.1.1; Value (Integer): 120 */ - { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.1, + snmp_info_default("outlet.%i.current.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.10418.17.2.5.5.1.101.1.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTableCurrentLowWarning.1.1.1; Value (Integer): 0 */ - { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.1, + snmp_info_default("outlet.%i.current.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.10418.17.2.5.5.1.102.1.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTableCurrentLowCritical.1.1.1; Value (Integer): 0 */ - { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.1, + snmp_info_default("outlet.%i.current.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.10418.17.2.5.5.1.103.1.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTablePowerValue.1.1.1; Value (Integer): 0 */ - { "outlet.%i.realpower", 0, 0.1, - ".1.3.6.1.4.1.10418.17.2.5.5.1.60.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.%i.realpower", 0, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.60.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pmPowerMgmtOutletsTableVoltageValue.1.1.1; Value (Integer): 238 */ - { "outlet.%i.voltage", 0, 1, - ".1.3.6.1.4.1.10418.17.2.5.5.1.70.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.%i.voltage", 0, 1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.70.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* TODO: handle statistics * pmPowerMgmtOutletsTableEnergyValue.1.1.1; Value (Integer): 0 (Wh) @@ -212,8 +166,8 @@ static snmp_info_t emerson_avocent_pdu_mib[] = { /* Outlet groups collection */ /* pmPowerMgmtNumberOfOutletGroup.0; Value (Integer): 0 */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.6.%i", - "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.group.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.6.%i", + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL), /* TODO: support for "Banks" (not sure to understand what is this?!) * pmPowerMgmtTotalNumberOfBanks.0; Value (Integer): 6 @@ -228,12 +182,12 @@ static snmp_info_t emerson_avocent_pdu_mib[] = { * powerLock(5), * powerUnlock(6) */ - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "4", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "3", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "2", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "4", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "3", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "2", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t emerson_avocent_pdu = { "emerson_avocent_pdu", EMERSON_AVOCENT_MIB_VERSION, NULL, EMERSON_AVOCENT_OID_MODEL_NAME, emerson_avocent_pdu_mib, EMERSON_AVOCENT_SYSOID, NULL }; diff --git a/drivers/etapro.c b/drivers/etapro.c index 85b88d5964..2e5fa812ef 100644 --- a/drivers/etapro.c +++ b/drivers/etapro.c @@ -55,7 +55,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "ETA PRO driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/ever-hid.c b/drivers/ever-hid.c index 866fff96c3..ed4c439164 100644 --- a/drivers/ever-hid.c +++ b/drivers/ever-hid.c @@ -33,7 +33,7 @@ #include "main.h" /* for getval() */ #include "usb-common.h" -#define EVER_HID_VERSION "Ever HID 0.1" +#define EVER_HID_VERSION "Ever HID 0.10" /* FIXME: experimental flag to be put in upsdrv_info */ /* Ever */ @@ -41,12 +41,17 @@ /* ST Microelectronics */ #define STMICRO_VENDORID 0x0483 +/* Please note that USB vendor ID 0x0483 is from ST Microelectronics - + * with actual product IDs delegated to different OEMs. + * Devices handled in this driver are marketed under Ever brand. + */ /* USB IDs device table */ static usb_device_id_t ever_usb_device_table[] = { { USB_DEVICE(STMICRO_VENDORID, 0xa113), NULL }, { USB_DEVICE(EVER_VENDORID, 0xffff), NULL}, + { USB_DEVICE(EVER_VENDORID, 0x0000), NULL}, /* Terminating entry */ { 0, 0, NULL } @@ -58,7 +63,7 @@ static usb_device_id_t ever_usb_device_table[] = { static const char *ever_format_hardware_fun(double value) { - /*TODO - add exception handling for v1.0b0B */ + /* TODO - add exception handling for v1.0b0B */ const char* hard_rev[27] = {"0", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; static char model[10]; snprintf(model, sizeof(model), "rev.%sv%02u", @@ -69,8 +74,9 @@ static const char *ever_format_hardware_fun(double value) static const char *ever_format_version_fun(double value) { - /*upsdebugx(1, "UPS ups_firmware_conversion_fun VALUE: %d", (long)value ); */ static char model[10]; + + /*upsdebugx(1, "UPS ups_firmware_conversion_fun VALUE: %d", (long)value);*/ snprintf(model, sizeof(model), "v%X.%Xb%02d", ((unsigned int)value & 0xF000)>>12, ((unsigned int)value & 0xF00)>>8, @@ -80,18 +86,17 @@ static const char *ever_format_version_fun(double value) static const char *ever_mac_address_fun(double value) { + int mac_adress_report_id = 210; + int len = reportbuf->len[mac_adress_report_id]; + int n = 0; /* number of characters currently in line */ + int i; /* number of bytes output from buffer */ + const void *buf = reportbuf->data[mac_adress_report_id]; + static char line[100]; NUT_UNUSED_VARIABLE(value); - int mac_adress_report_id = 210; - int len = reportbuf->len[mac_adress_report_id]; - const void *buf = reportbuf->data[mac_adress_report_id]; - - static char line[100]; line[0] = '\0'; - int n = 0; /* number of characters currently in line */ - int i; /* number of bytes output from buffer */ - /* skip first elemnt which is a report id */ + /* skip first element which is a report id */ for (i = 1; i < len; i++) { n = snprintfcat(line, sizeof(line), n ? ":%02x" : "%02x", ((unsigned char *)buf)[i]); @@ -102,11 +107,14 @@ static const char *ever_mac_address_fun(double value) static const char *ever_ip_address_fun(double value) { + static int report_counter = 1; + int report_id = 211, len; + int n = 0; /* number of characters currently in line */ + int i; /* number of bytes output from buffer */ + const void *buf; + static char line[100]; NUT_UNUSED_VARIABLE(value); - static int report_counter = 1; - int report_id = 211; - if(report_counter == 1) report_id = 211; /* notification dest ip */ else if(report_counter == 2) @@ -118,15 +126,12 @@ static const char *ever_ip_address_fun(double value) report_counter== 4 ? report_counter=1 : report_counter++; - int len = reportbuf->len[report_id]; - const void *buf = reportbuf->data[report_id]; + len = reportbuf->len[report_id]; + buf = reportbuf->data[report_id]; - static char line[100]; line[0] = '\0'; - int n = 0; /* number of characters currently in line */ - int i; /* number of bytes output from buffer */ - /*skip first element which is a report id */ + /* skip first element which is a report id */ for (i = 1; i < len; i++) { n = snprintfcat(line, sizeof(line), n ? ".%d" : "%d", @@ -138,11 +143,12 @@ static const char *ever_ip_address_fun(double value) static const char *ever_packets_fun(double value) { + static int report_counter = 1; + int report_id = 215, len, res; + const unsigned char *buf; + static char line[200]; NUT_UNUSED_VARIABLE(value); - static int report_counter = 1; - int report_id = 215; - if(report_counter == 1 ) report_id = 215; else if(report_counter == 2 ) @@ -154,18 +160,17 @@ static const char *ever_packets_fun(double value) report_counter== 4 ? report_counter=1 : report_counter++; - int len = reportbuf->len[report_id]; - const unsigned char *buf = reportbuf->data[report_id]; + len = reportbuf->len[report_id]; + buf = reportbuf->data[report_id]; - static char line[100]; line[0] = '\0'; - /*skip first elemnt which is a report id */ + /* skip first element which is a report id */ if(len < 5) return ""; - int res = (int)buf[1]; + res = (int)buf[1]; res |= (int)buf[2] << 8; res |= (int)buf[3] << 16; res |= (int)buf[4] << 24; @@ -177,16 +182,15 @@ static const char *ever_packets_fun(double value) static const char* ever_workmode_fun(double value) { + int workmode_report_id = 74; + int workmode = -1; + const unsigned char *buf = reportbuf->data[workmode_report_id]; + static char line[200]; NUT_UNUSED_VARIABLE(value); - int workmode_report_id = 74; - int workmode = -1; - const unsigned char *buf = reportbuf->data[workmode_report_id]; - - static char line[100]; line[0] = '\0'; - /*skip first element which is a report id */ + /* skip first element which is a report id */ snprintfcat(line, sizeof(line), "%d", buf[1]); workmode = atoi(line); @@ -222,20 +226,18 @@ static const char* ever_workmode_fun(double value) static const char* ever_messages_fun(double value) { + int messages_report_id = 75, messages; + int n = 0; /* number of characters currently in line */ + const unsigned char *buf = reportbuf->data[messages_report_id]; + static char line[200]; NUT_UNUSED_VARIABLE(value); - int messages_report_id = 75; - const unsigned char *buf = reportbuf->data[messages_report_id]; - - static char line[200]; line[0] = '\0'; - /*skip first element which is a report id */ - int messages = (int)buf[1]; + /* skip first element which is a report id */ + messages = (int)buf[1]; messages |= (int)buf[2] << 8; - int n = 0; /* number of characters currently in line */ - /* duplicate of ups.status: OB LB */ /* if(messages & 0x01) @@ -270,21 +272,18 @@ static const char* ever_messages_fun(double value) static const char* ever_alarms_fun(double value) { + int alarms_report_id = 76, alarms; + int n = 0; /* number of characters currently in line */ + const unsigned char *buf = reportbuf->data[alarms_report_id]; + static char line[200]; NUT_UNUSED_VARIABLE(value); - int alarms_report_id = 76; - - const unsigned char *buf = reportbuf->data[alarms_report_id]; - - static char line[200]; line[0] = '\0'; - /*skip first element which is a report id */ - int alarms = (int)buf[1]; + /* skip first element which is a report id */ + alarms = (int)buf[1]; alarms |= (int)buf[2] << 8; - int n = 0; /* number of characters currently in line */ - if(alarms & 0x01) n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "OVERLOAD"); if(alarms & 0x02) @@ -307,16 +306,15 @@ static const char* ever_alarms_fun(double value) static const char* ever_on_off_fun(double value) { + int workmode_report_id = 74; + int workmode = -1; + const unsigned char *buf = reportbuf->data[workmode_report_id]; + static char line[200]; NUT_UNUSED_VARIABLE(value); - int workmode_report_id = 74; - int workmode = -1; - const unsigned char *buf = reportbuf->data[workmode_report_id]; - - static char line[100]; line[0] = '\0'; - /*skip first element which is a report id */ + /* skip first element which is a report id */ snprintfcat(line, sizeof(line), "%d", buf[1]); workmode = atoi(line); @@ -532,7 +530,7 @@ static hid_info_t ever_hid2nut[] = { /* WAS: "experimental.inverter_info.battery_temperature" */ { "battery.temperature", 0, 0, "UPS.EVER1.EVER43", NULL, "%s", 0, kelvin_celsius_conversion }, /* WAS: "experimental.ups_info.output_powerfactor" */ - { "powerfactor", 0, 0, "UPS.EVER1.EVER44", NULL, "%.0f", 0, NULL }, + { "output.powerfactor", 0, 0, "UPS.EVER1.EVER44", NULL, "%.0f", 0, NULL }, /* experimental: Should these be HU_TYPE_CMD entries? * Or are they really settings? */ @@ -686,8 +684,10 @@ static hid_info_t ever_hid2nut[] = { /* WAS: experimental.powersummary.delay_before_shutdown */ { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, +#if WITH_UNMAPPED_DATA_POINTS /* not implemented*/ - /* { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, */ + { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.AwaitingPower", NULL, NULL, HU_FLAG_QUICK_POLL, awaitingpower_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, diff --git a/drivers/everups.c b/drivers/everups.c index 63ccea879d..dc032b0fec 100644 --- a/drivers/everups.c +++ b/drivers/everups.c @@ -21,7 +21,7 @@ #include "serial.h" #define DRIVER_NAME "Ever UPS driver (serial)" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index b0be56db45..bf0c6ad333 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -33,7 +33,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Gamatronic UPS driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -101,7 +101,7 @@ static ssize_t sec_cmd(const char mode, const char *command, char *msgbuf, ssize upsdebugx(1, "PC-->UPS: \"%s\"",msg); ret = ser_send(upsfd, "%s", msg); - upsdebugx(1, " send returned: %zd",ret); + upsdebugx(1, " send returned: %" PRIiSIZE, ret); if (ret == -1) return -1; @@ -368,7 +368,7 @@ static void setup_serial(const char *port) exit (1); } else - printf("Connected to UPS on %s baudrate: %zu\n", + printf("Connected to UPS on %s baudrate: %" PRIuSIZE "\n", port, baud_rates[i].name); } diff --git a/drivers/generic_gpio_common.c b/drivers/generic_gpio_common.c new file mode 100644 index 0000000000..6457f8c7d8 --- /dev/null +++ b/drivers/generic_gpio_common.c @@ -0,0 +1,507 @@ +/* generic_gpio_common.c - common NUT driver code for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "main.h" +#include "attribute.h" +#include "generic_gpio_common.h" + +struct gpioups_t *gpioupsfd = (struct gpioups_t *)NULL; + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +struct gpioups_t *generic_gpio_open(const char *chipName); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void generic_gpio_close(struct gpioups_t *gpioupsfd); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void get_ups_rules(struct gpioups_t *upsfd, unsigned char *rulesString); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void add_rule_item(struct gpioups_t *upsfd, int newValue); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +int get_rule_lex(unsigned char *rulesBuff, int *startPos, int *endPos); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +int calc_rule_states(int upsLinesStates[], int cRules[], int subCount, int sIndex); + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void update_ups_states(struct gpioups_t *gpioupsfd); + +/* + * allocate common data structures and process/check rules + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +struct gpioups_t *generic_gpio_open(const char *chipName) { + struct gpioups_t *upsfdlocal=xcalloc(sizeof(*upsfdlocal),1); + upsfdlocal->runOptions=0; /* don't use ROPT_REQRES and ROPT_EVMODE yet */ + upsfdlocal->chipName=chipName; + + if(!testvar("rules")) /* rules is required configuration parameter */ + fatalx(EXIT_FAILURE, "UPS status calculation rules not specified"); + + get_ups_rules(upsfdlocal, (unsigned char *)getval("rules")); + upsfdlocal->upsLinesStates = xcalloc(sizeof(int), upsfdlocal->upsLinesCount); + + return upsfdlocal; +} + +/* + * release common data structures + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void generic_gpio_close(struct gpioups_t *gpioupsfdlocal) { + if (gpioupsfdlocal) { + if (gpioupsfdlocal->upsLines) { + free(gpioupsfdlocal->upsLines); + } + if (gpioupsfdlocal->upsLinesStates) { + free(gpioupsfdlocal->upsLinesStates); + } + if (gpioupsfdlocal->rules) { + int i; + for (i=0; i < gpioupsfdlocal->rulesCount; i++) { + free(gpioupsfdlocal->rules[i]); + } + } + free(gpioupsfdlocal); + gpioupsfdlocal = NULL; + } +} + +/* + * add compiled subrules item to the array + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void add_rule_item(struct gpioups_t *upsfdlocal, int newValue) { + int subCount = (upsfdlocal->rules[upsfdlocal->rulesCount-1]) ? upsfdlocal->rules[upsfdlocal->rulesCount-1]->subCount+1 : 1; + int itemSize = subCount*sizeof(upsfdlocal->rules[0]->cRules[0])+sizeof(rulesint); + + upsfdlocal->rules[upsfdlocal->rulesCount-1] = xrealloc(upsfdlocal->rules[upsfdlocal->rulesCount-1], itemSize); + upsfdlocal->rules[upsfdlocal->rulesCount-1]->subCount = subCount; + upsfdlocal->rules[upsfdlocal->rulesCount-1]->cRules[subCount-1] = newValue; +} + +/* + * get next lexem out of rules configuration string recognizing separators = and ; , + * logical commands ^ , & , | , state names - several ascii characters matching NUT states, + * and several numbers to denote GPIO chip lines to read statuses + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +int get_rule_lex(unsigned char *rulesBuff, int *startPos, int *endPos) { + static unsigned char lexType[256]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 0x00 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 0x10 */ + 0, 0, 0, 0, 0, 0,'&', 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 0x20 */ + '0','0','0','0','0','0','0','0','0','0', 0,';', 0,'=', 0, 0, /* 48 0x30 */ + 0,'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', /* 64 0x40 */ + 'a','a','a','a','a','a','a','a','a','a','a', 0, 0, 0,'^', 0, /* 80 0x50 */ + 0,'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', /* 96 0x60 */ + 'a','a','a','a','a','a','a','a','a','a','a', 0,'|', 0, 0, 0, /* 112 0x70 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128 0x80 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144 0x90 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160 0xa0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176 0xb0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192 0xc0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208 0xd0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224 0xe0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 240 0xf0 */ + }; + unsigned char lexTypeChr = lexType[rulesBuff[*startPos]]; + + *endPos = (*startPos)+1; + if(lexTypeChr == 'a' || lexTypeChr == '0') { + for(; lexType[rulesBuff[*endPos]] == lexTypeChr; (*endPos)++); + } + return (int)lexTypeChr; +} + +/* + * split subrules and translate them to array of commands/line numbers + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void get_ups_rules(struct gpioups_t *upsfdlocal, unsigned char *rulesString) { + /* statename=[^]line[&||[line]] */ + char lexBuff[33]; + int startPos = 0, endPos; + int lexType; + int lexStatus = 0; + int i, j, k; + int tranformationDelta; + + upsdebugx(4, "rules = [%s]", rulesString); + /* state machine to process rules definition */ + while((lexType=get_rule_lex(rulesString, &startPos, &endPos)) > 0 && lexStatus >= 0) { + memset(lexBuff, 0, sizeof(lexBuff)); + strncpy(lexBuff, (char *)(rulesString+startPos), endPos-startPos); + upsdebugx(4, + "rules start %d, end %d, lexType %d, lex [%s]", + startPos, + endPos, + lexType, + lexBuff + ); + switch(lexStatus) { + case 0: + if(lexType != 'a') { + lexStatus=-1; + } else { + lexStatus = 1; + upsfdlocal->rulesCount++; + upsfdlocal->rules = xrealloc(upsfdlocal->rules, (size_t)(sizeof(upsfdlocal->rules[0])*upsfdlocal->rulesCount)); + upsfdlocal->rules[upsfdlocal->rulesCount-1] = xcalloc(sizeof(rulesint), 1); + strncpy(upsfdlocal->rules[upsfdlocal->rulesCount-1]->stateName, (char *)(rulesString+startPos), endPos-startPos); + upsfdlocal->rules[upsfdlocal->rulesCount-1]->stateName[endPos-startPos] = 0; + } + break; + + case 1: + if(lexType != '=') { + lexStatus = -1; + } else { + lexStatus = 2; + } + break; + + case 2: + if(lexType == '^') { + lexStatus = 3; + add_rule_item(upsfdlocal, RULES_CMD_NOT); + } else if(lexType == '0') { + lexStatus = 4; + add_rule_item(upsfdlocal, atoi((char *)(rulesString+startPos))); + } else { + lexStatus = -1; + } + break; + + case 3: + if(lexType != '0') { + lexStatus = -1; + } else { + lexStatus = 4; + add_rule_item(upsfdlocal, atoi((char *)(rulesString+startPos))); + } + break; + + case 4: + if(lexType == '&') { + lexStatus = 2; + add_rule_item(upsfdlocal, RULES_CMD_AND); + } else if(lexType == '|') { + lexStatus = 2; + add_rule_item(upsfdlocal, RULES_CMD_OR); + } + else if(lexType == ';') { + lexStatus = 0; + } else { + lexStatus = -1; + } + break; + + default: + lexStatus = -1; + break; + } + if(lexStatus == -1) + fatalx(LOG_ERR, "Line processing rule error at position %d", startPos); + startPos = endPos; + } + if(lexType == 0 && lexStatus != 0) + fatalx(LOG_ERR, "Line processing rule error at position %d", startPos); + + /* debug printout for extracted rules */ + upsdebugx(4, "rules count [%d]", upsfdlocal->rulesCount); + for(i = 0; i < upsfdlocal->rulesCount; i++) { + upsdebugx(4, + "rule state name [%s], subcount %d", + upsfdlocal->rules[i]->stateName, + upsfdlocal->rules[i]->subCount + ); + for(j = 0; jrules[i]->subCount; j++) { + upsdebugx(4, + "[%s] substate %d [%d]", + upsfdlocal->rules[i]->stateName, + j, + upsfdlocal->rules[i]->cRules[j] + ); + } + } + + /* get gpio lines used in rules, find max line number used to check with chip lines count*/ + upsfdlocal->upsLinesCount = 0; + upsfdlocal->upsMaxLine = 0; + for(i = 0; i < upsfdlocal->rulesCount; i++) { + for(j = 0; j < upsfdlocal->rules[i]->subCount; j++) { + int pinOnList = 0; + for(k = 0; k < upsfdlocal->upsLinesCount && !pinOnList; k++) { + if(upsfdlocal->upsLines[k] == upsfdlocal->rules[i]->cRules[j]) { + pinOnList = 1; + } + } + if(!pinOnList) { + if(upsfdlocal->rules[i]->cRules[j] >= 0) { + upsfdlocal->upsLinesCount++; + upsfdlocal->upsLines = xrealloc(upsfdlocal->upsLines, sizeof(upsfdlocal->upsLines[0])*upsfdlocal->upsLinesCount); + upsfdlocal->upsLines[upsfdlocal->upsLinesCount-1] = upsfdlocal->rules[i]->cRules[j]; + if(upsfdlocal->upsLines[upsfdlocal->upsLinesCount-1] > upsfdlocal->upsMaxLine) { + upsfdlocal->upsMaxLine = upsfdlocal->upsLines[upsfdlocal->upsLinesCount-1]; + } + } + } + } + } + + upsdebugx(4, "UPS line count = %d", upsfdlocal->upsLinesCount); + for(i = 0; i < upsfdlocal->upsLinesCount; i++) { + upsdebugx(4, "UPS line%d number %d", i, upsfdlocal->upsLines[i]); + } + + /* transform lines to indexes for easier state calculation */ + tranformationDelta = upsfdlocal->upsMaxLine - RULES_CMD_LAST + 1; + for(i = 0; i < upsfdlocal->rulesCount; i++) { + for(j = 0; j < upsfdlocal->rules[i]->subCount; j++) { + if(upsfdlocal->rules[i]->cRules[j] >= 0) { + upsfdlocal->rules[i]->cRules[j] -= tranformationDelta; + } + } + } + for(k = 0; k < upsfdlocal->upsLinesCount; k++) { + for(i = 0; i < upsfdlocal->rulesCount; i++) { + for(j = 0; j < upsfdlocal->rules[i]->subCount; j++) { + if((upsfdlocal->rules[i]->cRules[j] + tranformationDelta) == upsfdlocal->upsLines[k]) { + upsfdlocal->rules[i]->cRules[j] = k; + } + } + } + } + + /* debug printout of transformed lines numbers */ + upsdebugx(4, "rules count [%d] translated", upsfdlocal->rulesCount); + for(i = 0; i < upsfdlocal->rulesCount; i++) { + upsdebugx(4, + "rule state name [%s], subcount %d translated", + upsfdlocal->rules[i]->stateName, + upsfdlocal->rules[i]->subCount + ); + for(j = 0; j < upsfdlocal->rules[i]->subCount; j++) { + upsdebugx(4, + "[%s] substate %d [%d]", + upsfdlocal->rules[i]->stateName, j, + upsfdlocal->rules[i]->cRules[j] + ); + } + } +} + +/* + * calculate state rule value based on GPIO line values + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +int calc_rule_states(int upsLinesStates[], int cRules[], int subCount, int sIndex) { + int ruleVal = 0; + int iopStart = sIndex; + int rs; + if(iopStart < subCount) { /* calculate left side */ + if(cRules[iopStart] >= 0) { + ruleVal = upsLinesStates[cRules[iopStart]]; + } else { + iopStart++; + ruleVal = !upsLinesStates[cRules[iopStart]]; + } + iopStart++; + } + for(; iopStart < subCount; iopStart++) { /* right side calculation */ + if(cRules[iopStart] == RULES_CMD_OR) { + ruleVal = ruleVal || calc_rule_states(upsLinesStates, cRules, subCount, iopStart+1); + break; + } else { + iopStart++; + if(cRules[iopStart] == RULES_CMD_NOT) { + iopStart++; + rs = !upsLinesStates[cRules[iopStart]]; + } else { + rs = upsLinesStates[cRules[iopStart]]; + } + ruleVal = ruleVal && rs; + } + } + + return ruleVal; +} + +/* + * set ups state according to rules, do adjustments for CHRG/DISCHRG + * and battery charge states + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void update_ups_states(struct gpioups_t *gpioupsfdlocal) { + int batLow = 0; + int bypass = 0; + int chargerStatusSet = 0; + int ruleNo; + + status_init(); + + for(ruleNo = 0; ruleNo < gpioupsfdlocal->rulesCount; ruleNo++) { + gpioupsfdlocal->rules[ruleNo]->currVal = + calc_rule_states( + gpioupsfdlocal->upsLinesStates, + gpioupsfdlocal->rules[ruleNo]->cRules, + gpioupsfdlocal->rules[ruleNo]->subCount, 0 + ); + if(gpioupsfdlocal->rules[ruleNo]->currVal) { + status_set(gpioupsfdlocal->rules[ruleNo]->stateName); + + if(!strcmp(gpioupsfdlocal->rules[ruleNo]->stateName, "CHRG")) { + dstate_setinfo("battery.charger.status", "%s", "charging"); + chargerStatusSet++; + } + if(!strcmp(gpioupsfdlocal->rules[ruleNo]->stateName, "DISCHRG")) { + dstate_setinfo("battery.charger.status", "%s", "discharging"); + chargerStatusSet++; + } + if(!strcmp(gpioupsfdlocal->rules[ruleNo]->stateName, "LB")) { + batLow = 1; + } + if(!strcmp(gpioupsfdlocal->rules[ruleNo]->stateName, "BYPASS")) { + bypass = 1; + } + } + if(gpioupsfdlocal->aInfoAvailable && + gpioupsfdlocal->rules[ruleNo]->archVal != gpioupsfdlocal->rules[ruleNo]->currVal) { + upslogx(LOG_WARNING, "UPS state [%s] changed to %d", + gpioupsfdlocal->rules[ruleNo]->stateName, + gpioupsfdlocal->rules[ruleNo]->currVal + ); + } + gpioupsfdlocal->rules[ruleNo]->archVal = gpioupsfdlocal->rules[ruleNo]->currVal; + } + + if(chargerStatusSet <= 0) { + dstate_delinfo("battery.charger.status"); + } + + if(dstate_getinfo("battery.charge.low")!=NULL) { + if(batLow) { + dstate_setinfo("battery.charge", "%s", dstate_getinfo("battery.charge.low")); + } else { + dstate_setinfo("battery.charge", "%s", "100"); + } + } + + if(bypass) { + dstate_delinfo("battery.charge"); + } + + gpioupsfdlocal->aInfoAvailable = 1; + + status_commit(); +} + +void upsdrv_initinfo(void) +{ + if(testvar("mfr")) { + dstate_setinfo("device.mfr", "%s", getval("mfr")); + } + if(testvar("model")) { + dstate_setinfo("device.model", "%s", getval("model")); + } +} + +void upsdrv_updateinfo(void) +{ + /* read GPIO lines states */ + gpio_get_lines_states(gpioupsfd); + + /* calculate/set UPS states based on line values */ + update_ups_states(gpioupsfd); + + /* no protocol failures possible - mark data as OK */ + dstate_dataok(); +} + +void upsdrv_shutdown(void) +{ + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); +} + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "mfr", "Override UPS manufacturer name"); + addvar(VAR_VALUE, "model", "Override UPS model name"); + addvar(VAR_VALUE, "rules", "Line rules to produce status strings"); +} + +void upsdrv_initups(void) +{ + /* prepare rules and allocate related structures */ + gpioupsfd=generic_gpio_open(device_path); + /* open GPIO chip and check pin consistence */ + if(gpioupsfd) { + gpio_open(gpioupsfd); + } +} + +void upsdrv_cleanup(void) +{ + /* release gpio library resources */ + gpio_close(gpioupsfd); + /* release related generic resources */ + generic_gpio_close(gpioupsfd); +} diff --git a/drivers/generic_gpio_common.h b/drivers/generic_gpio_common.h new file mode 100644 index 0000000000..3b17a86c9a --- /dev/null +++ b/drivers/generic_gpio_common.h @@ -0,0 +1,86 @@ +/* generic_gpio_common.h - common NUT driver definitions for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef GENERIC_GPIO_COMMON_H_SEEN +#define GENERIC_GPIO_COMMON_H_SEEN + +#include +#include +#include + +/* rules commands definition */ +#define RULES_CMD_NOT -2 +#define RULES_CMD_AND -3 +#define RULES_CMD_OR -4 +#define RULES_CMD_OBR -5 +#define RULES_CMD_CBR -6 +#define RULES_CMD_LAST RULES_CMD_CBR + +/* run option definitions */ +#define ROPT_REQRES 0x00000001 /* reserve GPIO lines only during request processing */ +#define ROPT_EVMODE 0x00000002 /* event driven run */ + +/* buffer size for chipName arrays */ +#define NUT_GPIO_CHIPNAMEBUF 32 +#define NUT_GPIO_SUBTYPEBUF 16 + +typedef struct rulesint_t { /* structure to store processed rules configuration per each state */ + char stateName[12]; /* NUT state name for rules in cRules */ + int archVal; /* previous state value */ + int currVal; /* current state value */ + int subCount; /* element count in translated rules subitem */ + int cRules[]; /* translated rules subitem - rules commands followed by line number(s) */ +} rulesint; + +typedef struct gpioups_t { + void *lib_data; /* pointer to driver's gpio support library data structure */ + const char *chipName; /* port or file name to reference GPIO chip */ + int initial; /* initialization flag - 0 on 1st entry */ + int runOptions; /* run options, not yet used */ + int aInfoAvailable; /* non-zero if previous state information is available */ + int chipLinesCount; /* gpio chip lines count, set after sucessful open */ + int upsLinesCount; /* no of lines used in rules */ + int *upsLines; /* lines numbers */ + int *upsLinesStates; /* lines states */ + int upsMaxLine; /* maximum line number referenced in rules */ + int rulesCount; /* rules subitem count: no of NUT states defined in rules*/ + struct rulesint_t **rules; +} gpioups; + +extern struct gpioups_t *gpioupsfd; + +void gpio_open(struct gpioups_t *gpioupsfd); +void gpio_get_lines_states(struct gpioups_t *gpioupsfd); +void gpio_close(struct gpioups_t *gpioupsfd); + +# ifdef DRIVERS_MAIN_WITHOUT_MAIN +/* Methods externalized for unit-tests, otherwise private to this module */ +struct gpioups_t *generic_gpio_open(const char *chipName); +void generic_gpio_close(struct gpioups_t *gpioupsfd); +void get_ups_rules(struct gpioups_t *upsfd, unsigned char *rulesString); +void add_rule_item(struct gpioups_t *upsfd, int newValue); +int get_rule_lex(unsigned char *rulesBuff, int *startPos, int *endPos); +int calc_rule_states(int upsLinesStates[], int cRules[], int subCount, int sIndex); +void update_ups_states(struct gpioups_t *gpioupsfd); +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ + +#endif /* GENERIC_GPIO_COMMON_H_SEEN */ diff --git a/drivers/generic_gpio_libgpiod.c b/drivers/generic_gpio_libgpiod.c new file mode 100644 index 0000000000..dded983b85 --- /dev/null +++ b/drivers/generic_gpio_libgpiod.c @@ -0,0 +1,255 @@ +/* generic_gpio_libgpiod.c - gpiod based NUT driver code for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "main.h" +#include "attribute.h" +#include "generic_gpio_common.h" +#include "generic_gpio_libgpiod.h" + +#define DRIVER_NAME "GPIO UPS driver" +#define DRIVER_VERSION "1.01" + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "ModrisB ", + DRV_STABLE, + { NULL } +}; + +static void reserve_lines_libgpiod(struct gpioups_t *gpioupsfd, int inner); + +/* CyberPower 12V open collector state definitions + 0 ON BATTERY Low when operating from utility line + Open when operating from battery + 1 REPLACE BATTERY Low when battery is charged + Open when battery fails the Self Test + 6 BATTERY MISSING Low when battery is present + Open when battery is missing + 3 LOW BATTERY Low when battery is near full charge capacity + Open when operating from a battery with < 20% capacity + NUT supported states + OL On line (mains is present) + OB On battery (mains is not present) + LB Low battery + HB High battery + RB The battery needs to be replaced + CHRG The battery is charging + DISCHRG The battery is discharging (inverter is providing load power) + BYPASS UPS bypass circuit is active -- no battery protection is available + CAL UPS is currently performing runtime calibration (on battery) + OFF UPS is offline and is not supplying power to the load + OVER UPS is overloaded + TRIM UPS is trimming incoming voltage (called "buck" in some hardware) + BOOST UPS is boosting incoming voltage + FSD Forced Shutdown (restricted use, see the note below) + CyberPower rules setting + OL=^0;OB=0;LB=3;HB=^3;RB=1;DISCHRG=0&^3;BYPASS=6; +*/ + +/* + * reserve GPIO lines as per run options and inner parameter: do reservation once + * or per each status read + */ +static void reserve_lines_libgpiod(struct gpioups_t *gpioupsfdlocal, int inner) { + struct libgpiod_data_t *libgpiod_data = (struct libgpiod_data_t *)(gpioupsfdlocal->lib_data); + upsdebugx(5, "reserve_lines_libgpiod runOptions 0x%x, inner %d", gpioupsfdlocal->runOptions, inner); + + if(((gpioupsfdlocal->runOptions&ROPT_REQRES) != 0) == inner) { + struct gpiod_line_request_config config; + int gpioRc; + config.consumer=upsdrv_info.name; + if(gpioupsfdlocal->runOptions&ROPT_EVMODE) { + config.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + upsdebugx(5, "reserve_lines_libgpiod GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES"); + } else { + config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; + upsdebugx(5, "reserve_lines_libgpiod GPIOD_LINE_REQUEST_DIRECTION_INPUT"); + } + config.flags = 0; /* GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; */ + gpioRc = gpiod_line_request_bulk(&libgpiod_data->gpioLines, &config, NULL); + if(gpioRc) + fatal_with_errno( + LOG_ERR, + "GPIO gpiod_line_request_bulk call failed, check for other applications that may have reserved GPIO lines" + ); + upsdebugx(5, + "GPIO gpiod_line_request_bulk with type %d return code %d", + config.request_type, + gpioRc + ); + } +} + +/* + * allocate memeory for libary, open gpiochip + * and check lines numbers validity - consistency with h/w chip + */ +void gpio_open(struct gpioups_t *gpioupsfdlocal) { + struct libgpiod_data_t *libgpiod_data = xcalloc(sizeof(struct libgpiod_data_t),1); + gpioupsfdlocal->lib_data = libgpiod_data; + + libgpiod_data->gpioChipHandle = gpiod_chip_open_by_name(gpioupsfdlocal->chipName); + if(!libgpiod_data->gpioChipHandle) + fatal_with_errno( + LOG_ERR, + "Could not open GPIO chip [%s], check chips presence and/or access rights", + gpioupsfdlocal->chipName + ); + else { + int gpioRc; + upslogx(LOG_NOTICE, "GPIO chip [%s] opened", gpioupsfdlocal->chipName); + gpioupsfdlocal->chipLinesCount = gpiod_chip_num_lines(libgpiod_data->gpioChipHandle); + upslogx(LOG_NOTICE, "Find %d lines on GPIO chip [%s]", gpioupsfdlocal->chipLinesCount, gpioupsfdlocal->chipName); + if(gpioupsfdlocal->chipLinesCountupsMaxLine) { + gpiod_chip_close(libgpiod_data->gpioChipHandle); + fatalx( + LOG_ERR, + "GPIO chip lines count %d smaller than UPS line number used (%d)", + gpioupsfdlocal->chipLinesCount, + gpioupsfdlocal->upsMaxLine + ); + } + gpiod_line_bulk_init(&libgpiod_data->gpioLines); + gpiod_line_bulk_init(&libgpiod_data->gpioEventLines); + gpioRc = gpiod_chip_get_lines( + libgpiod_data->gpioChipHandle, + (unsigned int *)gpioupsfdlocal->upsLines, + gpioupsfdlocal->upsLinesCount, + &libgpiod_data->gpioLines + ); + if(gpioRc) + fatal_with_errno( + LOG_ERR, + "GPIO line reservation (gpiod_chip_get_lines call) failed with code %d, check for possible issue in rules parameter", + gpioRc + ); + upsdebugx(5, "GPIO gpiod_chip_get_lines return code %d", gpioRc); + reserve_lines_libgpiod(gpioupsfdlocal, 0); + } +} + +/* + * close gpiochip and release allocated memory + */ +void gpio_close(struct gpioups_t *gpioupsfdlocal) { + if(gpioupsfdlocal) { + struct libgpiod_data_t *libgpiod_data = (struct libgpiod_data_t *)(gpioupsfdlocal->lib_data); + if(libgpiod_data) { + if(libgpiod_data->gpioChipHandle) { + gpiod_chip_close(libgpiod_data->gpioChipHandle); + } + free(gpioupsfdlocal->lib_data); + gpioupsfdlocal->lib_data = NULL; + } + } +} + +/* + * get GPIO line states for all needed lines + */ +void gpio_get_lines_states(struct gpioups_t *gpioupsfdlocal) { + int i; + int gpioRc; + struct libgpiod_data_t *libgpiod_data = (struct libgpiod_data_t *)(gpioupsfdlocal->lib_data); + + reserve_lines_libgpiod(gpioupsfdlocal, 1); + if(gpioupsfdlocal->runOptions&ROPT_EVMODE) { + struct timespec timeoutLong = {1,0}; + struct gpiod_line_event event; + int monRes; + upsdebugx(5, + "gpio_get_lines_states_libgpiod initial %d, timeout %ld", + gpioupsfdlocal->initial, + timeoutLong.tv_sec + ); + if(gpioupsfdlocal->initial) { + timeoutLong.tv_sec = 35; + } else { + gpioupsfdlocal->initial = 1; + } + upsdebugx(5, + "gpio_get_lines_states_libgpiod initial %d, timeout %ld", + gpioupsfdlocal->initial, + timeoutLong.tv_sec + ); + gpiod_line_bulk_init(&libgpiod_data->gpioEventLines); + monRes=gpiod_line_event_wait_bulk( + &libgpiod_data->gpioLines, + &timeoutLong, + &libgpiod_data->gpioEventLines + ); + upsdebugx(5, + "gpiod_line_event_wait_bulk completed with %d return code and timeout %ld s", + monRes, + timeoutLong.tv_sec + ); + if(monRes==1) { + int num_lines_local = (int)gpiod_line_bulk_num_lines(&libgpiod_data->gpioEventLines); + int j; + for(j=0; jgpioEventLines, + j + ); + int eventRc=gpiod_line_event_read(eLine, &event); + unsigned int lineOffset = gpiod_line_offset(eLine); + event.event_type=0; + upsdebugx(5, + "Event read return code %d and event type %d for line %d", + eventRc, + event.event_type, + lineOffset + ); + } + } + } + for(i=0; i < gpioupsfdlocal->upsLinesCount; i++) { + gpioupsfdlocal->upsLinesStates[i] = -1; + } + gpioRc=gpiod_line_get_value_bulk( + &libgpiod_data->gpioLines, + gpioupsfdlocal->upsLinesStates + ); + + if (gpioRc < 0) + fatal_with_errno(LOG_ERR, "GPIO line status read call failed"); + + upsdebugx(5, + "GPIO gpiod_line_get_value_bulk completed with %d return code, status values:", + gpioRc + ); + + for(i=0; i < gpioupsfdlocal->upsLinesCount; i++) { + upsdebugx(5, + "Line%d state = %d", + i, + gpioupsfdlocal->upsLinesStates[i] + ); + } + + if(gpioupsfdlocal->runOptions&ROPT_REQRES) { + gpiod_line_release_bulk(&libgpiod_data->gpioLines); + } +} diff --git a/drivers/generic_gpio_libgpiod.h b/drivers/generic_gpio_libgpiod.h new file mode 100644 index 0000000000..66f13b4511 --- /dev/null +++ b/drivers/generic_gpio_libgpiod.h @@ -0,0 +1,34 @@ +/* generic_gpio_libgpiod.h - gpiod based NUT driver definitions for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef GENERIC_GPIO_LIBGPIOD_H_SEEN +#define GENERIC_GPIO_LIBGPIOD_H_SEEN + +#include + +typedef struct libgpiod_data_t { + struct gpiod_chip *gpioChipHandle; /* libgpiod chip handle when opened */ + struct gpiod_line_bulk gpioLines; /* libgpiod lines to monitor */ + struct gpiod_line_bulk gpioEventLines; /* libgpiod lines for event monitoring */ +} libgpiod_data; + +#endif /* GENERIC_GPIO_LIBGPIOD_H_SEEN */ diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index 478e838b04..b242c5a6a5 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -23,10 +23,11 @@ #include "main.h" #include "generic_modbus.h" #include -#include +#include "timehead.h" +#include "nut_stdint.h" #define DRIVER_NAME "NUT Generic Modbus driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ @@ -335,9 +336,13 @@ void upsdrv_shutdown(void) switch (rval) { case STAT_INSTCMD_FAILED: case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); + upslogx(LOG_ERR, "shutdown failed"); + set_exit_flag(-1); + return; case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); + return; default: break; } @@ -409,25 +414,25 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) int rval = -1; /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; + uint16_t mask8 = 0x000F; + uint16_t mask16 = 0x00FF; switch (type) { case COIL: rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; + *(uint16_t *)data = *(uint16_t *)data & mask8; break; case INPUT_B: rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; + *(uint16_t *)data = *(uint16_t *)data & mask8; break; case INPUT_R: rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; + *(uint16_t *)data = *(uint16_t *)data & mask16; break; case HOLDING: rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; + *(uint16_t *)data = *(uint16_t *)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) ) @@ -475,7 +480,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) modbus_reconnect(); } } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + upsdebugx(3, "register addr: 0x%x, register type: %d read: %u",addr, type, *(unsigned int *)data); return rval; } @@ -485,16 +490,16 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) int rval = -1; /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; + uint16_t mask8 = 0x000F; + uint16_t mask16 = 0x00FF; switch (type) { case COIL: - *(uint *)data = *(uint *)data & mask8; + *(uint16_t *)data = *(uint16_t *)data & mask8; rval = modbus_write_bit(mb, addr, *(uint8_t *)data); break; case HOLDING: - *(uint *)data = *(uint *)data & mask16; + *(uint16_t *)data = *(uint16_t *)data & mask16; rval = modbus_write_register(mb, addr, *(uint16_t *)data); break; @@ -531,7 +536,7 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) modbus_reconnect(); } } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + upsdebugx(3, "register addr: 0x%x, register type: %d read: %u",addr, type, *(unsigned int *)data); return rval; } @@ -702,7 +707,7 @@ int get_signal_state(devstate_t state) } /* get driver configuration parameters */ -void get_config_vars() +void get_config_vars(void) { int i; /* local index */ @@ -1037,6 +1042,7 @@ void modbus_reconnect(void) int rval; upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + dstate_setinfo("driver.state", "reconnect.trying"); /* clear current modbus context */ modbus_close(mbctx); @@ -1096,4 +1102,6 @@ void modbus_reconnect(void) } /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + dstate_setinfo("driver.state", "quiet"); } diff --git a/drivers/genericups.c b/drivers/genericups.c index d6568ddad2..acd2e3f6d6 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -19,7 +19,11 @@ #include "config.h" /* must be first */ +#ifndef WIN32 #include +#else +#include "wincompat.h" +#endif #include "main.h" #include "serial.h" @@ -27,7 +31,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Generic contact-closure UPS driver" -#define DRIVER_VERSION "1.38" +#define DRIVER_VERSION "1.39" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -218,7 +222,11 @@ void upsdrv_updateinfo(void) { int flags, ol, bl, rb, bypass, ret; +#ifndef WIN32 ret = ioctl(upsfd, TIOCMGET, &flags); +#else + ret = w32_getcomm( upsfd, &flags ); +#endif if (ret != 0) { upslog_with_errno(LOG_INFO, "ioctl failed"); @@ -305,34 +313,49 @@ void upsdrv_shutdown(void) int flags, ret; if (upstype == -1) { - fatalx(EXIT_FAILURE, "No upstype set - see help text / man page!"); + upslogx(LOG_ERR, "No upstype set - see help text / man page!"); + set_exit_flag(-1); + return; } flags = upstab[upstype].line_sd; if (flags == -1) { - fatalx(EXIT_FAILURE, "No shutdown command defined for this model!"); + upslogx(LOG_ERR, "No shutdown command defined for this model!"); + set_exit_flag(-1); + return; } if (flags == TIOCM_ST) { +#ifndef WIN32 #ifndef HAVE_TCSENDBREAK - fatalx(EXIT_FAILURE, "Need to send a BREAK, but don't have tcsendbreak!"); + upslogx(LOG_ERR, "Need to send a BREAK, but don't have tcsendbreak!"); + set_exit_flag(-1); + return; +#endif #endif ret = tcsendbreak(upsfd, 4901); if (ret != 0) { - fatal_with_errno(EXIT_FAILURE, "tcsendbreak"); + upslog_with_errno(LOG_ERR, "tcsendbreak"); + set_exit_flag(-1); } return; } +#ifndef WIN32 ret = ioctl(upsfd, TIOCMSET, &flags); +#else + ret = w32_setcomm(upsfd,&flags); +#endif if (ret != 0) { - fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); + upslog_with_errno(LOG_ERR, "ioctl TIOCMSET"); + set_exit_flag(-1); + return; } if (getval("sdtime")) { @@ -426,7 +449,11 @@ void upsdrv_initups(void) upsdebugx(2, "parse_output_signals: SD overridden with %s\n", v); } +#ifndef WIN32 if (ioctl(upsfd, TIOCMSET, &upstab[upstype].line_norm)) { +#else + if (w32_setcomm(upsfd,&upstab[upstype].line_norm)) { +#endif fatal_with_errno(EXIT_FAILURE, "ioctl TIOCMSET"); } } @@ -435,4 +462,3 @@ void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } - diff --git a/drivers/hidparser.c b/drivers/hidparser.c index 48fb1b0bf3..fd52fb3208 100644 --- a/drivers/hidparser.c +++ b/drivers/hidparser.c @@ -202,7 +202,7 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) /* Remove Usage */ pParser->UsageSize--; - } + } /* Get Index if any */ if (pParser->Value >= 0x80) { @@ -361,7 +361,7 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) upslogx(LOG_ERR, "%s: HID Usage too high", __func__); /* FIXME: comparison is always false due to limited range of data type [-Werror=type-limits] - * with ReportID beint uint8_t and MAX_REPORT being 500 currently */ + * with ReportID being uint8_t and MAX_REPORT being 500 currently */ /* if(pParser->Data.ReportID >= MAX_REPORT) upslogx(LOG_ERR, "%s: Too many HID reports", __func__); @@ -442,6 +442,35 @@ 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]; + HIDPath_t *pPath; + uint8_t size; + + if (pData->ReportID != ReportID) { + continue; + } + + pPath = &pData->Path; + 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..9f604565e5 100644 --- a/drivers/hidparser.h +++ b/drivers/hidparser.h @@ -32,17 +32,24 @@ extern "C" { /* *INDENT-ON* */ #endif /* __cplusplus */ -#include "config.h" +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + #include "hidtypes.h" /* Include "usb-common.h" or "libshut.h" as appropriate, to define the * usb_ctrl_* types used below according to the backend USB API version */ -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE # include "libshut.h" -#else +#else /* !SHUT_MODE => USB */ # include "usb-common.h" -#endif +#endif /* SHUT_MODE / USB */ /* * Parse_ReportDesc @@ -63,6 +70,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..ad2960f735 100644 --- a/drivers/hpe-pdu-mib.c +++ b/drivers/hpe-pdu-mib.c @@ -24,1254 +24,884 @@ #include "hpe-pdu-mib.h" #include "dstate.h" -#define HPE_EPDU_MIB_VERSION "0.32" +#define HPE_EPDU_MIB_VERSION "0.34" #define HPE_EPDU_MIB_SYSOID ".1.3.6.1.4.1.232.165.7" #define HPE_EPDU_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.7.1.2.1.3.0" static info_lkp_t hpe_pdu_outlet_status_info[] = { - { 1, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "pendingOff" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 4, "pendingOn" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "off"), + info_lkp_default(2, "on"), + info_lkp_default(3, "pendingOff"), /* transitional status */ + info_lkp_default(4, "pendingOn"), /* transitional status */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_outletgroups_status_info[] = { - { 1, "N/A" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* notApplicable, if group.type == outlet-section */ - { 2, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* breakerOn */ - { 3, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* breakerOff */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "N/A"), /* notApplicable, if group.type == outlet-section */ + info_lkp_default(2, "on"), /* breakerOn */ + info_lkp_default(3, "off"), /* breakerOff */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_outlet_switchability_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel }; /* The physical type of outlet */ static info_lkp_t hpe_pdu_outlet_type_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "iecC13" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "iecC19" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "uk" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 11, "french" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 12, "schuko" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 20, "nema515" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 21, "nema51520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 22, "nema520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 23, "nemaL520" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 24, "nemaL530" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 25, "nema615" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 26, "nema620" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 27, "nemaL620" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 28, "nemaL630" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 29, "nemaL715" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 30, "rf203p277" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(1, "iecC13"), + info_lkp_default(2, "iecC19"), + info_lkp_default(10, "uk"), + info_lkp_default(11, "french"), + info_lkp_default(12, "schuko"), + info_lkp_default(20, "nema515"), + info_lkp_default(21, "nema51520"), + info_lkp_default(22, "nema520"), + info_lkp_default(23, "nemaL520"), + info_lkp_default(24, "nemaL530"), + info_lkp_default(25, "nema615"), + info_lkp_default(26, "nema620"), + info_lkp_default(27, "nemaL620"), + info_lkp_default(28, "nemaL630"), + info_lkp_default(29, "nemaL715"), + info_lkp_default(30, "rf203p277"), + info_lkp_sentinel }; static info_lkp_t hpe_pdu_ambient_presence_info[] = { - { -1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* disconnected */ - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* connected */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "no"), /* disconnected */ + info_lkp_default(2, "yes"), /* connected */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "warning-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 3, "critical-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 4, "warning-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 5, "critical-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "warning-low"), /* Warning low threshold triggered */ + info_lkp_default(3, "critical-low"), /* Critical low threshold triggered */ + info_lkp_default(4, "warning-high"), /* Warning high threshold triggered */ + info_lkp_default(5, "critical-high"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_frequency_status_info[] = { - { 1, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "out-of-range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "good"), /* No threshold triggered */ + info_lkp_default(2, "out-of-range"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_ambient_drycontacts_info[] = { - { -1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "open" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "bad" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* FIXME: what to do with that? */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(0, "unknown"), + info_lkp_default(1, "opened"), + info_lkp_default(2, "closed"), + info_lkp_default(3, "bad"), + /* FIXME: what to do with that? */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_voltage_alarms_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "low voltage warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 3, "low voltage critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 4, "high voltage warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 5, "high voltage critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No threshold triggered */ + info_lkp_default(2, "low voltage warning!"), /* Warning low threshold triggered */ + info_lkp_default(3, "low voltage critical!"), /* Critical low threshold triggered */ + info_lkp_default(4, "high voltage warning!"), /* Warning high threshold triggered */ + info_lkp_default(5, "high voltage critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_current_alarms_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "low current warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 3, "low current critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 4, "high current warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 5, "high current critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No threshold triggered */ + info_lkp_default(2, "low current warning!"), /* Warning low threshold triggered */ + info_lkp_default(3, "low current critical!"), /* Critical low threshold triggered */ + info_lkp_default(4, "high current warning!"), /* Warning high threshold triggered */ + info_lkp_default(5, "high current critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_frequency_alarm_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "frequency out of range!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Frequency out of range triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No threshold triggered */ + info_lkp_default(2, "frequency out of range!"), /* Frequency out of range triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_temperature_alarms_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "low temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 3, "low temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 4, "high temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 5, "high temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No threshold triggered */ + info_lkp_default(2, "low temperature warning!"), /* Warning low threshold triggered */ + info_lkp_default(3, "low temperature critical!"), /* Critical low threshold triggered */ + info_lkp_default(4, "high temperature warning!"), /* Warning high threshold triggered */ + info_lkp_default(5, "high temperature critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_threshold_humidity_alarms_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 2, "low humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 3, "low humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 4, "high humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 5, "high humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* No threshold triggered */ + info_lkp_default(2, "low humidity warning!"), /* Warning low threshold triggered */ + info_lkp_default(3, "low humidity critical!"), /* Critical low threshold triggered */ + info_lkp_default(4, "high humidity warning!"), /* Warning high threshold triggered */ + info_lkp_default(5, "high humidity critical!"), /* Critical high threshold triggered */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_outlet_group_type_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "breaker1pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "breaker2pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "breaker3pole" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "outlet-section" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "unknown"), + info_lkp_default(1, "unknown"), + info_lkp_default(2, "breaker1pole"), + info_lkp_default(3, "breaker2pole"), + info_lkp_default(4, "breaker3pole"), + info_lkp_default(5, "outlet-section"), + info_lkp_sentinel }; static info_lkp_t hpe_pdu_input_type_info[] = { - { 1, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* singlePhase */ - { 2, "2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* splitPhase */ - { 3, "3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* threePhaseDelta */ - { 4, "3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* threePhaseWye */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "1"), /* singlePhase */ + info_lkp_default(2, "2"), /* splitPhase */ + info_lkp_default(3, "3"), /* threePhaseDelta */ + info_lkp_default(4, "3"), /* threePhaseWye */ + info_lkp_sentinel }; static info_lkp_t hpe_pdu_outlet_group_phase_info[] = { - { 1, "L1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* singlePhase */ - { 2, "L1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase1toN */ - { 3, "L2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase2toN */ - { 4, "L3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase3toN */ - { 5, "L1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase1to2 */ - { 6, "L2" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase2to3 */ - { 7, "L3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* phase3to1 */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "L1"), /* singlePhase */ + info_lkp_default(2, "L1"), /* phase1toN */ + info_lkp_default(3, "L2"), /* phase2toN */ + info_lkp_default(4, "L3"), /* phase3toN */ + info_lkp_default(5, "L1"), /* phase1to2 */ + info_lkp_default(6, "L2"), /* phase2to3 */ + info_lkp_default(7, "L3"), /* phase3to1 */ + info_lkp_sentinel }; /* Snmp2NUT lookup table for HPE PDU MIB */ static snmp_info_t hpe_pdu_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* pdu2Model.0 = STRING: "HP 8.6kVA 208V 30A 3Ph NA/JP maPDU" */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.3.%i", - "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* pdu2SerialNumber.0 = STRING: "CN94230105" */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.7.%i", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* pdu2PartNumber.0 = STRING: "H8B52A" */ - { "device.part", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.6.%i", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* For daisychain, there is only 1 physical interface! */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* Daisychained devices support * Notes: this definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ /* pdu2NumberPDU.0 = INTEGER: 1 */ - { "device.count", 0, 1, + snmp_info_default("device.count", 0, 1, ".1.3.6.1.4.1.232.165.7.1.1.0", - "1", SU_FLAG_STATIC, NULL }, + "1", SU_FLAG_STATIC, NULL), /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.3.%i", - "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: use unitName.0 (ePDU)? - * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, + * snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL), */ + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.7.%i", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: this entry should be SU_FLAG_SEMI_STATIC */ /* pdu2FirmwareVersion.0 = STRING: "02.00.0043" */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.1.2.1.5.%i", - "", SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + "", SU_FLAG_OK, NULL), + snmp_info_default("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 + /* FIXME: needs a date reformatting callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 - * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, + * snmp_info_default("ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + * snmp_info_default("ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), */ /* Input collection */ /* Note: for daisychain mode, we must handle phase(s) per device, not as a whole */ /* pdu2InputType.0 = INTEGER: threePhaseWye(4) */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.1.%i", - NULL, SU_FLAG_STATIC, &hpe_pdu_input_type_info[0] }, + snmp_info_default("input.phases", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.1.%i", + NULL, SU_FLAG_STATIC, &hpe_pdu_input_type_info[0]), /* Frequency is measured globally */ /* pdu2InputFrequency.0 = INTEGER: 500 */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.232.165.7.2.1.1.2.%i", - NULL, 0, NULL }, + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.232.165.7.2.1.1.2.%i", + NULL, 0, NULL), /* pdu2InputFrequencyStatus.0 = INTEGER: good(1) */ - { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.1.1.3.%i", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.1.1.3.%i", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_alarm_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_alarm_info[0]), /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ - { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhaseCurrentPercentLoad.0.1 = INTEGER: 0 */ - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhaseCurrentPercentLoad.0.2 = INTEGER: 0 */ - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhaseCurrentPercentLoad.0.3 = INTEGER: 0 */ - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + snmp_info_default("input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* FIXME: * - Voltage is only measured per phase, as mV! * so input.voltage == input.L1.voltage for both single and 3phase - * - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) + * - As per NUT namespace (https://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) * => RFC input.Lx.voltage.context */ /* pdu2InputPhaseVoltage.0.1 = INTEGER: 216790 */ - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", - NULL, 0, NULL }, + snmp_info_default("input.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", + NULL, 0, NULL), /* pdu2InputPhaseVoltageThStatus.0.1 = INTEGER: good(1) */ - { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0]), /* pdu2InputPhaseVoltageThLowerWarning.0.1 = INTEGER: 190000 */ - { "input.voltage.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThLowerCritical.0.1 = INTEGER: 180000 */ - { "input.voltage.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperWarning.0.1 = INTEGER: 255000 */ - { "input.voltage.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperCritical.0.1 = INTEGER: 265000 */ - { "input.voltage.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltage.0.1 = INTEGER: 216790 */ - { "input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", - NULL, 0, NULL }, + snmp_info_default("input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", + NULL, 0, NULL), /* pdu2InputPhaseVoltageThStatus.0.1 = INTEGER: good(1) */ - { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0]), /* pdu2InputPhaseVoltageThLowerWarning.0.1 = INTEGER: 190000 */ - { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThLowerCritical.0.1 = INTEGER: 180000 */ - { "input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperWarning.0.1 = INTEGER: 255000 */ - { "input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperCritical.0.1 = INTEGER: 265000 */ - { "input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltage.0.2 = INTEGER: 216790 */ - { "input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.2", - NULL, 0, NULL }, + snmp_info_default("input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.2", + NULL, 0, NULL), /* pdu2InputPhaseVoltageThStatus.0.2 = INTEGER: good(1) */ - { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0]), /* pdu2InputPhaseVoltageThLowerWarning.0.2 = INTEGER: 190000 */ - { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThLowerCritical.0.2 = INTEGER: 180000 */ - { "input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperWarning.0.2 = INTEGER: 255000 */ - { "input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperCritical.0.2 = INTEGER: 265000 */ - { "input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltage.0.3 = INTEGER: 216790 */ - { "input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.3", - NULL, 0, NULL }, + snmp_info_default("input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.3", + NULL, 0, NULL), /* pdu2InputPhaseVoltageThStatus.0.3 = INTEGER: good(1) */ - { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.3", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.3", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0]), /* pdu2InputPhaseVoltageThLowerWarning.0.3 = INTEGER: 190000 */ - { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThLowerCritical.0.3 = INTEGER: 180000 */ - { "input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperWarning.0.3 = INTEGER: 255000 */ - { "input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseVoltageThUpperCritical.0.3 = INTEGER: 265000 */ - { "input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* FIXME: * - input.current is mapped on input.L1.current for both single and 3phase !!! */ /* pdu2InputPhaseCurrent.0.1 = INTEGER: 185 */ - { "input.current", 0, 0.001, + snmp_info_default("input.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.1", - NULL, 0, NULL }, + NULL, 0, NULL), /* pdu2InputPhaseCurrentRating.0.1 = INTEGER: 24000 */ - { "input.current.nominal", 0, 0.001, + snmp_info_default("input.current.nominal", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThStatus.0.1 = INTEGER: good(1) */ - { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2InputPhaseCurrentThLowerWarning.0.1 = INTEGER: 0 */ - { "input.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThLowerCritical.0.1 = INTEGER: -1 */ - { "input.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperWarning.0.1 = INTEGER: 19200 */ - { "input.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperCritical.0.1 = INTEGER: 24000 */ - { "input.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrent.0.1 = INTEGER: 185 */ - { "input.L1.current", 0, 0.001, + snmp_info_default("input.L1.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.1", - NULL, 0, NULL }, + NULL, 0, NULL), /* pdu2InputPhaseCurrentRating.0.1 = INTEGER: 24000 */ - { "input.L1.current.nominal", 0, 0.001, + snmp_info_default("input.L1.current.nominal", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThStatus.0.1 = INTEGER: good(1) */ - { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2InputPhaseCurrentThLowerWarning.0.1 = INTEGER: 0 */ - { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThLowerCritical.0.1 = INTEGER: -1 */ - { "input.L1.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperWarning.0.1 = INTEGER: 19200 */ - { "input.L1.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperCritical.0.1 = INTEGER: 24000 */ - { "input.L1.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L1.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.1", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrent.0.2 = INTEGER: 185 */ - { "input.L2.current", 0, 0.001, + snmp_info_default("input.L2.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.2", - NULL, 0, NULL }, + NULL, 0, NULL), /* pdu2InputPhaseCurrentRating.0.2 = INTEGER: 24000 */ - { "input.L2.current.nominal", 0, 0.001, + snmp_info_default("input.L2.current.nominal", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThStatus.0.2 = INTEGER: good(1) */ - { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2InputPhaseCurrentThLowerWarning.0.2 = INTEGER: 0 */ - { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThLowerCritical.0.2 = INTEGER: -1 */ - { "input.L2.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperWarning.0.2 = INTEGER: 19200 */ - { "input.L2.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperCritical.0.2 = INTEGER: 24000 */ - { "input.L2.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L2.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.2", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrent.0.3 = INTEGER: 185 */ - { "input.L3.current", 0, 0.001, + snmp_info_default("input.L3.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.3", - NULL, 0, NULL }, + NULL, 0, NULL), /* pdu2InputPhaseCurrentRating.0.3 = INTEGER: 24000 */ - { "input.L3.current.nominal", 0, 0.001, + snmp_info_default("input.L3.current.nominal", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThStatus.0.3 = INTEGER: good(1) */ - { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.3", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2InputPhaseCurrentThLowerWarning.0.3 = INTEGER: 0 */ - { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThLowerCritical.0.3 = INTEGER: -1 */ - { "input.L3.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperWarning.0.3 = INTEGER: 19200 */ - { "input.L3.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPhaseCurrentThUpperCritical.0.3 = INTEGER: 24000 */ - { "input.L3.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("input.L3.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.3", - NULL, SU_FLAG_NEGINVALID, NULL }, + NULL, SU_FLAG_NEGINVALID, NULL), /* pdu2InputPowerWatts.0 = INTEGER: 19 */ - { "input.realpower", 0, 1.0, + snmp_info_default("input.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.1.1.5.%i", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerWatts.0.1 = INTEGER: 19 */ - { "input.L1.realpower", 0, 1.0, + snmp_info_default("input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerWatts.0.2 = INTEGER: 0 */ - { "input.L2.realpower", 0, 1.0, + snmp_info_default("input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerWatts.0.3 = INTEGER: 0 */ - { "input.L3.realpower", 0, 1.0, + snmp_info_default("input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ /* pdu2InputPowerVA.0 = INTEGER: 39 */ - { "input.power", 0, 1.0, + snmp_info_default("input.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.1.1.4.%i", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerVA.0.1 = INTEGER: 40 */ - { "input.L1.power", 0, 1.0, + snmp_info_default("input.L1.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerVA.0.2 = INTEGER: 0 */ - { "input.L2.power", 0, 1.0, + snmp_info_default("input.L2.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.2", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2InputPhasePowerVA.0.3 = INTEGER: 0 */ - { "input.L3.power", 0, 1.0, + snmp_info_default("input.L3.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.3", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* TODO: handle statistics */ - /* pdu2InputPowerWattHour.0 = INTEGER: 91819 - { "unmapped.pdu2InputPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, */ - /* pdu2InputPowerWattHourTimer.0 = STRING: "16/10/2017,17:58:53" - { "unmapped.pdu2InputPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.1.1.7.0", NULL, SU_FLAG_OK, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + /* pdu2InputPowerWattHour.0 = INTEGER: 91819 */ + snmp_info_default("unmapped.pdu2InputPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* pdu2InputPowerWattHourTimer.0 = STRING: "16/10/2017,17:58:53" */ + snmp_info_default("unmapped.pdu2InputPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.1.1.7.0", NULL, SU_FLAG_OK, NULL), +#endif /* #if WIH_UNMAPPED_DATA_POINTS */ + /* pdu2InputPowerFactor.0 = INTEGER: 483 */ - { "input.powerfactor", 0, 0.001, + snmp_info_default("input.powerfactor", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.1.1.8.%i", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* Ambient collection */ /* pdu2TemperatureProbeStatus.0.1 = INTEGER: disconnected(1) */ - { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.2.1.3.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_ambient_presence_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_ambient_presence_info[0]), /* pdu2TemperatureThStatus.0.1 = INTEGER: good(1) */ - { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.2.1.5.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.2.1.5.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_temperature_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_temperature_alarms_info[0]), /* pdu2TemperatureValue.0.1 = INTEGER: 0 */ - { "ambient.temperature", 0, 0.1, + snmp_info_default("ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.4.%i.1", - NULL, SU_FLAG_OK, NULL }, + NULL, SU_FLAG_OK, NULL), /* Low and high threshold use the respective critical levels */ /* pdu2TemperatureThLowerCritical.0.1 = INTEGER: 50 */ - { "ambient.temperature.low", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.temperature.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.low.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2TemperatureThLowerWarning.0.1 = INTEGER: 100 */ - { "ambient.temperature.low.warning", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2TemperatureThUpperCritical.0.1 = INTEGER: 650 */ - { "ambient.temperature.high", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.temperature.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.temperature.high.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2TemperatureThUpperWarning.0.1 = INTEGER: 200 */ - { "ambient.temperature.high.warning", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.2.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2HumidityThStatus.0.1 = INTEGER: good(1) */ - { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.3.1.5.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.3.1.5.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_threshold_humidity_alarms_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_threshold_humidity_alarms_info[0]), /* pdu2HumidityValue.0.1 = INTEGER: 0 */ - { "ambient.humidity", 0, 0.1, + snmp_info_default("ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.4.%i.1", - NULL, SU_FLAG_OK, NULL }, + NULL, SU_FLAG_OK, NULL), /* Low and high threshold use the respective critical levels */ /* pdu2HumidityThLowerCritical.0.1 = INTEGER: 100 */ - { "ambient.humidity.low", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.humidity.low", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.low.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.7.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2HumidityThLowerWarning.0.1 = INTEGER: 200 */ - { "ambient.humidity.low.warning", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.6.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2HumidityThUpperWarning.0.1 = INTEGER: 250 */ - { "ambient.humidity.high.warning", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.8.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* pdu2HumidityThUpperCritical.0.1 = INTEGER: 900 */ - { "ambient.humidity.high", ST_FLAG_RW, 0.1, + snmp_info_default("ambient.humidity.high", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "ambient.humidity.high.critical", ST_FLAG_RW, 0.1, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.232.165.7.4.3.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL), /* Dry contacts on TH module */ /* pdu2ContactState.0.1 = INTEGER: contactBad(3) */ - { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.4.1.4.%i.1", - NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0]), /* pdu2ContactState.0.2 = INTEGER: contactBad(3) */ - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.4.4.1.4.%i.2", - NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0]), /* Outlet collection */ - { "outlet.id", 0, 1, NULL, - "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, + "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* pdu2OutletCount.0 = INTEGER: 24 */ - { "outlet.count", 0, 1, + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.232.165.7.1.2.1.12.%i", - "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ /* pdu2OutletName.0.%i = STRING: "Outlet L1-%i" */ - { "outlet.%i.desc", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.desc", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlStatus.0.%i = INTEGER: on(2) */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.2.1.1.%i.%i", - NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_outlet_status_info[0] }, + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_outlet_status_info[0]), /* Numeric identifier of the outlet, tied to the whole unit */ - { "outlet.%i.id", 0, 1, NULL, "%i", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + snmp_info_default("outlet.%i.id", 0, 1, NULL, "%i", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL), #if 0 /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.1", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.2", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.3", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.4", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.5", - NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), + snmp_info_default("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 }, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL), #endif /* pdu2OutletCurrent.0.%i = INTEGER: 0 */ - { "outlet.%i.current", 0, 0.001, + snmp_info_default("outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.5.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletCurrentThStatus.0.%i = INTEGER: good(1) */ - { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.6.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_status_info[0] }, - { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_status_info[0]), + snmp_info_default("outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.6.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_current_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2OutletCurrentThLowerWarning.0.%i = INTEGER: 0 */ - { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletCurrentThLowerCritical.0.%i = INTEGER: -1 */ - { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.8.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletCurrentThUpperWarning.0.1 = INTEGER: 8000 */ - { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.9.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletCurrentThUpperCritical.0.1 = INTEGER: 10000 */ - { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.10.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletWatts.0.1 = INTEGER: 0 */ - { "outlet.%i.realpower", 0, 1.0, + snmp_info_default("outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.5.1.1.14.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletVA.0.%i = INTEGER: 0 */ - { "outlet.%i.power", 0, 1.0, + snmp_info_default("outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.5.1.1.13.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlSwitchable.0.%i = INTEGER: switchable(1) */ - { "outlet.%i.switchable", ST_FLAG_RW, SU_INFOSIZE, + snmp_info_default("outlet.%i.switchable", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.2.1.8.%i.%i", "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, - &hpe_pdu_outlet_switchability_info[0] }, + &hpe_pdu_outlet_switchability_info[0]), /* pdu2OutletType.0.%i = INTEGER: iecC13(1) */ - { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.3.%i.%i", "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, - &hpe_pdu_outlet_type_info[0] }, + &hpe_pdu_outlet_type_info[0]), /* pdu2OutletPowerFactor.0.%i = INTEGER: 1000 */ - { "outlet.%i.powerfactor", 0, 0.001, + snmp_info_default("outlet.%i.powerfactor", 0, 0.001, ".1.3.6.1.4.1.232.165.7.5.1.1.17.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), + /* TODO: handle statistics */ +#if WITH_UNMAPPED_DATA_POINTS /* pdu2OutletWh.0.1 = INTEGER: 1167 - * Note: setting this to zero resets the counter and timestamp => instcmd ???counter???.reset - { "unmapped.pdu2OutletWh", 0, 1, ".1.3.6.1.4.1.232.165.7.5.1.1.15.%i.%i", NULL, SU_FLAG_OK, NULL }, */ - /* pdu2OutletWhTimer.0.1 = STRING: "25/03/2016,09:03:26" - { "unmapped.pdu2OutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.16.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + * Note: setting this to zero resets the counter and timestamp => instcmd ???counter???.reset */ + snmp_info_default("unmapped.pdu2OutletWh", 0, 1, ".1.3.6.1.4.1.232.165.7.5.1.1.15.%i.%i", NULL, SU_FLAG_OK, NULL), + /* pdu2OutletWhTimer.0.1 = STRING: "25/03/2016,09:03:26" */ + snmp_info_default("unmapped.pdu2OutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.16.%i.%i", NULL, SU_FLAG_OK, NULL), +#endif /* #if WITH_UNMAPPED_DATA_POINTS */ /* Outlet groups collection */ /* pdu2GroupCount.0 = INTEGER: 3 */ - { "outlet.group.count", 0, 1, + snmp_info_default("outlet.group.count", 0, 1, ".1.3.6.1.4.1.232.165.7.1.2.1.11.%i", - "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL), /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ /* pdu2GroupIndex.0.%i = INTEGER: %i */ - { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.1.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupName.0.%i = STRING: "Section L1" */ - { "outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupType.0.%i = INTEGER: breaker2pole(3) */ - { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &hpe_pdu_outlet_group_type_info[0] }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &hpe_pdu_outlet_group_type_info[0]), /* pdu2GroupVoltageMeasType.0.1 = INTEGER: phase1to2(5) */ - { "outlet.group.%i.phase", 0, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_outlet_group_phase_info[0] }, + &hpe_pdu_outlet_group_phase_info[0]), /* pdu2groupBreakerStatus.0.%i = INTEGER: breakerOn(2) */ - { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.27.%i.%i", NULL, SU_FLAG_OK | SU_FLAG_NAINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_outletgroups_status_info[0] }, + &hpe_pdu_outletgroups_status_info[0]), /* pdu2GroupOutletCount.0.%i = INTEGER: 8 */ - { "outlet.group.%i.count", 0, 1, + snmp_info_default("outlet.group.%i.count", 0, 1, ".1.3.6.1.4.1.232.165.7.3.1.1.26.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupVoltage.0.%i = INTEGER: 216760 */ - { "outlet.group.%i.voltage", 0, 0.001, + snmp_info_default("outlet.group.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.5.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupVoltageThStatus.0.%i = INTEGER: good(1) */ - { "outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.6.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_threshold_status_info[0] }, - { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + &hpe_pdu_threshold_status_info[0]), + snmp_info_default("outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.6.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_threshold_voltage_alarms_info[0] }, + &hpe_pdu_threshold_voltage_alarms_info[0]), /* pdu2GroupVoltageThLowerWarning.0.%i = INTEGER: 190000 */ - { "outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupVoltageThLowerCritical.0.%i = INTEGER: 180000 */ - { "outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.8.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupVoltageThUpperWarning.0.%i = INTEGER: 255000 */ - { "outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.9.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupVoltageThUpperCritical.0.%i = INTEGER: 265000 */ - { "outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.10.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrent.0.%i = INTEGER: 0 */ - { "outlet.group.%i.current", 0, 0.001, + snmp_info_default("outlet.group.%i.current", 0, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.12.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2groupCurrentRating.0.%i = INTEGER: 16000 */ - { "outlet.group.%i.current.nominal", 0, 0.001, + snmp_info_default("outlet.group.%i.current.nominal", 0, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.11.%i.%i", - NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrentThStatus.0.%i = INTEGER: good(1) */ - { "outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + snmp_info_default("outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.13.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_threshold_status_info[0] }, - { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + &hpe_pdu_threshold_status_info[0]), + snmp_info_default("outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.13.%i.%i", NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &hpe_pdu_threshold_current_alarms_info[0] }, + &hpe_pdu_threshold_current_alarms_info[0]), /* pdu2GroupCurrentThLowerWarning.0.%i = INTEGER: 0 */ - { "outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.14.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrentThLowerCritical.0.%i = INTEGER: -1 */ - { "outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.15.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrentThUpperWarning.0.%i = INTEGER: 12800 */ - { "outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.16.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrentThUpperCritical.0.%i = INTEGER: 16000 */ - { "outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, + snmp_info_default("outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.17.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupCurrentPercentLoad.0.%i = INTEGER: 0 */ - { "outlet.group.%i.load", 0, 1.0, + snmp_info_default("outlet.group.%i.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.3.1.1.19.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupPowerWatts.0.%i = INTEGER: 0 */ - { "outlet.group.%i.realpower", 0, 1.0, + snmp_info_default("outlet.group.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.7.3.1.1.21.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupPowerVA.0.%i = INTEGER: 0 */ - { "outlet.group.%i.power", 0, 1.0, + snmp_info_default("outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.232.165.7.3.1.1.20.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL), /* pdu2GroupPowerFactor.0.%i = INTEGER: 1000 */ - { "outlet.group.%i.powerfactor", 0, 0.001, + snmp_info_default("outlet.group.%i.powerfactor", 0, 0.001, ".1.3.6.1.4.1.232.165.7.3.1.1.24.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* TODO: handle statistics */ +#if WITH_UNMAPPED_DATA_POINTS /* pdu2GroupPowerWattHour.0.%i = INTEGER: 1373 - * Note: setting this to zero resets the counter and timestamp => instcmd .reset - { "unmapped.pdu2GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.3.1.1.22.%i.%i", NULL, SU_FLAG_OK, NULL }, */ - /* pdu2GroupPowerWattHourTimer.0.%i = STRING: "25/03/2016,09:01:16" - { "unmapped.pdu2GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.23.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + * Note: setting this to zero resets the counter and timestamp => instcmd .reset */ + snmp_info_default("unmapped.pdu2GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.3.1.1.22.%i.%i", NULL, SU_FLAG_OK, NULL), + /* pdu2GroupPowerWattHourTimer.0.%i = STRING: "25/03/2016,09:01:16" */ + snmp_info_default("unmapped.pdu2GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.23.%i.%i", NULL, SU_FLAG_OK, NULL), +#endif /* #if WITH_UNMAPPED_DATA_POINTS */ /* instant commands. */ /* TODO: handle delays (outlet.%i.{on,off}.delay) */ /* pdu2OutletControlOffCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.off", 0, 1, + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.2.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlOnCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.on", 0, 1, + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.3.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlRebootCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.cycle", 0, 1, + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.4.%i.%i", - "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* Delayed version, parameter is mandatory (so dfl is NULL)! */ /* pdu2OutletControlOffCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.off.delay", 0, 1, + snmp_info_default("outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.2.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlOnCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.on.delay", 0, 1, + snmp_info_default("outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.3.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* pdu2OutletControlRebootCmd.0.%i = INTEGER: -1 */ - { "outlet.%i.load.cycle.delay", 0, 1, + snmp_info_default("outlet.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.232.165.7.5.2.1.4.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; diff --git a/drivers/hpe-pdu3-cis-mib.c b/drivers/hpe-pdu3-cis-mib.c new file mode 100644 index 0000000000..c429173b39 --- /dev/null +++ b/drivers/hpe-pdu3-cis-mib.c @@ -0,0 +1,1734 @@ +/* hpe-pdu3-cis-mib.c - subdriver to monitor HPE_PDU_CIS SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2022 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "hpe-pdu3-cis-mib.h" + +#define HPE_PDU3_CIS_MIB_VERSION "0.10" + +#define HPE_PDU3_CIS_SYSOID ".1.3.6.1.4.1.232.165.11" +#define HPE_PDU3_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.11.1.2.1.3.1" + +static info_lkp_t hpe_cis_unit_switchability_info[] = { + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel +}; + +static info_lkp_t hpe_cis_outlet_group_type_info[] = { + info_lkp_default(2, "breaker1pole"), + info_lkp_default(3, "breaker2pole"), + info_lkp_default(4, "breaker3pole"), + info_lkp_default(5, "outlet-section"), + info_lkp_sentinel +}; + +/* Note: same as marlin_outlet_type_info + i5-20R */ +/* and to eaton_nlogic_outlet_type_info - few entries */ +static info_lkp_t hpe_cis_outlet_type_info[] = { + info_lkp_default(1, "iecC13"), + info_lkp_default(2, "iecC19"), + info_lkp_default(10, "uk"), + info_lkp_default(11, "french"), + info_lkp_default(12, "schuko"), + info_lkp_default(20, "nema515"), + info_lkp_default(21, "nema51520"), + info_lkp_default(22, "nema520"), + info_lkp_default(23, "nemaL520"), + info_lkp_default(24, "nemaL530"), + info_lkp_default(25, "nema615"), + info_lkp_default(26, "nema620"), + info_lkp_default(27, "nemaL620"), + info_lkp_default(28, "nemaL630"), + info_lkp_default(29, "nemaL715"), + info_lkp_default(30, "rf203p277"), + info_lkp_sentinel +}; + +/* Same as eaton_nlogic_outlet_status_info */ +static info_lkp_t hpe_cis_outlet_status_info[] = { + info_lkp_default(1, "off"), + info_lkp_default(2, "on"), + info_lkp_default(3, "pendingOff"), /* transitional status */ + info_lkp_default(4, "pendingOn"), /* transitional status */ + info_lkp_sentinel +}; + +static info_lkp_t hpe_cis_outlet_switchability_info[] = { + info_lkp_default(1, "yes"), /* switchable */ + info_lkp_default(2, "no"), /* notSwitchable */ + info_lkp_sentinel +}; + +/* HPE_PDU_CIS Snmp2NUT lookup table */ +static snmp_info_t hpe_pdu3_cis_mib[] = { + +/* standard MIB items; if the vendor MIB contains better OIDs for + * this (e.g. with daisy-chain support), consider adding those here + */ + /* Device collection */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* pdu3NumberPDU.0 = INTEGER: 1 (for daisychain support) */ +/* + snmp_info_default("device.count", 0, 1, ".1.3.6.1.4.1.232.165.11.1.1.0", NULL, SU_FLAG_OK, NULL), +*/ + /* pdu3Manufacturer.1 = STRING: "HPE" */ + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.4.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + /* pdu3Model.1 = STRING: "230V, 32A, 7.4kVA, 50/60Hz" */ + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pdu3FirmwareVersion.1 = STRING: "2.0.0.J" */ + snmp_info_default("device.firmware", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.5.1", NULL, SU_FLAG_OK, NULL), + /* pdu3PartNumber.1 = STRING: "P9S18A" */ + snmp_info_default("device.part", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pdu3SerialNumber.1 = STRING: "CN09416708" */ + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.8.1", NULL, SU_FLAG_OK, NULL), + /* pdu3MACAddress.1 = Hex-STRING: EC EB B8 3D 78 6D */ + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.14.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + + /* Input collection */ + /* pdu3InputPhaseCount.1 = INTEGER: 1 */ + snmp_info_default("input.phases", 0, 1, + ".1.3.6.1.4.1.232.165.11.1.2.1.11.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPowerVA.1 = INTEGER: 922 */ + snmp_info_default("input.power", 0, 0.001, + ".1.3.6.1.4.1.232.165.11.2.1.1.4.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), + /* pdu3InputPowerWatts.1 = INTEGER: 900 */ + snmp_info_default("input.realpower", 0, 0.001, + ".1.3.6.1.4.1.232.165.11.2.1.1.5.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentRating.1.%i = INTEGER: 3200 */ + /* (can be instanciated by phase) */ + snmp_info_default("input.current.nominal", 0, 0.01, + ".1.3.6.1.4.1.232.165.11.2.2.1.10.1.1", + NULL, 0, NULL), + /* pdu3InputPhaseCurrent.1.%i = INTEGER: 398 */ + /* (can be instanciated by phase) */ + snmp_info_default("input.current", 0, 0.01, + ".1.3.6.1.4.1.232.165.11.2.2.1.11.1.1", + NULL, 0, NULL), + /* pdu3InputPhaseVoltage.1.%i = INTEGER: 2286 */ + /* (can be instanciated by phase) */ + snmp_info_default("input.voltage", 0, 0.1, + ".1.3.6.1.4.1.232.165.11.2.2.1.3.1.1", + NULL, 0, NULL), + /* pdu3InputFrequency.%i = INTEGER: 500 */ + /* (can be instanciated by phase) */ + snmp_info_default("input.frequency", 0, 0.1, + ".1.3.6.1.4.1.232.165.11.2.1.1.2.1", + NULL, 0, NULL), + + /* Outlet groups collection */ + /* pdu3GroupCount.1 = INTEGER: 2 */ + snmp_info_default("outlet.group.count", 0, 1, + ".1.3.6.1.4.1.232.165.11.1.2.1.12.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupName.1.%i = STRING: "B01" */ + snmp_info_default("outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.3.1.1.2.1.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP /*| SU_TYPE_DAISY_1*/, + NULL), + /* pdu3GroupType.1.%i = INTEGER: 2 */ + snmp_info_default("outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.3.1.1.3.1.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP /*| SU_TYPE_DAISY_1*/, + &hpe_cis_outlet_group_type_info[0]), + /* pdu3GroupCurrentPercentLoad.1.%i = INTEGER: 11 */ + snmp_info_default("outlet.group.%i.load", 0, 1.0, + ".1.3.6.1.4.1.232.165.11.3.1.1.18.1.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP /*| SU_TYPE_DAISY_1*/, NULL), + /* pdu3GroupCurrent.1.1 = INTEGER: 187 */ + snmp_info_default("outlet.group.%i.current", 0, 0.01, + ".1.3.6.1.4.1.232.165.11.3.1.1.12.1.%i", + NULL, SU_OUTLET_GROUP /*| SU_TYPE_DAISY_1*/, NULL), + /* pdu3GroupVoltage.1.%i = INTEGER: 2286 */ + snmp_info_default("outlet.group.%i.voltage", 0, 0.1, + ".1.3.6.1.4.1.232.165.11.3.1.1.5.1.%i", + NULL, SU_OUTLET_GROUP, NULL), + /* pdu3GroupOutletCount.1.1 = INTEGER: 12 */ + snmp_info_default("outlet.group.%i.count", 0, 1, + ".1.3.6.1.4.1.232.165.11.3.1.1.25.1.1", + NULL, SU_OUTLET_GROUP /* | SU_TYPE_DAISY_1 */, NULL), + + /* Outlet collection */ + /* pdu3OutletCount.1 = INTEGER: 24 */ + snmp_info_default("outlet.count", 0, 1, + ".1.3.6.1.4.1.232.165.11.1.2.1.13.1", NULL, SU_FLAG_OK, NULL), + /* pdu3Controllable.1 = INTEGER: 1 */ + snmp_info_default("outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.1.2.1.10.1", + "no", SU_FLAG_STATIC, + &hpe_cis_unit_switchability_info[0]), + + /* pdu3OutletControlStatus.1.1 = INTEGER: 2 */ + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.5.2.1.1.1.%i", + NULL, SU_OUTLET, + &hpe_cis_outlet_status_info[0]), + /* pdu3OutletName.1.%i = STRING: "CABNIET A FAN DOOR" */ + snmp_info_default("outlet.%i.name", ST_FLAG_RW |ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.5.1.1.2.1.%i", + NULL, SU_OUTLET, NULL), + /* pdu3OutletType.1.%i = INTEGER: 2 */ + snmp_info_default("outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.5.1.1.3.1.%i", + "unknown", SU_FLAG_STATIC | SU_OUTLET, + &hpe_cis_outlet_type_info[0]), + /* pdu3OutletCurrentRating.1.%i = INTEGER: 2000 */ + snmp_info_default("outlet.%i.current.nominal", 0, 0.01, + ".1.3.6.1.4.1.232.165.11.5.1.1.4.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pdu3OutletCurrent.1.%i = INTEGER: 36 */ + snmp_info_default("outlet.%i.current", 0, 0.01, + ".1.3.6.1.4.1.232.165.11.5.1.1.5.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pdu3OutletCurrentPercentLoad.1.%i = INTEGER: 18 */ + snmp_info_default("outlet.%i.load", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.1.1.11.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pdu3OutletVA.1.%i = INTEGER: 84 */ + snmp_info_default("outlet.%i.power", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.1.1.12.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pdu3OutletWatts.1.%i = INTEGER: 84 */ + snmp_info_default("outlet.%i.realpower", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.1.1.13.1.%i", + NULL, SU_OUTLET | SU_FLAG_NEGINVALID, NULL), + /* pdu3OutletControlSwitchable.1.%i = INTEGER: 1 */ + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.11.5.2.1.8.1.%i", + "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, + &hpe_cis_outlet_switchability_info[0]), + + /* instant commands. */ + /* Delays handling: + * 0-n :Time in seconds until the group command is issued + * -1:Cancel a pending group-level Off/On/Reboot command */ + /* pdu3OutletControlOffCmd.1.%i = INTEGER: -1 */ + snmp_info_default("outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.2.1.%i", + "0", SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), + /* pdu3OutletControlOnCmd.1.%i = INTEGER: -1 */ + snmp_info_default("outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.3.1.%i", + "0", SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), + /* pdu3OutletControlRebootCmd.1.%i = INTEGER: -1 */ + snmp_info_default("outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.4.1.%i", + "0", SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), + + +#if 0 + /* Per-outlet shutdown / startup delay (configuration point, not the timers) + * outletControlShutoffDelay.0.3 = INTEGER: 120 + * outletControlSequenceDelay.0.8 = INTEGER: 8 + * (by default each output socket startup is delayed by its number in seconds) + */ + snmp_info_default("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), + snmp_info_default("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), +#endif + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + snmp_info_default("outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.2.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), + snmp_info_default("outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.3.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), + snmp_info_default("outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.232.165.11.5.2.1.4.1.%i", + NULL, SU_TYPE_CMD | SU_OUTLET /*| SU_TYPE_DAISY_1*/, NULL), +#if 0 + /* Per-outlet shutdown / startup timers + * outletControlOffCmd.0.1 = INTEGER: -1 + * outletControlOnCmd.0.1 = INTEGER: -1 + */ + snmp_info_default("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), + snmp_info_default("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), +#endif + +#if WITH_UNMAPPED_DATA_POINTS + + /* pdu3IdentIndex.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3IdentIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.1.2.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3Name.1 = "" */ + snmp_info_default("unmapped.pdu3Name", 0, 1, ".1.3.6.1.4.1.232.165.11.1.2.1.2.1", NULL, SU_FLAG_OK, NULL), + + /* pdu3FirmwareVersionTimeStamp.1 = Hex-STRING: 32 30 31 36 2F 31 31 2F 30 31 20 32 30 3A 30 38 3A 33 39 00 */ + snmp_info_default("unmapped.pdu3FirmwareVersionTimeStamp", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.1.2.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pdu3Status.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3Status", 0, 1, ".1.3.6.1.4.1.232.165.11.1.2.1.9.1", NULL, SU_FLAG_OK, NULL), + + /* pdu3IPv4Address.1 = IpAddress: [PDU_IP] */ + snmp_info_default("unmapped.pdu3IPv4Address", 0, 1, ".1.3.6.1.4.1.232.165.11.1.2.1.15.1", NULL, SU_FLAG_OK, NULL), + /* pdu3IPv6Address.1 = STRING: "FE80::EEEB:B8FF:FE3D:786D" */ + snmp_info_default("unmapped.pdu3IPv6Address", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.1.2.1.16.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigSsh.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigSsh", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.2.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigFtps.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigFtps", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigHttp.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigHttp", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.4.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigHttps.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigHttps", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.5.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigIPv4IPv6Switch.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigIPv4IPv6Switch", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigRedfishAPI.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ConfigRedfishAPI", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigOledDispalyOrientation.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigOledDispalyOrientation", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.8.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigEnergyReset.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ConfigEnergyReset", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.9.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigNetworkManagementCardReset.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ConfigNetworkManagementCardReset", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.10.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ConfigDaisyChainStatus.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ConfigDaisyChainStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.1.3.1.11.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputType.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputFrequencyStatus.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputFrequencyStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.3.1", NULL, SU_FLAG_OK, NULL), + + /* pdu3InputTotalEnergy.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputTotalEnergy", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.6.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPowerWattHourTimer.1 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3InputPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.2.1.1.7.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputResettableEnergy.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputResettableEnergy", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.8.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPowerFactor.1 = INTEGER: 97 */ + snmp_info_default("unmapped.pdu3InputPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.9.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPowerVAR.1 = INTEGER: 197 */ + snmp_info_default("unmapped.pdu3InputPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.2.1.1.10.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhaseIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseIndex.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + + /* pdu3InputPhaseVoltageThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerWarning.1.1 = INTEGER: 1900 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerCritical.1.1 = INTEGER: 1800 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperWarning.1.1 = INTEGER: 2500 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperCritical.1.1 = INTEGER: 2600 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseVoltageThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.9.1.3", NULL, SU_FLAG_OK, NULL), + + /* pdu3InputPhaseCurrentThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.12.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.12.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.12.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.13.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.13.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.13.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.14.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.14.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.14.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperWarning.1.1 = INTEGER: 2200 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.15.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.15.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.15.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperCritical.1.1 = INTEGER: 2800 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.16.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.16.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.16.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentPercentLoad.1.1 = INTEGER: 124 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.17.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentPercentLoad.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.17.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhaseCurrentPercentLoad.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhaseCurrentPercentLoad", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.17.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3InputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.18.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerMeasType.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.18.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.18.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVA.1.1 = INTEGER: 921 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.19.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVA.1.2 = INTEGER: 921 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.19.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVA.1.3 = INTEGER: 921 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.19.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWatts.1.1 = INTEGER: 899 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.20.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWatts.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.20.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWatts.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.20.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHour.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.21.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHour.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.21.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHour.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.21.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHourTimer.1.1 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.2.2.1.22.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHourTimer.1.2 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.2.2.1.22.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerWattHourTimer.1.3 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3InputPhasePowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.2.2.1.22.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerFactor.1.1 = INTEGER: 97 */ + snmp_info_default("unmapped.pdu3InputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.23.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerFactor.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.23.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerFactor.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.23.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVAR.1.1 = INTEGER: 195 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.24.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVAR.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.24.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3InputPhasePowerVAR.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3InputPhasePowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.2.2.1.24.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.3 = INTEGER: 3 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.4 = INTEGER: 4 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.5 = INTEGER: 5 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.6 = INTEGER: 6 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.7 = INTEGER: 7 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.8 = INTEGER: 8 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.9 = INTEGER: 9 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.10 = INTEGER: 10 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.11 = INTEGER: 11 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupIndex.1.12 = INTEGER: 12 */ + snmp_info_default("unmapped.pdu3GroupIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.1.1.12", NULL, SU_FLAG_OK, NULL), + + + + + /* pdu3GroupVoltageMeasType.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageMeasType.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageMeasType", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.4.1.12", NULL, SU_FLAG_OK, NULL), + + /* pdu3GroupVoltageThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.6.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.7.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.8.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperWarning.1.%i = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.9.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3GroupVoltageThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupVoltageThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupVoltageThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.10.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.1 = INTEGER: 1600 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.2 = INTEGER: 1600 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3groupCurrentRating.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3groupCurrentRating", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.11.1.12", NULL, SU_FLAG_OK, NULL), + + /* pdu3GroupCurrentThStatus.1.%i = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3GroupCurrentThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.13.1.%i", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerWarning.1.%i = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.14.1.%i", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.15.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.1 = INTEGER: 1100 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.2 = INTEGER: 1100 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.16.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.1 = INTEGER: 1400 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.2 = INTEGER: 1400 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupCurrentThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupCurrentThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.17.1.12", NULL, SU_FLAG_OK, NULL), + + /* pdu3GroupPowerVA.1.1 = INTEGER: 430 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.2 = INTEGER: 530 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVA.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVA", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.19.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.1 = INTEGER: 422 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.2 = INTEGER: 512 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWatts.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWatts", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.20.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHour.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.21.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.1 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.2 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.3 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.4 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.5 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.6 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.7 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.8 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.9 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.10 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.11 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerWattHourTimer.1.12 = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.3.1.1.22.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.1 = INTEGER: 98 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.2 = INTEGER: 97 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerFactor.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.23.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.1 = INTEGER: 43 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.2 = INTEGER: 36 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupPowerVAR.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupPowerVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.24.1.12", NULL, SU_FLAG_OK, NULL), + + /* pdu3GroupBreakerStatus.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3GroupBreakerStatus.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3GroupBreakerStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.3.1.1.26.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureScale.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3TemperatureScale", 0, 1, ".1.3.6.1.4.1.232.165.11.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureCount.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3TemperatureCount", 0, 1, ".1.3.6.1.4.1.232.165.11.4.1.1.2.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityCount.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3HumidityCount", 0, 1, ".1.3.6.1.4.1.232.165.11.4.1.1.3.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactCount.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ContactCount", 0, 1, ".1.3.6.1.4.1.232.165.11.4.1.1.4.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3TemperatureIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureName.1.1 = STRING: "T" */ + snmp_info_default("unmapped.pdu3TemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pdu3TemperatureName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.2.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureProbeStatus.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3TemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3TemperatureProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureValue.1.1 = INTEGER: 27 */ + snmp_info_default("unmapped.pdu3TemperatureValue", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureValue.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureValue", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3TemperatureThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThLowerWarning.1.1 = INTEGER: 15 */ + snmp_info_default("unmapped.pdu3TemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThLowerCritical.1.1 = INTEGER: 10 */ + snmp_info_default("unmapped.pdu3TemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThUpperWarning.1.1 = INTEGER: 30 */ + snmp_info_default("unmapped.pdu3TemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThUpperCritical.1.1 = INTEGER: 35 */ + snmp_info_default("unmapped.pdu3TemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3TemperatureThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3TemperatureThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.2.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityIndex.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3HumidityIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityName.1.1 = STRING: "RH" */ + snmp_info_default("unmapped.pdu3HumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pdu3HumidityName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.2", NULL, SU_FLAG_OK, NULL), +/* FIXME: missing sub MIB for ambient sensor? */ + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.3", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.4", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.5", NULL, SU_FLAG_OK, NULL), + /* = STRING: " " */ + snmp_info_default("unmapped. = STRING: " "", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.3.1.2.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityProbeStatus.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3HumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3HumidityProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 1 */ + snmp_info_default("unmapped. = INTEGER: 1", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.3.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityValue.1.1 = INTEGER: 27 */ + snmp_info_default("unmapped.pdu3HumidityValue", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityValue.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityValue", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.4.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThStatus.1.1 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3HumidityThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThStatus.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.5.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThLowerWarning.1.1 = INTEGER: 30 */ + snmp_info_default("unmapped.pdu3HumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThLowerCritical.1.1 = INTEGER: 10 */ + snmp_info_default("unmapped.pdu3HumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThUpperWarning.1.1 = INTEGER: 60 */ + snmp_info_default("unmapped.pdu3HumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThUpperCritical.1.1 = INTEGER: 80 */ + snmp_info_default("unmapped.pdu3HumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3HumidityThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3HumidityThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* = INTEGER: 0 */ + snmp_info_default("unmapped. = INTEGER: 0", 0, 1, ".1.3.6.1.4.1.232.165.11.4.3.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactIndex.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ContactIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactIndex.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ContactIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactName.1.1 = STRING: " " */ + snmp_info_default("unmapped.pdu3ContactName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.4.1.2.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactName.1.2 = STRING: " " */ + snmp_info_default("unmapped.pdu3ContactName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.4.4.1.2.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactProbeStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ContactProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.3.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactProbeStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3ContactProbeStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.3.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactState.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ContactState", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.4.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3ContactState.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3ContactState", 0, 1, ".1.3.6.1.4.1.232.165.11.4.4.1.4.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.2 = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.3 = INTEGER: 3 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.4 = INTEGER: 4 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.5 = INTEGER: 5 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.6 = INTEGER: 6 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.7 = INTEGER: 7 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.8 = INTEGER: 8 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.9 = INTEGER: 9 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.10 = INTEGER: 10 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.11 = INTEGER: 11 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.12 = INTEGER: 12 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.13 = INTEGER: 13 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.14 = INTEGER: 14 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.15 = INTEGER: 15 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.16 = INTEGER: 16 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.17 = INTEGER: 17 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.18 = INTEGER: 18 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.19 = INTEGER: 19 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.20 = INTEGER: 20 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.21 = INTEGER: 21 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.22 = INTEGER: 22 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.23 = INTEGER: 23 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.24 = INTEGER: 24 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletIndex.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletIndex", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.1.1.48", NULL, SU_FLAG_OK, NULL), + + + /* pdu3OutletActivePowerThStatus.1.1 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.2 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.3 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.4 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.5 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.6 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.7 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.8 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.9 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.10 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.11 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.12 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.13 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.14 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.15 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.16 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.17 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.18 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.19 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.20 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.21 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.22 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.23 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.24 = INTEGER: 1 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThStatus.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThStatus", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.6.1.48", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerWarning.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.7.1.48", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThLowerCritical.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThLowerCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.8.1.48", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperWarning.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperWarning", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.9.1.48", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.1 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.1", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.2 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.2", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.3 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.3", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.4 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.4", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.5 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.5", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.6 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.6", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.7 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.7", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.8 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.8", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.9 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.9", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.10 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.10", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.11 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.11", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.12 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.12", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.13 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.13", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.14 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.14", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.15 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.15", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.16 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.16", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.17 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.17", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.18 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.18", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.19 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.19", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.20 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.20", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.21 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.21", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.22 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.22", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.23 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.23", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.24 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.24", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.25 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.25", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.26 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.26", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.27 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.27", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.28 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.28", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.29 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.29", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.30 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.30", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.31 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.31", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.32 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.32", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.33 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.33", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.34 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.34", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.35 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.35", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.36 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.36", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.37 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.37", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.38 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.38", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.39 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.39", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.40 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.40", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.41 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.41", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.42 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.42", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.43 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.43", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.44 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.44", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.45 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.45", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.46 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.46", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.47 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.47", NULL, SU_FLAG_OK, NULL), + /* pdu3OutletActivePowerThUpperCritical.1.48 = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletActivePowerThUpperCritical", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.10.1.48", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletWh.1.%i = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletWh", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.14.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletWhTimer.1.%i = Hex-STRING: 32 30 31 36 2F 31 30 2F 31 31 20 30 32 3A 34 36 3A 35 30 00 */ + snmp_info_default("unmapped.pdu3OutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.11.5.1.1.15.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletPowerFactor.1.%i = INTEGER: 88 */ + snmp_info_default("unmapped.pdu3OutletPowerFactor", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.16.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletVAR.1.%i = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3OutletVAR", 0, 1, ".1.3.6.1.4.1.232.165.11.5.1.1.17.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletControlPowerOnState.1.%i = INTEGER: 2 */ + snmp_info_default("unmapped.pdu3OutletControlPowerOnState", 0, 1, ".1.3.6.1.4.1.232.165.11.5.2.1.5.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletControlSequenceDelay.1.%i = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletControlSequenceDelay", 0, 1, ".1.3.6.1.4.1.232.165.11.5.2.1.6.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletControlRebootOffTime.1.%i = INTEGER: 5 */ + snmp_info_default("unmapped.pdu3OutletControlRebootOffTime", 0, 1, ".1.3.6.1.4.1.232.165.11.5.2.1.7.1.%i", NULL, SU_FLAG_OK, NULL), + + /* pdu3OutletControlShutoffDelay.1.%i = INTEGER: 0 */ + snmp_info_default("unmapped.pdu3OutletControlShutoffDelay", 0, 1, ".1.3.6.1.4.1.232.165.11.5.2.1.9.1.%i", NULL, SU_FLAG_OK, NULL), +#endif /* WITH_UNMAPPED_DATA_POINTS */ + + /* end of structure. */ + snmp_info_sentinel +}; + +mib2nut_info_t hpe_pdu3_cis = { "hpe_pdu3_cis", HPE_PDU3_CIS_MIB_VERSION, NULL, HPE_PDU3_OID_MODEL_NAME, hpe_pdu3_cis_mib, HPE_PDU3_CIS_SYSOID, NULL }; diff --git a/drivers/hpe-pdu3-cis-mib.h b/drivers/hpe-pdu3-cis-mib.h new file mode 100644 index 0000000000..b6a79ac9b7 --- /dev/null +++ b/drivers/hpe-pdu3-cis-mib.h @@ -0,0 +1,30 @@ +/* hpe_pdu_cis-mib.h - subdriver to monitor HPE_PDU_CIS SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2022 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef HPE_PDU3_CIS_MIB_H +#define HPE_PDU3_CIS_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t hpe_pdu3_cis; + +#endif /* HPE_PDU3_CIS_MIB_H */ diff --git a/drivers/huawei-mib.c b/drivers/huawei-mib.c index 16aa8d494f..8278a9c46e 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.40" #define HUAWEI_SYSOID ".1.3.6.1.4.1.8072.3.2.10" #define HUAWEI_UPSMIB ".1.3.6.1.4.1.2011" @@ -30,260 +30,72 @@ /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t huawei_onbatt_info[] = { - * { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ static info_lkp_t huawei_supplymethod_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* no supply */ - { 2, "OL BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* combined */ - { 6, "OL ECO" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "OB ECO" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* no supply */ + info_lkp_default(2, "OL BYPASS"), + info_lkp_default(3, "OL"), + info_lkp_default(4, "OB"), + info_lkp_default(5, ""), /* combined */ + info_lkp_default(6, "OL ECO"), + info_lkp_default(7, "OB ECO"), + info_lkp_sentinel }; static info_lkp_t huawei_battstate_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* not connected */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* not charging or discharging */ - { 3, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* hibernation */ - { 4, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* float */ - { 5, "CHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* equalized charging */ - { 6, "DISCHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* not connected */ + info_lkp_default(2, ""), /* not charging or discharging */ + info_lkp_default(3, ""), /* hibernation */ + info_lkp_default(4, ""), /* float */ + info_lkp_default(5, "CHRG"), /* equalized charging */ + info_lkp_default(6, "DISCHRG"), + info_lkp_sentinel }; static info_lkp_t huawei_phase_info[] = { - { 1, "1" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "3" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "1"), + info_lkp_default(2, "3"), + info_lkp_sentinel }; static info_lkp_t huawei_voltrating_info[] = { - { 1, "200" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "208" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "220" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "380" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "400" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "415" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "480" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "600" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "690" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "200"), + info_lkp_default(2, "208"), + info_lkp_default(3, "220"), + info_lkp_default(4, "380"), + info_lkp_default(5, "400"), + info_lkp_default(6, "415"), + info_lkp_default(7, "480"), + info_lkp_default(8, "600"), + info_lkp_default(9, "690"), + info_lkp_sentinel }; static info_lkp_t huawei_freqrating_info[] = { - { 1, "50" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "60" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "50"), + info_lkp_default(2, "60"), + info_lkp_sentinel }; static info_lkp_t huawei_pwrrating_info[] = { - { 1, "80000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "100000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "120000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "160000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "200000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "30000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "40000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "60000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "2400000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "2500000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 11, "2800000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 12, "3000000" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "80000"), + info_lkp_default(2, "100000"), + info_lkp_default(3, "120000"), + info_lkp_default(4, "160000"), + info_lkp_default(5, "200000"), + info_lkp_default(6, "30000"), + info_lkp_default(7, "40000"), + info_lkp_default(8, "60000"), + info_lkp_default(9, "2400000"), + info_lkp_default(10, "2500000"), + info_lkp_default(11, "2800000"), + info_lkp_default(12, "3000000"), + info_lkp_sentinel }; /* Note: This is currently identical to ietf_test_result_info from IETF MIB @@ -291,41 +103,13 @@ static info_lkp_t huawei_pwrrating_info[] = { * b) avoid namespace conflicts, especially with DMF loader of named objects */ static info_lkp_t huawei_test_result_info[] = { - { 1, "done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "done and warning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "aborted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "in progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "no test initiated" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "done and passed"), + info_lkp_default(2, "done and warning"), + info_lkp_default(3, "done and error"), + info_lkp_default(4, "aborted"), + info_lkp_default(5, "in progress"), + info_lkp_default(6, "no test initiated"), + info_lkp_sentinel }; @@ -333,7 +117,7 @@ static info_lkp_t huawei_test_result_info[] = { static snmp_info_t huawei_mib[] = { /* Data format: - * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * snmp_info_default(info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar), * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo @@ -345,110 +129,115 @@ static snmp_info_t huawei_mib[] = { * oid2info: lookup table between OID and NUT values * * Example: - * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, - * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, huawei_onbatt_info }, + * snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL), + * snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, huawei_onbatt_info), * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t huawei_onbatt_info[] = { - * { 1, "OB" }, - * { 2, "OL" }, - * { 0, NULL } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.1.1.2.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.1.1.2.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), - { "ups.time", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.11.1.0", NULL, SU_FLAG_OK, NULL }, /* seconds since epoch */ + snmp_info_default("ups.time", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.11.1.0", NULL, SU_FLAG_OK, NULL), /* seconds since epoch */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.3.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.5.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.3.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.5.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), - { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.1.1", NULL, SU_FLAG_OK, huawei_supplymethod_info }, - { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.3.1", NULL, SU_STATUS_BATT | SU_FLAG_OK, huawei_battstate_info }, + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.1.1", NULL, SU_FLAG_OK, huawei_supplymethod_info), + snmp_info_default("ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.3.1", NULL, SU_STATUS_BATT | SU_FLAG_OK, huawei_battstate_info), - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, huawei_test_result_info }, + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, huawei_test_result_info), /* Input page */ /* hwUpsCtrlInputStandard listed in MIB but not present on tested UPS5000-E */ - { "input.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.8", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info }, + snmp_info_default("input.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.8", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info), - { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.1.1", NULL, SU_FLAG_OK, NULL }, - { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.2.1", NULL, SU_FLAG_OK, NULL }, - { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.1.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.2.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.3.1", NULL, SU_FLAG_OK, NULL), - { "input.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.4.1", NULL, SU_FLAG_OK, NULL), - { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.5.1", NULL, SU_FLAG_OK, NULL }, - { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.6.1", NULL, SU_FLAG_OK, NULL }, - { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.5.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.6.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.7.1", NULL, SU_FLAG_OK, NULL), - { "input.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.8.1", NULL, SU_FLAG_OK, NULL }, - { "input.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.9.1", NULL, SU_FLAG_OK, NULL }, - { "input.L3.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.8.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.9.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.L3.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.10.1", NULL, SU_FLAG_OK, NULL), - { "input.bypass.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.1.1", NULL, SU_FLAG_OK, NULL }, - { "input.bypass.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.2.1", NULL, SU_FLAG_OK, NULL }, - { "input.bypass.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.bypass.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.1.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.bypass.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.2.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.bypass.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.3.1", NULL, SU_FLAG_OK, NULL), - { "input.bypass.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.bypass.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.4.1", NULL, SU_FLAG_OK, NULL), /* Output page */ /* hwUpsCtrlOutputStandard listed in MIB but not present on tested UPS5000-E */ - { "output.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.9", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info }, + snmp_info_default("output.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.9", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info), - { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.1.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.2.1", NULL, SU_FLAG_OK, NULL }, - { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.1.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.2.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.3.1", NULL, SU_FLAG_OK, NULL), - { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.4.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.5.1", NULL, SU_FLAG_OK, NULL }, - { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.4.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.5.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.6.1", NULL, SU_FLAG_OK, NULL), - { "output.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.7.1", NULL, SU_FLAG_OK, NULL), - { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.8.1", NULL, SU_FLAG_OK, NULL }, - { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.9.1", NULL, SU_FLAG_OK, NULL }, - { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.8.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.9.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.10.1", NULL, SU_FLAG_OK, NULL), - { "output.L1.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.11.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.12.1", NULL, SU_FLAG_OK, NULL }, - { "output.L3.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.13.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.11.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.12.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L3.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.13.1", NULL, SU_FLAG_OK, NULL), - { "output.L1.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.14.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.15.1", NULL, SU_FLAG_OK, NULL }, - { "output.L3.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.16.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.14.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.15.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L3.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.16.1", NULL, SU_FLAG_OK, NULL), - { "output.voltage.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.17.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_voltrating_info }, - { "output.frequency.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.18.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_freqrating_info }, - { "output.power.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.6.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_pwrrating_info }, + snmp_info_default("output.voltage.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.17.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_voltrating_info), + snmp_info_default("output.frequency.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.18.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_freqrating_info), + snmp_info_default("output.power.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.6.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_pwrrating_info), - { "output.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.19.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.20.1", NULL, SU_FLAG_OK, NULL }, - { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.21.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("output.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.19.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.20.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.21.1", NULL, SU_FLAG_OK, NULL), /* Battery page */ - { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.1.1", NULL, SU_FLAG_OK, NULL }, - { "battery.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.2.1", NULL, SU_FLAG_OK, NULL }, - { "battery.charge", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.3.1", NULL, SU_FLAG_OK, NULL }, - { "battery.runtime", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.4.1", NULL, SU_FLAG_OK, NULL }, - - - /* { "unmapped.hwUpsBattTest", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.103.101.1.6.1", NULL, SU_FLAG_OK, NULL }, */ + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.1.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("battery.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.2.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("battery.charge", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.3.1", NULL, SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.4.1", NULL, SU_FLAG_OK, NULL), +#if WITH_UNMAPPED_DATA_POINTS + snmp_info_default("unmapped.hwUpsBattTest", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.103.101.1.6.1", NULL, SU_FLAG_OK, NULL), +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t huawei = { "huawei", HUAWEI_MIB_VERSION, NULL, HUAWEI_OID_MODEL_NAME, huawei_mib, HUAWEI_SYSOID, NULL }; diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c index c144fcc26d..9ebf86ff28 100644 --- a/drivers/huawei-ups2000.c +++ b/drivers/huawei-ups2000.c @@ -1,33 +1,21 @@ /* * huawei-ups2000.c - Driver for Huawei UPS2000 (1kVA-3kVA) * - * Note: Huawei UPS2000 (1kVA-3kVA) can be accessed via RS-232, - * USB, or an optional RMS-MODBUS01B (RS-485) adapter. Only - * RS-232 and USB are supported, RS-485 is not. + * Note: If you're trying to debug the driver because it doesn't work, + * please BE SURE to read the manual in "docs/man/huawei-ups2000.txt" + * first! Otherwise you are guaranteed to waste your time! * - * The USB port on the UPS is implemented via a MaxLinear RX21V1410 - * USB-to-serial converter, and can be recongized as a standard - * USB-CDC serial device. Unfortunately, the generic USB-CDC driver - * is incompatible with the specific chip configuration and cannot - * be used. A device-specific driver, "xr_serial", must be used. - * - * The driver has only been merged to Linux 5.12 or later, via the - * "xr_serial" kernel module. When the UPS2000 is connected via USB - * to a supported Linux system, you should see the following logs in - * "dmesg". - * - * xr_serial 1-1.2:1.1: xr_serial converter detected - * usb 1-1.2: xr_serial converter now attached to ttyUSB0 - * - * The driver must be "xr_serial". If your system doesn't have the - * necessary device driver, you will get this message instead: - * - * cdc_acm 1-1.2:1.0: ttyACM0: USB ACM device - * - * On other operating systems, USB cannot be used due to the absence - * of the driver. You must use connect UPS2000 to your computer via - * RS-232, either directly or using an USB-to-RS-232 converter supported - * by your Linux or BSD kernel. + * Long story short, Huawei UPS2000 (1kVA-3kVA) can be accessed via + * RS-232, USB, or an optional RMS-MODBUS01B (RS-485) adapter. Only + * RS-232 and USB are supported, RS-485 is not. Also, for most UPS + * units, their USB ports are implemented via the MaxLinear RX21V1410 + * USB-to-serial converter, and they DO NOT WORK without a special + * "xr_serial" driver, only available on Linux 5.12+ (not BSD or Solaris). + * Without this driver, the USB can still be recognized as a generic + * USB ACM device, but it DOES NOT WORK. Alternatively, some newer UPS + * units use the WCH CH341 chip, which should have better compatibility. + * Detailed information will not be repeated here, please read + * "docs/man/huawei-ups2000.txt". * * A document describing the protocol implemented by this driver can * be found online at: @@ -36,7 +24,7 @@ * * Huawei UPS2000 driver implemented by * Copyright (C) 2020, 2021 Yifeng Li - * The author is not affiliated to Huawei or other manufacturers. + * The author is not affiliated with Huawei or other manufacturers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,9 +47,11 @@ #include #include "main.h" #include "serial.h" +#include "nut_stdint.h" +#include "timehead.h" /* fallback gmtime_r() variants if needed (e.g. some WIN32) */ #define DRIVER_NAME "NUT Huawei UPS2000 (1kVA-3kVA) RS-232 Modbus driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.05" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) #define MODBUS_SLAVE_ID 1 @@ -72,7 +62,7 @@ * model of the UPS2000 series. */ static const char *supported_model[] = { - "UPS2000", "UPS2000A", + "UPS2000", "UPS2000A", "UPS2000G", NULL }; @@ -169,7 +159,7 @@ static size_t ups2000_read_serial(uint8_t *buf, size_t buf_len); static int ups2000_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); static int ups2000_write_register(modbus_t *ctx, int addr, uint16_t val); static int ups2000_write_registers(modbus_t *ctx, int addr, int nb, uint16_t *src); -static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length); +static uint16_t crc16(uint8_t *buffer, size_t buffer_length); static time_t time_seek(time_t t, int seconds); /* rw variables function prototypes */ @@ -235,12 +225,15 @@ void upsdrv_initups(void) fatalx(EXIT_FAILURE, "Unable to create the libmodbus context"); #if LIBMODBUS_VERSION_CHECK(3, 1, 2) - /* It can take as slow as 1 sec. for the UPS to respond. */ - modbus_set_response_timeout(modbus_ctx, 1, 0); + /* + * Although it rarely occurs, it can take as slow as 2 sec. for the + * UPS to respond a read and finish transmitting the message. + */ + modbus_set_response_timeout(modbus_ctx, 2, 0); #else { struct timeval timeout; - timeout.tv_sec = 1; + timeout.tv_sec = 2; timeout.tv_usec = 0; modbus_set_response_timeout(modbus_ctx, &timeout); } @@ -345,20 +338,13 @@ static void ups2000_device_identification(void) if (ptr + IDENT_RESPONSE_HEADER_LEN > ident_response_end) { fatalx(EXIT_FAILURE, "response header too short! " - "expected %d, received %zu.", + "expected %d, received %" PRIuSIZE ".", IDENT_RESPONSE_HEADER_LEN, ident_response_len); } /* step 3: check response CRC-16 */ - crc16_recv = (uint16_t)((uint16_t)(ident_response_end[0]) << 8) | (uint16_t)(ident_response_end[1]); - if (ident_response_len < IDENT_RESPONSE_CRC_LEN - || (((uintmax_t)(ident_response_len) - IDENT_RESPONSE_CRC_LEN) > UINT16_MAX) - ) { - fatalx(EXIT_FAILURE, "response header shorter than CRC " - "or longer than UINT16_MAX!"); - } - - crc16_calc = crc16(ident_response, (uint16_t)(ident_response_len - IDENT_RESPONSE_CRC_LEN)); + crc16_recv = (uint16_t) ident_response_end[0] << 8 | ident_response_end[1]; + crc16_calc = crc16(ident_response, ident_response_len - IDENT_RESPONSE_CRC_LEN); if (crc16_recv == crc16_calc) { crc16_fail = 0; break; @@ -1441,7 +1427,8 @@ static int ups2000_delay_set(const char *var, const char *string) * used to handle commands that needs additional processing. * If "reg1" is not necessary or unsuitable, "-1" is used. */ -#define REG_NONE -1, -1 +#define REG_NULL -1, -1 +#define FUNC_NULL NULL static struct ups2000_cmd_t { const char *cmd; @@ -1449,20 +1436,20 @@ static struct ups2000_cmd_t { int (*const handler_func)(const uint16_t); } ups2000_cmd[] = { - { "test.battery.start.quick", 2028, 1, REG_NONE, NULL }, - { "test.battery.start.deep", 2021, 1, REG_NONE, NULL }, - { "test.battery.stop", 2023, 1, REG_NONE, NULL }, - { "beeper.enable", 1046, 0, REG_NONE, NULL }, - { "beeper.disable", 1046, 1, REG_NONE, NULL }, - { "load.off", 1045, 0, 1030, 1, NULL }, - { "bypass.stop", 1029, 1, 1045, 0, NULL }, - { "load.on", 1029, -1, REG_NONE, ups2000_instcmd_load_on }, - { "bypass.start", REG_NONE, REG_NONE, ups2000_instcmd_bypass_start }, - { "beeper.toggle", 1046, -1, REG_NONE, ups2000_instcmd_beeper_toggle }, - { "shutdown.stayoff", 1049, -1, REG_NONE, ups2000_instcmd_shutdown_stayoff }, - { "shutdown.return", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_return }, - { "shutdown.reboot", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_reboot }, - { "shutdown.reboot.graceful", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_reboot_graceful }, + { "test.battery.start.quick", 2028, 1, REG_NULL, FUNC_NULL }, + { "test.battery.start.deep", 2021, 1, REG_NULL, FUNC_NULL }, + { "test.battery.stop", 2023, 1, REG_NULL, FUNC_NULL }, + { "beeper.enable", 1046, 0, REG_NULL, FUNC_NULL }, + { "beeper.disable", 1046, 1, REG_NULL, FUNC_NULL }, + { "load.off", 1045, 0, 1030, 1, FUNC_NULL }, + { "bypass.stop", 1029, 1, 1045, 0, FUNC_NULL }, + { "load.on", 1029, -1, REG_NULL, ups2000_instcmd_load_on }, + { "bypass.start", REG_NULL, REG_NULL, ups2000_instcmd_bypass_start }, + { "beeper.toggle", 1046, -1, REG_NULL, ups2000_instcmd_beeper_toggle }, + { "shutdown.stayoff", 1049, -1, REG_NULL, ups2000_instcmd_shutdown_stayoff }, + { "shutdown.return", REG_NULL, REG_NULL, ups2000_instcmd_shutdown_return }, + { "shutdown.reboot", REG_NULL, REG_NULL, ups2000_instcmd_shutdown_reboot }, + { "shutdown.reboot.graceful", REG_NULL, REG_NULL, ups2000_instcmd_shutdown_reboot_graceful }, { NULL, -1, -1, -1, -1, NULL }, }; @@ -1814,8 +1801,10 @@ void upsdrv_shutdown(void) int r; r = instcmd("shutdown.reboot", ""); - if (r != STAT_INSTCMD_HANDLED) - fatalx(EXIT_FAILURE, "upsdrv_shutdown failed!"); + if (r != STAT_INSTCMD_HANDLED) { + upslogx(LOG_ERR, "upsdrv_shutdown failed!"); + set_exit_flag(-1); + } } @@ -1908,11 +1897,16 @@ static size_t ups2000_read_serial(uint8_t *buf, size_t buf_len) else if (bytes == 0) return total; /* nothing to read */ + if ((size_t) bytes > buf_len) { + /* + * Assertion: This should never happen. bytes is always less or equal + * to buf_len, and buf_len will never underflow under any circumstances. + */ + fatalx(EXIT_FAILURE, "ups2000_read_serial() reads too much!"); + } + total += (size_t)bytes; /* increment byte counter */ buf += bytes; /* advance buffer position */ - if ((size_t)bytes > buf_len) { - fatalx(EXIT_FAILURE, "ups2000_read_serial() read too much!"); - } buf_len -= (size_t)bytes; /* decrement limiter */ } return 0; /* buffer exhaustion */ @@ -1956,10 +1950,22 @@ static int ups2000_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *des "Please file a bug report!", addr); for (i = 0; i < 3; i++) { + /* + * If the previous read failed with a timeout, often there + * are still unprocessed bytes in the serial buffer and they + * would be mixed with the new data, creating invalid messages, + * making all subsequent reads to fail as well. + * + * Flush read buffer first to avoid it. + */ + modbus_flush(ctx); + r = modbus_read_registers(ctx, addr, nb, dest); /* generic retry for modbus read failures. */ if (retry_status == RETRY_ENABLE && r != nb) { + upslogx(LOG_WARNING, "modbus_read_registers() failed (%d, errno %d): %s", + r, errno, modbus_strerror(errno)); upslogx(LOG_WARNING, "Register %04d has a read failure. Retrying...", addr); sleep(1); continue; @@ -1985,7 +1991,9 @@ static int ups2000_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *des } /* Give up */ - upslogx(LOG_WARNING, "Register %04d has a fatal read failure.", addr); + upslogx(LOG_ERR, "modbus_read_registers() failed (%d, errno %d): %s", + r, errno, modbus_strerror(errno)); + upslogx(LOG_ERR, "Register %04d has a fatal read failure.", addr); retry_status = RETRY_DISABLE_TEMPORARY; return r; } @@ -2005,6 +2013,8 @@ static int ups2000_write_registers(modbus_t *ctx, int addr, int nb, uint16_t *sr /* generic retry for modbus write failures. */ if (retry_status == RETRY_ENABLE && r != nb) { + upslogx(LOG_WARNING, "modbus_write_registers() failed (%d, errno %d): %s", + r, errno, modbus_strerror(errno)); upslogx(LOG_WARNING, "Register %04d has a write failure. Retrying...", addr); sleep(1); continue; @@ -2016,7 +2026,9 @@ static int ups2000_write_registers(modbus_t *ctx, int addr, int nb, uint16_t *sr } /* Give up */ - upslogx(LOG_WARNING, "Register %04d has a fatal write failure.", addr); + upslogx(LOG_ERR, "modbus_write_registers() failed (%d, errno %d): %s", + r, errno, modbus_strerror(errno)); + upslogx(LOG_ERR, "Register %04d has a fatal write failure.", addr); retry_status = RETRY_DISABLE_TEMPORARY; return r; } @@ -2105,7 +2117,7 @@ static const uint8_t table_crc_lo[] = { }; -static uint16_t crc16(uint8_t * buffer, uint16_t buffer_length) +static uint16_t crc16(uint8_t * buffer, size_t buffer_length) { uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ @@ -2118,5 +2130,5 @@ static uint16_t crc16(uint8_t * buffer, uint16_t buffer_length) crc_lo = table_crc_lo[i]; } - return ((uint16_t)((uint16_t)(crc_hi) << 8) | (uint16_t)crc_lo); + return (uint16_t) crc_hi << 8 | crc_lo; } diff --git a/drivers/idowell-hid.c b/drivers/idowell-hid.c index f96958f72f..e20505d2dc 100644 --- a/drivers/idowell-hid.c +++ b/drivers/idowell-hid.c @@ -65,7 +65,7 @@ static usage_tables_t idowell_utab[] = { /* --------------------------------------------------------------- */ static hid_info_t idowell_hid2nut[] = { -#ifdef DEBUG +#if WITH_UNMAPPED_DATA_POINTS || (defined DEBUG) { "unmapped.ups.flow.[4].flowid", 0, 0, "UPS.Flow.[4].FlowID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powerconverter.output.outputid", 0, 0, "UPS.PowerConverter.Output.OutputID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powerconverter.powerconverterid", 0, 0, "UPS.PowerConverter.PowerConverterID", NULL, "%.0f", 0, NULL }, @@ -78,7 +78,7 @@ static hid_info_t idowell_hid2nut[] = { { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.presentstatus.undefined", 0, 0, "UPS.PowerSummary.PresentStatus.Undefined", NULL, "%.0f", 0, NULL }, -#endif /* DEBUG */ +#endif /* if WITH_UNMAPPED_DATA_POINTS || DEBUG */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, diff --git a/drivers/ietf-mib.c b/drivers/ietf-mib.c index 8763efecc5..a66afa9b87 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.55" /* SNMP OIDs set */ #define IETF_OID_UPS_MIB "1.3.6.1.2.1.33.1." @@ -46,395 +46,249 @@ /* #define DEBUG */ static info_lkp_t ietf_battery_info[] = { - { 1, "" /* unknown */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" /* batteryNormal */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "LB" /* batteryLow */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "LB" /* batteryDepleted */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, ""), /* batteryNormal */ + info_lkp_default(3, "LB"), /* batteryLow */ + info_lkp_default(4, "LB"), /* batteryDepleted */ + info_lkp_sentinel }; static info_lkp_t ietf_power_source_info[] = { - { 1, "" /* other */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OFF" /* none */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "OL" /* normal */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "OL BYPASS" /* bypass */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "OB" /* battery */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "OL BOOST" /* booster */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "OL TRIM" /* reducer */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* other */ + info_lkp_default(2, "OFF"), /* none */ + info_lkp_default(3, "OL"), /* normal */ + info_lkp_default(4, "OL BYPASS"), /* bypass */ + info_lkp_default(5, "OB"), /* battery */ + info_lkp_default(6, "OL BOOST"), /* booster */ + info_lkp_default(7, "OL TRIM"), /* reducer */ + info_lkp_sentinel }; static info_lkp_t ietf_overload_info[] = { - { 1, "OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output overload */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OVER"), /* output overload */ + info_lkp_sentinel }; static info_lkp_t ietf_test_active_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* upsTestNoTestsInitiated */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* upsTestAbortTestInProgress */ - { 3, "TEST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* upsTestGeneralSystemsTest */ - { 4, "TEST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* upsTestQuickBatteryTest */ - { 5, "CAL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* upsTestDeepBatteryCalibration */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* upsTestNoTestsInitiated */ + info_lkp_default(2, ""), /* upsTestAbortTestInProgress */ + info_lkp_default(3, "TEST"), /* upsTestGeneralSystemsTest */ + info_lkp_default(4, "TEST"), /* upsTestQuickBatteryTest */ + info_lkp_default(5, "CAL"), /* upsTestDeepBatteryCalibration */ + info_lkp_sentinel }; static info_lkp_t ietf_test_result_info[] = { - { 1, "done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "done and warning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "aborted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "in progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "no test initiated" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "done and passed"), + info_lkp_default(2, "done and warning"), + info_lkp_default(3, "done and error"), + info_lkp_default(4, "aborted"), + info_lkp_default(5, "in progress"), + info_lkp_default(6, "no test initiated"), + info_lkp_sentinel }; #ifdef DEBUG static info_lkp_t ietf_shutdown_type_info[] = { - { 1, "output" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "system" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "output"), + info_lkp_default(2, "system"), + info_lkp_sentinel }; #endif static info_lkp_t ietf_yes_no_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel }; static info_lkp_t ietf_beeper_status_info[] = { - { 1, "disabled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "enabled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "muted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "disabled"), + info_lkp_default(2, "enabled"), + info_lkp_default(3, "muted"), + info_lkp_sentinel }; /* Snmp2NUT lookup table info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ static snmp_info_t ietf_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.3.0", "", SU_FLAG_STATIC, NULL }, /* upsIdentUPSSoftwareVersion */ - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.4.0", "", SU_FLAG_STATIC, NULL }, /* upsIdentAgentSoftwareVersion */ + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.1.0", "Generic", SU_FLAG_STATIC, NULL), /* upsIdentManufacturer */ + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.2.0", "Generic SNMP UPS", SU_FLAG_STATIC, NULL), /* upsIdentModel */ + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.3.0", "", SU_FLAG_STATIC, NULL), /* upsIdentUPSSoftwareVersion */ + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.4.0", "", SU_FLAG_STATIC, NULL), /* upsIdentAgentSoftwareVersion */ #ifdef DEBUG - { "debug.upsIdentName", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.5.0", "", 0, NULL }, /* upsIdentName */ - { "debug.upsIdentAttachedDevices", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.6.0", "", 0, NULL }, /* upsIdentAttachedDevices */ + snmp_info_default("debug.upsIdentName", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.5.0", "", 0, NULL), /* upsIdentName */ + snmp_info_default("debug.upsIdentAttachedDevices", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.6.0", "", 0, NULL), /* upsIdentAttachedDevices */ #endif /* Battery Group */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "2.1.0", "", SU_STATUS_BATT, ietf_battery_info }, /* upsBatteryStatus */ + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "2.1.0", "", SU_STATUS_BATT, ietf_battery_info), /* upsBatteryStatus */ #ifdef DEBUG - { "debug.upsSecondsOnBattery", 0, 1.0, IETF_OID_UPS_MIB "2.2.0", "", 0, NULL }, /* upsSecondsOnBattery */ + snmp_info_default("debug.upsSecondsOnBattery", 0, 1.0, IETF_OID_UPS_MIB "2.2.0", "", 0, NULL), /* upsSecondsOnBattery */ #endif - { "battery.runtime", 0, 60.0, IETF_OID_UPS_MIB "2.3.0", "", 0, NULL }, /* upsEstimatedMinutesRemaining */ - { "battery.charge", 0, 1, IETF_OID_UPS_MIB "2.4.0", "", 0, NULL }, /* upsEstimatedChargeRemaining */ - { "battery.voltage", 0, 0.1, IETF_OID_UPS_MIB "2.5.0", "", 0, NULL }, /* upsBatteryVoltage */ - { "battery.current", 0, 0.1, IETF_OID_UPS_MIB "2.6.0", "", 0, NULL }, /* upsBatteryCurrent */ - { "battery.temperature", 0, 1.0, IETF_OID_UPS_MIB "2.7.0", "", 0, NULL }, /* upsBatteryTemperature */ + snmp_info_default("battery.runtime", 0, 60.0, IETF_OID_UPS_MIB "2.3.0", "", 0, NULL), /* upsEstimatedMinutesRemaining */ + snmp_info_default("battery.charge", 0, 1, IETF_OID_UPS_MIB "2.4.0", "", 0, NULL), /* upsEstimatedChargeRemaining */ + snmp_info_default("battery.voltage", 0, 0.1, IETF_OID_UPS_MIB "2.5.0", "", 0, NULL), /* upsBatteryVoltage */ + snmp_info_default("battery.current", 0, 0.1, IETF_OID_UPS_MIB "2.6.0", "", SU_FLAG_NEGINVALID, NULL), /* upsBatteryCurrent */ + snmp_info_default("battery.temperature", 0, 1.0, IETF_OID_UPS_MIB "2.7.0", "", 0, NULL), /* upsBatteryTemperature */ /* Input Group */ #ifdef DEBUG - { "debug.upsInputLineBads", 0, 1.0, IETF_OID_UPS_MIB "3.1.0", "", 0, NULL }, /* upsInputLineBads */ + snmp_info_default("debug.upsInputLineBads", 0, 1.0, IETF_OID_UPS_MIB "3.1.0", "", 0, NULL), /* upsInputLineBads */ #endif - { "input.phases", 0, 1.0, IETF_OID_UPS_MIB "3.2.0", "", 0, NULL }, /* upsInputNumLines */ + snmp_info_default("input.phases", 0, 1.0, IETF_OID_UPS_MIB "3.2.0", "", 0, NULL), /* upsInputNumLines */ #ifdef DEBUG - { "debug.upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_1, NULL }, /* upsInputLineIndex */ - { "debug.[1].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_3, NULL }, - { "debug.[2].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.2", "", SU_INPUT_3, NULL }, - { "debug.[3].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.3", "", SU_INPUT_3, NULL }, -#endif - { "input.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_1, NULL }, /* upsInputFrequency */ - { "input.L1.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_3, NULL }, - { "input.L2.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.2", "", SU_INPUT_3, NULL }, - { "input.L3.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.3", "", SU_INPUT_3, NULL }, - { "input.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_1, NULL }, /* upsInputVoltage */ - { "input.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.3", "", SU_INPUT_3, NULL }, - { "input.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_1, NULL }, /* upsInputCurrent */ - { "input.L1.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_3, NULL }, - { "input.L2.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.2", "", SU_INPUT_3, NULL }, - { "input.L3.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.3", "", SU_INPUT_3, NULL }, - { "input.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_1, NULL }, /* upsInputTruePower */ - { "input.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_3, NULL }, - { "input.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.2", "", SU_INPUT_3, NULL }, - { "input.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.3", "", SU_INPUT_3, NULL }, + snmp_info_default("debug.upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_1, NULL), /* upsInputLineIndex */ + snmp_info_default("debug.[1].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_3, NULL), + snmp_info_default("debug.[2].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.2", "", SU_INPUT_3, NULL), + snmp_info_default("debug.[3].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.3", "", SU_INPUT_3, NULL), +#endif + snmp_info_default("input.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_1, NULL), /* upsInputFrequency */ + snmp_info_default("input.L1.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.frequency", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.2.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_1, NULL), /* upsInputVoltage */ + snmp_info_default("input.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.3.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_1 |SU_FLAG_NEGINVALID, NULL), /* upsInputCurrent */ + snmp_info_default("input.L1.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 0.1, IETF_OID_UPS_MIB "3.3.1.4.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_1 | SU_FLAG_NEGINVALID, NULL), /* upsInputTruePower */ + snmp_info_default("input.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.5.3", "", SU_INPUT_3, NULL), /* Output Group */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "4.1.0", "", SU_STATUS_PWR, ietf_power_source_info }, /* upsOutputSource */ - { "output.frequency", 0, 0.1, IETF_OID_UPS_MIB "4.2.0", "", 0, NULL }, /* upsOutputFrequency */ - { "output.phases", 0, 1.0, IETF_OID_UPS_MIB "4.3.0", "", 0, NULL }, /* upsOutputNumLines */ + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "4.1.0", "", SU_STATUS_PWR, ietf_power_source_info), /* upsOutputSource */ + snmp_info_default("output.frequency", 0, 0.1, IETF_OID_UPS_MIB "4.2.0", "", 0, NULL), /* upsOutputFrequency */ + snmp_info_default("output.phases", 0, 1.0, IETF_OID_UPS_MIB "4.3.0", "", 0, NULL), /* upsOutputNumLines */ #ifdef DEBUG - { "debug.upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_1, NULL }, /* upsOutputLineIndex */ - { "debug.[1].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_3, NULL }, - { "debug.[2].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.2", "", SU_OUTPUT_3, NULL }, - { "debug.[3].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.3", "", SU_OUTPUT_3, NULL }, -#endif - { "output.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_1, NULL }, /* upsOutputVoltage */ - { "output.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.2", "", SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.3", "", SU_OUTPUT_3, NULL }, - { "output.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_1, NULL }, /* upsOutputCurrent */ - { "output.L1.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.3", "", SU_OUTPUT_3, NULL }, - { "output.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_1, NULL }, /* upsOutputPower */ - { "output.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.3", "", SU_OUTPUT_3, NULL }, - { "ups.load", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_1, NULL }, /* upsOutputPercentLoad */ - { "output.L1.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.3", "", SU_OUTPUT_3, NULL }, + snmp_info_default("debug.upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_1, NULL), /* upsOutputLineIndex */ + snmp_info_default("debug.[1].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("debug.[2].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("debug.[3].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.3", "", SU_OUTPUT_3, NULL), +#endif + snmp_info_default("output.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_1, NULL), /* upsOutputVoltage */ + snmp_info_default("output.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.2.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_1 | SU_FLAG_NEGINVALID, NULL), /* upsOutputCurrent */ + snmp_info_default("output.L1.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 0.1, IETF_OID_UPS_MIB "4.4.1.3.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_1 | SU_FLAG_NEGINVALID, NULL), /* upsOutputPower */ + snmp_info_default("output.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.4.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.load", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_1, NULL), /* upsOutputPercentLoad */ + snmp_info_default("output.L1.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.3", "", SU_OUTPUT_3, NULL), /* Bypass Group */ - { "input.bypass.phases", 0, 1.0, IETF_OID_UPS_MIB "5.2.0", "", 0, NULL }, /* upsBypassNumLines */ - { "input.bypass.frequency", 0, 0.1, IETF_OID_UPS_MIB "5.1.0", "", SU_BYPASS_1 | SU_BYPASS_3, NULL }, /* upsBypassFrequency */ + snmp_info_default("input.bypass.phases", 0, 1.0, IETF_OID_UPS_MIB "5.2.0", "", SU_FLAG_NEGINVALID, NULL), /* upsBypassNumLines */ + snmp_info_default("input.bypass.frequency", 0, 0.1, IETF_OID_UPS_MIB "5.1.0", "", SU_BYPASS_1 | SU_BYPASS_3 | SU_FLAG_NEGINVALID, NULL), /* upsBypassFrequency */ #ifdef DEBUG - { "debug.upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_1, NULL }, /* upsBypassLineIndex */ - { "debug.[1].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_3, NULL }, - { "debug.[2].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.2", "", SU_BYPASS_3, NULL }, - { "debug.[3].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.3", "", SU_BYPASS_3, NULL }, -#endif - { "input.bypass.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_1, NULL }, /* upsBypassVoltage */ - { "input.bypass.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.3", "", SU_BYPASS_3, NULL }, - { "input.bypass.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_1, NULL }, /* upsBypassCurrent */ - { "input.bypass.L1.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.3", "", SU_BYPASS_3, NULL }, - { "input.bypass.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_1, NULL }, /* upsBypassPower */ - { "input.bypass.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.3", "", SU_BYPASS_3, NULL }, + snmp_info_default("debug.upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_1, NULL), /* upsBypassLineIndex */ + snmp_info_default("debug.[1].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_3, NULL), + snmp_info_default("debug.[2].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.2", "", SU_BYPASS_3, NULL), + snmp_info_default("debug.[3].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.3", "", SU_BYPASS_3, NULL), +#endif + snmp_info_default("input.bypass.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_1, NULL), /* upsBypassVoltage */ + snmp_info_default("input.bypass.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.2", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.3", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_1, NULL), /* upsBypassCurrent */ + snmp_info_default("input.bypass.L1.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L2.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.2", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L3.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.3", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_1, NULL), /* upsBypassPower */ + snmp_info_default("input.bypass.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.2", "", SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.3", "", SU_BYPASS_3, NULL), /* Alarm Group */ #ifdef DEBUG - { "debug.upsAlarmsPresent", 0, 1.0, IETF_OID_UPS_MIB "6.1.0", "", 0, NULL }, /* upsAlarmsPresent */ - { "debug.upsAlarmBatteryBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.1", "", 0, NULL }, /* upsAlarmBatteryBad */ - { "debug.upsAlarmOnBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.2", "", 0, NULL }, /* upsAlarmOnBattery */ - { "debug.upsAlarmLowBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.3", "", 0, NULL }, /* upsAlarmLowBattery */ - { "debug.upsAlarmDepletedBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.4", "", 0, NULL }, /* upsAlarmDepletedBattery */ - { "debug.upsAlarmTempBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.5", "", 0, NULL }, /* upsAlarmTempBad */ - { "debug.upsAlarmInputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.6", "", 0, NULL }, /* upsAlarmInputBad */ - { "debug.upsAlarmOutputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.7", "", 0, NULL }, /* upsAlarmOutputBad */ -#endif - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.8", "", 0, ietf_overload_info }, /* upsAlarmOutputOverload */ + snmp_info_default("debug.upsAlarmsPresent", 0, 1.0, IETF_OID_UPS_MIB "6.1.0", "", 0, NULL), /* upsAlarmsPresent */ + snmp_info_default("debug.upsAlarmBatteryBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.1", "", 0, NULL), /* upsAlarmBatteryBad */ + snmp_info_default("debug.upsAlarmOnBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.2", "", 0, NULL), /* upsAlarmOnBattery */ + snmp_info_default("debug.upsAlarmLowBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.3", "", 0, NULL), /* upsAlarmLowBattery */ + snmp_info_default("debug.upsAlarmDepletedBattery", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.4", "", 0, NULL), /* upsAlarmDepletedBattery */ + snmp_info_default("debug.upsAlarmTempBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.5", "", 0, NULL), /* upsAlarmTempBad */ + snmp_info_default("debug.upsAlarmInputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.6", "", 0, NULL), /* upsAlarmInputBad */ + snmp_info_default("debug.upsAlarmOutputBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.7", "", 0, NULL), /* upsAlarmOutputBad */ +#endif + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.8", "", 0, ietf_overload_info), /* upsAlarmOutputOverload */ #ifdef DEBUG - { "debug.upsAlarmOnBypass", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.9", "", 0, NULL }, /* upsAlarmOnBypass */ - { "debug.upsAlarmBypassBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.10", "", 0, NULL }, /* upsAlarmBypassBad */ - { "debug.upsAlarmOutputOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.11", "", 0, NULL }, /* upsAlarmOutputOffAsRequested */ - { "debug.upsAlarmUpsOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.12", "", 0, NULL }, /* upsAlarmUpsOffAsRequested */ - { "debug.upsAlarmChargerFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.13", "", 0, NULL }, /* upsAlarmChargerFailed */ - { "debug.upsAlarmUpsOutputOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.14", "", 0, NULL }, /* upsAlarmUpsOutputOff */ - { "debug.upsAlarmUpsSystemOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.15", "", 0, NULL }, /* upsAlarmUpsSystemOff */ - { "debug.upsAlarmFanFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.16", "", 0, NULL }, /* upsAlarmFanFailure */ - { "debug.upsAlarmFuseFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.17", "", 0, NULL }, /* upsAlarmFuseFailure */ - { "debug.upsAlarmGeneralFault", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.18", "", 0, NULL }, /* upsAlarmGeneralFault */ - { "debug.upsAlarmDiagnosticTestFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.19", "", 0, NULL }, /* upsAlarmDiagnosticTestFailed */ - { "debug.upsAlarmCommunicationsLost", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.20", "", 0, NULL }, /* upsAlarmCommunicationsLost */ - { "debug.upsAlarmAwaitingPower", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.21", "", 0, NULL }, /* upsAlarmAwaitingPower */ - { "debug.upsAlarmShutdownPending", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.22", "", 0, NULL }, /* upsAlarmShutdownPending */ - { "debug.upsAlarmShutdownImminent", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.23", "", 0, NULL }, /* upsAlarmShutdownImminent */ - { "debug.upsAlarmTestInProgress", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.24", "", 0, NULL }, /* upsAlarmTestInProgress */ + snmp_info_default("debug.upsAlarmOnBypass", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.9", "", 0, NULL), /* upsAlarmOnBypass */ + snmp_info_default("debug.upsAlarmBypassBad", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.10", "", 0, NULL), /* upsAlarmBypassBad */ + snmp_info_default("debug.upsAlarmOutputOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.11", "", 0, NULL), /* upsAlarmOutputOffAsRequested */ + snmp_info_default("debug.upsAlarmUpsOffAsRequested", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.12", "", 0, NULL), /* upsAlarmUpsOffAsRequested */ + snmp_info_default("debug.upsAlarmChargerFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.13", "", 0, NULL), /* upsAlarmChargerFailed */ + snmp_info_default("debug.upsAlarmUpsOutputOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.14", "", 0, NULL), /* upsAlarmUpsOutputOff */ + snmp_info_default("debug.upsAlarmUpsSystemOff", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.15", "", 0, NULL), /* upsAlarmUpsSystemOff */ + snmp_info_default("debug.upsAlarmFanFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.16", "", 0, NULL), /* upsAlarmFanFailure */ + snmp_info_default("debug.upsAlarmFuseFailure", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.17", "", 0, NULL), /* upsAlarmFuseFailure */ + snmp_info_default("debug.upsAlarmGeneralFault", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.18", "", 0, NULL), /* upsAlarmGeneralFault */ + snmp_info_default("debug.upsAlarmDiagnosticTestFailed", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.19", "", 0, NULL), /* upsAlarmDiagnosticTestFailed */ + snmp_info_default("debug.upsAlarmCommunicationsLost", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.20", "", 0, NULL), /* upsAlarmCommunicationsLost */ + snmp_info_default("debug.upsAlarmAwaitingPower", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.21", "", 0, NULL), /* upsAlarmAwaitingPower */ + snmp_info_default("debug.upsAlarmShutdownPending", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.22", "", 0, NULL), /* upsAlarmShutdownPending */ + snmp_info_default("debug.upsAlarmShutdownImminent", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.23", "", 0, NULL), /* upsAlarmShutdownImminent */ + snmp_info_default("debug.upsAlarmTestInProgress", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "6.3.24", "", 0, NULL), /* upsAlarmTestInProgress */ #endif /* Test Group */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.1.0", "", 0, ietf_test_active_info }, /* upsTestId */ - { "test.battery.stop", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestAbortTestInProgress */ - { "test.battery.start", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestGeneralSystemsTest */ - { "test.battery.start.quick", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestQuickBatteryTest */ - { "test.battery.start.deep", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestDeepBatteryCalibration */ + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.1.0", "", 0, ietf_test_active_info), /* upsTestId */ + snmp_info_default("test.battery.stop", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL), /* upsTestAbortTestInProgress */ + snmp_info_default("test.battery.start", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL), /* upsTestGeneralSystemsTest */ + snmp_info_default("test.battery.start.quick", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL), /* upsTestQuickBatteryTest */ + snmp_info_default("test.battery.start.deep", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL), /* upsTestDeepBatteryCalibration */ #ifdef DEBUG - { "debug.upsTestSpinLock", 0, 1.0, IETF_OID_UPS_MIB "7.2.0", "", 0, NULL }, /* upsTestSpinLock */ + snmp_info_default("debug.upsTestSpinLock", 0, 1.0, IETF_OID_UPS_MIB "7.2.0", "", 0, NULL), /* upsTestSpinLock */ #endif - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.3.0", "", 0, ietf_test_result_info }, /* upsTestResultsSummary */ + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.3.0", "", 0, ietf_test_result_info), /* upsTestResultsSummary */ #ifdef DEBUG - { "debug.upsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.4.0", "", 0, NULL }, /* upsTestResultsDetail */ - { "debug.upsTestStartTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.5.0", "", 0, NULL }, /* upsTestStartTime */ - { "debug.upsTestElapsedTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.6.0", "", 0, NULL }, /* upsTestElapsedTime */ + snmp_info_default("debug.upsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.4.0", "", 0, NULL), /* upsTestResultsDetail */ + snmp_info_default("debug.upsTestStartTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.5.0", "", 0, NULL), /* upsTestStartTime */ + snmp_info_default("debug.upsTestElapsedTime", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.6.0", "", 0, NULL), /* upsTestElapsedTime */ #endif /* Control Group */ #ifdef DEBUG - { "debug.upsShutdownType", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.1.0", "", 0, ietf_shutdown_type_info }, /* upsShutdownType */ -#endif - { "ups.timer.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.2.0", "", 0, NULL }, /* upsShutdownAfterDelay*/ - { "load.off", 0, 1, IETF_OID_UPS_MIB "8.2.0", "0", SU_TYPE_CMD, NULL }, - { "ups.timer.start", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.3.0", "", 0, NULL }, /* upsStartupAfterDelay */ - { "load.on", 0, 1, IETF_OID_UPS_MIB "8.3.0", "0", SU_TYPE_CMD, NULL }, - { "ups.timer.reboot", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.4.0", "", 0, NULL }, /* upsRebootWithDuration */ - { "ups.start.auto", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.5.0", "", 0, ietf_yes_no_info }, /* upsAutoRestart */ + snmp_info_default("debug.upsShutdownType", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.1.0", "", 0, ietf_shutdown_type_info), /* upsShutdownType */ +#endif + snmp_info_default("ups.timer.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.2.0", "", 0, NULL), /* upsShutdownAfterDelay*/ + snmp_info_default("load.off", 0, 1, IETF_OID_UPS_MIB "8.2.0", "0", SU_TYPE_CMD, NULL), + snmp_info_default("ups.timer.start", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.3.0", "", 0, NULL), /* upsStartupAfterDelay */ + snmp_info_default("load.on", 0, 1, IETF_OID_UPS_MIB "8.3.0", "0", SU_TYPE_CMD, NULL), + snmp_info_default("ups.timer.reboot", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.4.0", "", 0, NULL), /* upsRebootWithDuration */ + snmp_info_default("ups.start.auto", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.5.0", "", 0, ietf_yes_no_info), /* upsAutoRestart */ /* Configuration Group */ - { "input.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.1.0", "", 0, NULL }, /* upsConfigInputVoltage */ - { "input.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.2.0", "", 0, NULL }, /* upsConfigInputFreq */ - { "output.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.3.0", "", 0, NULL }, /* upsConfigOutputVoltage */ - { "output.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.4.0", "", 0, NULL }, /* upsConfigOutputFreq */ - { "output.power.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.5.0", "", 0, NULL }, /* upsConfigOutputVA */ - { "output.realpower.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.6.0", "", 0, NULL }, /* upsConfigOutputPower */ - { "battery.runtime.low", 0, 60.0, IETF_OID_UPS_MIB "9.7.0", "", 0, NULL }, /* upsConfigLowBattTime */ - { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "9.8.0", "", 0, ietf_beeper_status_info }, /* upsConfigAudibleStatus */ - { "beeper.disable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "1", SU_TYPE_CMD, NULL }, - { "beeper.enable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "2", SU_TYPE_CMD, NULL }, - { "beeper.mute", 0, 1, IETF_OID_UPS_MIB "9.8.0", "3", SU_TYPE_CMD, NULL }, - { "input.transfer.low", 0, 1.0, IETF_OID_UPS_MIB "9.9.0", "", 0, NULL }, /* upsConfigLowVoltageTransferPoint */ - { "input.transfer.high", 0, 1.0, IETF_OID_UPS_MIB "9.10.0", "", 0, NULL }, /* upsConfigHighVoltageTransferPoint */ + snmp_info_default("input.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.1.0", "", SU_FLAG_NEGINVALID, NULL), /* upsConfigInputVoltage */ + snmp_info_default("input.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.2.0", "", SU_FLAG_NEGINVALID, NULL), /* upsConfigInputFreq */ + snmp_info_default("output.voltage.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.3.0", "", 0, NULL), /* upsConfigOutputVoltage */ + snmp_info_default("output.frequency.nominal", 0, 0.1, IETF_OID_UPS_MIB "9.4.0", "", 0, NULL), /* upsConfigOutputFreq */ + snmp_info_default("output.power.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.5.0", "", SU_FLAG_NEGINVALID, NULL), /* upsConfigOutputVA */ + snmp_info_default("output.realpower.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.6.0", "", SU_FLAG_NEGINVALID, NULL), /* upsConfigOutputPower */ + snmp_info_default("battery.runtime.low", 0, 60.0, IETF_OID_UPS_MIB "9.7.0", "", 0, NULL), /* upsConfigLowBattTime */ + snmp_info_default("ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "9.8.0", "", 0, ietf_beeper_status_info), /* upsConfigAudibleStatus */ + snmp_info_default("beeper.disable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "1", SU_TYPE_CMD, NULL), + snmp_info_default("beeper.enable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "2", SU_TYPE_CMD, NULL), + snmp_info_default("beeper.mute", 0, 1, IETF_OID_UPS_MIB "9.8.0", "3", SU_TYPE_CMD, NULL), + snmp_info_default("input.transfer.low", 0, 1.0, IETF_OID_UPS_MIB "9.9.0", "", 0, NULL), /* upsConfigLowVoltageTransferPoint */ + snmp_info_default("input.transfer.high", 0, 1.0, IETF_OID_UPS_MIB "9.10.0", "", 0, NULL), /* upsConfigHighVoltageTransferPoint */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; /* FIXME: Rename the structure here (or even relocate to new file) diff --git a/drivers/isbmex.c b/drivers/isbmex.c index 8762562efc..923b46f7ee 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -27,7 +27,7 @@ #include #define DRIVER_NAME "ISBMEX UPS driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.08" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -148,10 +148,12 @@ void upsdrv_initinfo(void) } static const char *getpacket(int *we_know){ +#ifndef WIN32 fd_set readfds; struct timeval tv; - int bytes_per_packet=0; int ret; +#endif + int bytes_per_packet=0; static const char *packet_id=NULL; static char buf[256]; const char *s; @@ -160,35 +162,54 @@ static const char *getpacket(int *we_know){ bytes_per_packet=*we_know; D(printf("getpacket with %d\n",bytes_per_packet);) +#ifndef WIN32 FD_ZERO(&readfds); FD_SET(upsfd,&readfds); + /* Wait up to 2 seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; - ret=select(upsfd+1, &readfds, NULL, NULL, &tv); + ret = select(upsfd+1, &readfds, NULL, NULL, &tv); if (!ret) { - s="Nothing received from UPS. Check cable conexion"; + s = "Nothing received from UPS. Check cable conexion"; + upslogx(LOG_ERR, "%s", s); + D(printf("%s\n",s);) + return NULL; + } + + r = read(upsfd,buf,255); +#else + r = select_read(upsfd,buf,255,5,0); + if (r <= 0) { + s = "Nothing received from UPS. Check cable conexion"; upslogx(LOG_ERR, "%s", s); D(printf("%s\n",s);) return NULL; } +#endif + D(printf("%" PRIiSIZE " bytes read: ",r);) - r=read(upsfd,buf,255); - D(printf("%zd bytes read: ",r);) buf[r]=0; if (bytes_per_packet && r < bytes_per_packet){ ssize_t rr; D(printf("short read...\n");) usleep(500000); +#ifndef WIN32 tv.tv_sec = 2; tv.tv_usec = 0; - ret=select(upsfd+1, &readfds, NULL, NULL, &tv); + ret = select(upsfd+1, &readfds, NULL, NULL, &tv); if (!ret) return NULL; /* Casting is okay since bytes_per_packet is small * and r is smaller, so 255-r is positive */ assert (r <= 255); rr = read(upsfd, buf+r, (size_t)(255-r)); +#else + rr = select_read(upsfd,buf+r,255-r,2,0); + if (rr <= 0) { + return NULL; + } +#endif r += rr; if (r < bytes_per_packet) return NULL; } @@ -336,8 +357,11 @@ void upsdrv_shutdown(void) /* * here try to do the pin 9 trick, if it does not * work, else:*/ -/* fatalx(EXIT_FAILURE, "Shutdown only supported with the Generic Driver, type 6 and special cable"); */ - /*fatalx(EXIT_FAILURE, "shutdown not supported");*/ +/* + upslogx(LOG_ERR, "Shutdown only supported with the Generic Driver, type 6 and special cable"); + //upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); +*/ int i; for(i=0;i<=5;i++) { diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index 1c3cee73c6..3a07c72281 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -21,10 +21,11 @@ #include "config.h" #include "main.h" #include "serial.h" +#include "nut_stdint.h" #include "attribute.h" #define DRIVER_NAME "IVT Solar Controller driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -115,7 +116,7 @@ static ssize_t ivt_status(void) ret = sscanf(reply, "R:%f;%f;%f;%f;%f;%f;%f;", &battery.voltage.act, &battery.current.act, &battery.temperature, &battery.voltage.min, &battery.voltage.max, &battery.current.min, &battery.current.max); - upsdebugx(3, "Parsed %zd parameters from reply", ret); + upsdebugx(3, "Parsed %" PRIiSIZE " parameters from reply", ret); return ret; } @@ -180,9 +181,6 @@ void upsdrv_updateinfo(void) dstate_dataok(); } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { while (1) { @@ -195,7 +193,10 @@ void upsdrv_shutdown(void) continue; } - fatalx(EXIT_SUCCESS, "Power is back!"); + /* Hmmm, why was this an exit-case before? fatalx(EXIT_SUCCESS...) */ + upslogx(LOG_ERR, "Power is back!"); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; } } diff --git a/drivers/libhid.c b/drivers/libhid.c index 39fc22d982..74b9991973 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -12,7 +12,7 @@ * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for - * Network UPS Tools + * Network UPS Tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,15 +43,16 @@ #include "libhid.h" #include "hidparser.h" #include "common.h" /* for xmalloc, upsdebugx prototypes */ +#include "nut_stdint.h" /* Communication layers and drivers (USB and MGE SHUT) */ -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE #include "libshut.h" communication_subdriver_t *comm_driver = &shut_subdriver; -#else +#else /* !SHUT_MODE => USB */ #include "nut_libusb.h" communication_subdriver_t *comm_driver = &usb_subdriver; -#endif +#endif /* SHUT_MODE / USB */ /* support functions */ static double logical_to_physical(HIDData_t *Data, long logical); @@ -171,7 +172,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 +184,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 @@ -195,8 +199,8 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, - "%s: suggested buffer size %zu exceeds " - "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "%s: suggested buffer size %" PRIuSIZE " exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %" PRIuMAX "; " "report will be constrained", __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX @@ -220,7 +224,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 @@ -229,6 +233,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa (usb_ctrl_charbufsize)r); if (ret <= 0) { + errno = -ret; return -1; } r = (size_t)ret; @@ -236,7 +241,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa if (rbuf->len[id] != r) { /* e.g. if maxreportsize flag was set */ upsdebugx(2, - "%s: expected %zu bytes, but got %zu instead", + "%s: expected %" PRIuSIZE " bytes, but got %" PRIuSIZE " instead", __func__, rbuf->len[id], r); upsdebug_hex(3, "Report[err]", (usb_ctrl_charbuf)rbuf->data[id], r); @@ -281,7 +286,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 +298,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 @@ -305,8 +313,8 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, - "%s: suggested buffer size %zu exceeds " - "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "%s: suggested buffer size %" PRIuSIZE " exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %" PRIuMAX "; " "item setting will be constrained", __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX @@ -324,7 +332,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 @@ -356,7 +364,7 @@ static int file_report_buffer(reportbuf_t *rbuf, unsigned char *buf, size_t bufl if (rbuf->len[id] != buflen) { upsdebugx(2, - "%s: expected %zu bytes, but got %zu instead", + "%s: expected %" PRIuSIZE " bytes, but got %" PRIuSIZE " instead", __func__, rbuf->len[id], buflen); upsdebug_hex(3, "Report[err]", buf, buflen); } else { @@ -398,13 +406,13 @@ static struct { void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab) { size_t i; -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE NUT_UNUSED_VARIABLE(hd); -#else +#else /* !SHUT_MODE => USB */ /* extract the VendorId for further testing */ int vendorID = hd->VendorID; int productID = hd->ProductID; -#endif +#endif /* SHUT_MODE / USB */ /* Do not go further if we already know nothing will be displayed. * Some reports take a while before they timeout, so if these are @@ -415,7 +423,7 @@ void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab) return; } - upsdebugx(1, "%zu HID objects found", pDesc->nitems); + upsdebugx(1, "%" PRIuSIZE " HID objects found", pDesc->nitems); for (i = 0; i < pDesc->nitems; i++) { @@ -423,11 +431,11 @@ void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab) HIDData_t *pData = &pDesc->item[i]; /* skip reports 254/255 for Eaton / MGE / Dell due to special handling needs */ -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE if ((pData->ReportID == 254) || (pData->ReportID == 255)) { continue; } -#else +#else /* !SHUT_MODE => USB */ if ((vendorID == 0x0463) || (vendorID == 0x047c)) { if ((pData->ReportID == 254) || (pData->ReportID == 255)) { continue; @@ -440,7 +448,7 @@ void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab) continue; } } -#endif +#endif /* SHUT_MODE / USB */ /* Get data value */ if (HIDGetDataValue(udev, pData, &value, MAX_TS) == 1) { @@ -543,7 +551,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 +563,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 @@ -570,7 +581,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ ) { upsdebugx(2, "%s: requested index number is out of range, " - "expected %jd < %i < %ju", + "expected %" PRIdMAX " < %i < %" PRIuMAX, __func__, (intmax_t)USB_CTRL_STRINDEX_MIN, Index, @@ -581,8 +592,8 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ if ((uintmax_t)buflen > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, - "%s: suggested buffer size %zu exceeds " - "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "%s: suggested buffer size %" PRIuSIZE " exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %" PRIuMAX "; " "index string will be constrained", __func__, buflen, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); @@ -602,7 +613,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 @@ -684,8 +695,10 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) HIDData_t *pData; /* 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) ) + r = (interrupt_size > 0 && interrupt_size < sizeof(buf)) + ? interrupt_size : sizeof(buf); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS @@ -697,6 +710,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 @@ -710,8 +726,8 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { /* FIXME: Should we try here, or plain abort? */ upsdebugx(2, - "%s: suggested buffer size %zu exceeds " - "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "%s: suggested buffer size %" PRIuSIZE " exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %" PRIuMAX "; " "report will be constrained", __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); @@ -733,7 +749,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 @@ -806,6 +822,7 @@ static double logical_to_physical(HIDData_t *Data, long logical) if ((Data->PhyMax <= Data->PhyMin) || (Data->LogMax <= Data->LogMin)) { /* this should not really happen */ + upsdebugx(5, "Max was not greater than Min, returning logical value as is"); return (double)logical; } @@ -844,6 +861,7 @@ static long physical_to_logical(HIDData_t *Data, double physical) if ((Data->PhyMax <= Data->PhyMin) || (Data->LogMax <= Data->LogMin)) { /* this should not really happen */ + upsdebugx(5, "Max was not greater than Min, returning physical value as is"); return (long)physical; } @@ -904,10 +922,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 +943,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 %" PRIuMAX " too large", + l, (uintmax_t)l); goto badvalue; } path->Node[i++] = (HIDNode_t)l; @@ -927,6 +957,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 %" PRIuMAX " 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..52feeb2601 100644 --- a/drivers/libhid.h +++ b/drivers/libhid.h @@ -29,26 +29,35 @@ #ifndef NUT_LIBHID_H_SEEN #define NUT_LIBHID_H_SEEN -#include "config.h" +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif #include #include "nut_stdint.h" #include "hidtypes.h" #include "timehead.h" -#ifdef SHUT_MODE + +#if (defined SHUT_MODE) && SHUT_MODE #include "libshut.h" typedef SHUTDevice_t HIDDevice_t; typedef char HIDDeviceMatcher_t; typedef usb_dev_handle hid_dev_handle_t; typedef shut_communication_subdriver_t communication_subdriver_t; -#else + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(ERROR_FD_SER) +#else /* !SHUT_MODE => USB */ #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; -#endif + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(NULL) +#endif /* SHUT_MODE / USB */ /* use explicit booleans */ #ifndef FALSE diff --git a/drivers/libshut.c b/drivers/libshut.c index 4376d588a4..83b0cbe7c4 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -43,7 +43,7 @@ #include "common.h" /* for xmalloc, upsdebugx prototypes */ #define SHUT_DRIVER_NAME "SHUT communication driver" -#define SHUT_DRIVER_VERSION "0.86" +#define SHUT_DRIVER_VERSION "0.89" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -182,6 +182,7 @@ struct my_hid_descriptor { /*! * SHUT functions for HID marshalling */ + /* Expected evaluated types for the API after typedefs: * static int shut_get_descriptor(int upsfd, unsigned char type, * unsigned char index, void *buf, int size); @@ -298,6 +299,7 @@ typedef union device_desc_data_t { #endif /* Low level SHUT (Serial HID UPS Transfer) routines */ + /* Expected evaluated types for the API after typedefs: static void setline(int upsfd, int set); static int shut_synchronise(int upsfd); @@ -334,7 +336,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) @@ -360,6 +362,7 @@ static void align_request(struct shut_ctrltransfer_s *ctrl) * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. */ + /* Expected evaluated types for the API after typedefs: * static int libshut_open(int *arg_upsfd, SHUTDevice_t *curDevice, char *arg_device_path, * int (*callback)(int arg_upsfd, SHUTDevice_t *hd, @@ -391,10 +394,58 @@ static int libshut_open( * version is at index 1 (in which case, bcdDevice == 0x0202) */ usb_ctrl_descindex hid_desc_index = 0; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + static int usb_hid_number_opts_parsed = 0; + if (!usb_hid_number_opts_parsed) { + const char *s; + unsigned short us = 0; + if ((s = getval("usb_hid_desc_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__); + } + hid_desc_index = (usb_ctrl_descindex)us; + } + usb_hid_number_opts_parsed = 1; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + if (!arg_device_path) { + fatalx(EXIT_FAILURE, "%s: arg_device_path=null", __func__); + } + upsdebugx(2, "libshut_open: using port %s", arg_device_path); /* If device is still open, close it */ - if (*arg_upsfd > 0) { + if (VALID_FD_SER(*arg_upsfd)) { ser_close(*arg_upsfd, arg_device_path); } @@ -460,13 +511,27 @@ static int libshut_open( free(curDevice->Product); free(curDevice->Serial); free(curDevice->Bus); + free(curDevice->Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(curDevice->BusPort); +#endif memset(curDevice, '\0', sizeof(*curDevice)); curDevice->VendorID = dev_descriptor->idVendor; curDevice->ProductID = dev_descriptor->idProduct; curDevice->Bus = strdup("serial"); + curDevice->Device = strdup(arg_device_path); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + curDevice->BusPort = (char *)malloc(4); + if (curDevice->BusPort == NULL) { + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + upsdebugx(2, "%s: NOTE: BusPort is always zero with libshut", __func__); + sprintf(curDevice->BusPort, "%03d", 0); +#endif + curDevice->bcdDevice = dev_descriptor->bcdDevice; - curDevice->Vendor = strdup("Eaton"); + curDevice->Vendor = NULL; if (dev_descriptor->iManufacturer) { ret = shut_get_string_simple(*arg_upsfd, dev_descriptor->iManufacturer, string, MAX_STRING_SIZE); @@ -474,6 +539,9 @@ static int libshut_open( curDevice->Vendor = strdup(string); } } + if (curDevice->Vendor == NULL) { + curDevice->Vendor = strdup("Eaton"); + } /* ensure iProduct retrieval */ if (dev_descriptor->iProduct) { @@ -505,11 +573,16 @@ static int libshut_open( upsdebugx(2, "- Product: %s", curDevice->Product); upsdebugx(2, "- Serial Number: %s", curDevice->Serial); upsdebugx(2, "- Bus: %s", curDevice->Bus); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown"); +#endif + upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); upsdebugx(2, "Device matches"); if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { - upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); + upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); + if (!getval("usb_hid_desc_index")) hid_desc_index = 1; } @@ -546,8 +619,7 @@ static int libshut_open( } /* USB_LE16_TO_CPU(desc->wDescriptorLength); */ - desc->wDescriptorLength = (uint16_t)(buf[7]); - desc->wDescriptorLength |= (((uint16_t)buf[8]) << 8); + desc->wDescriptorLength = (0x00FF & (uint8_t)buf[7]) | ((0x00FF & (uint8_t)buf[8]) << 8); upsdebugx(2, "HID descriptor retrieved (Reportlen = %u)", desc->wDescriptorLength); /* @@ -583,7 +655,7 @@ static int libshut_open( #endif upsdebugx(2, "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE - " (max %zu)", + " (max %" PRIuSIZE ")", rdlen, sizeof(rdbuf)); return -1; } @@ -657,7 +729,7 @@ static int libshut_open( */ static void libshut_close(usb_dev_handle arg_upsfd) { - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return; } @@ -677,7 +749,7 @@ static int libshut_get_report( usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize) { - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return 0; } @@ -704,7 +776,7 @@ static int libshut_set_report( { int ret; - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return 0; } @@ -742,7 +814,7 @@ static int libshut_get_string( { int ret; - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return -1; } @@ -767,7 +839,7 @@ static int libshut_get_interrupt( { int ret; - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return -1; } @@ -807,7 +879,7 @@ shut_communication_subdriver_t shut_subdriver = { */ void setline(usb_dev_handle arg_upsfd, int set) { - if (arg_upsfd < 1) { + if (INVALID_FD_SER(arg_upsfd)) { return; } @@ -893,7 +965,7 @@ static unsigned char shut_checksum( unsigned char chk=0; for(i=0; i + * Network UPS Tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,9 +32,12 @@ #include "common.h" /* for xmalloc, upsdebugx prototypes */ #include "usb-common.h" #include "nut_libusb.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #define USB_DRIVER_NAME "USB communication driver (libusb 0.1)" -#define USB_DRIVER_VERSION "0.42" +#define USB_DRIVER_VERSION "0.47" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -46,6 +49,15 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 + +#if (!HAVE_STRCASESTR) && (HAVE_STRSTR && HAVE_STRLWR && HAVE_STRDUP) +/* Only used in this file of all NUT codebase, so not in str.{c,h} + * where it happens to conflict with netsnmp-provided variant for + * some of our build products. + */ +static char *strcasestr(const char *haystack, const char *needle); +#endif static void libusb_close(usb_dev_handle *udev); @@ -64,9 +76,33 @@ void nut_usb_addvars(void) addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); addvar(VAR_VALUE, "device", "Regular expression to match USB device name"); + /* Not supported by libusb0, but let's not crash config + * parsing on unknown keywords due to such nuances! :) */ + addvar(VAR_VALUE, "busport", "Regular expression to match USB bus port name" + " (tolerated but ignored in this build)" + ); + + /* Warning: this feature is inherently non-deterministic! + * If you only care to know that at least one of your no-name UPSes is online, + * this option can help. If you must really know which one, it will not! + */ + addvar(VAR_FLAG, "allow_duplicates", + "If you have several UPS devices which may not be uniquely " + "identified by options above, allow each driver instance with this " + "option to take the first match if available, or try another " + "(association of driver to device may vary between runs)"); + addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); + addvar(VAR_VALUE, "usb_config_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_rep_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_desc_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_ep_in", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_ep_out", "Deeper tuning of USB communications for complex devices"); + dstate_setinfo("driver.version.usb", "libusb-0.1 (or compat)"); + + upsdebugx(1, "Using USB implementation: %s", dstate_getinfo("driver.version.usb")); } /* From usbutils: workaround libusb (0.1) API goofs: @@ -166,9 +202,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { -#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; -#endif usb_ctrl_charbufsize rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; struct usb_device *dev; @@ -181,16 +215,101 @@ static int libusb_open(usb_dev_handle **udevp, usb_ctrl_char *p; char string[256]; int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; + int count_open_attempts = 0; /* report descriptor */ usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; usb_ctrl_charbufsize rdlen; + struct usb_bus *busses; + + static int usb_hid_number_opts_parsed = 0; + if (!usb_hid_number_opts_parsed) { + const char *s; + unsigned short us = 0; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((s = getval("usb_config_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_CFGINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_config_index", __func__); + } + usb_subdriver.usb_config_index = (usb_ctrl_cfgindex)us; + } + if ((s = getval("usb_hid_rep_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_REPINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_rep_index", __func__); + } + usb_subdriver.hid_rep_index = (usb_ctrl_repindex)us; + } + if ((s = getval("usb_hid_desc_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__); + } + usb_subdriver.hid_desc_index = (usb_ctrl_descindex)us; + } + if ((s = getval("usb_hid_ep_in"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_in", __func__); + } + usb_subdriver.hid_ep_in = (usb_ctrl_endpoint)us; + } + if ((s = getval("usb_hid_ep_out"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_out", __func__); + } + usb_subdriver.hid_ep_out = (usb_ctrl_endpoint)us; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + usb_hid_number_opts_parsed = 1; + } + /* libusb base init */ usb_init(); usb_find_busses(); usb_find_devices(); +#ifdef WIN32 + busses = usb_get_busses(); +#else + /* libusb built-in; not sure why original NUT for WIN32 + * code differed or if it is actually better? Or why + * this was not tackled in a few other files for USB?.. + */ + busses = usb_busses; +#endif + #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ libusb_close(*udevp); @@ -198,10 +317,12 @@ static int libusb_open(usb_dev_handle **udevp, upsdebugx(3, "usb_busses=%p", (void*)usb_busses); - for (bus = usb_busses; bus; bus = bus->next) { + for (bus = busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { /* int if_claimed = 0; */ + count_open_attempts++; + upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); @@ -212,10 +333,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; } @@ -230,35 +371,68 @@ static int libusb_open(usb_dev_handle **udevp, free(curDevice->Serial); free(curDevice->Bus); free(curDevice->Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(curDevice->BusPort); +#endif memset(curDevice, '\0', sizeof(*curDevice)); + /* Keep the list of items in sync with those matched by + * drivers/libusb1.c and tools/nut-scanner/scan_usb.c: + */ curDevice->VendorID = dev->descriptor.idVendor; curDevice->ProductID = dev->descriptor.idProduct; curDevice->Bus = xstrdup(bus->dirname); curDevice->Device = xstrdup(dev->filename); curDevice->bcdDevice = dev->descriptor.bcdDevice; +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + curDevice->BusPort = (char *)malloc(4); + if (curDevice->BusPort == NULL) { + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + upsdebugx(2, "%s: NOTE: BusPort is always zero with libusb0", __func__); + sprintf(curDevice->BusPort, "%03d", 0); +#endif + 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__); } } @@ -269,11 +443,15 @@ static int libusb_open(usb_dev_handle **udevp, upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown"); +#endif upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); /* FIXME: extend to Eaton OEMs (HP, IBM, ...) */ if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { - usb_subdriver.hid_desc_index = 1; + if (!getval("usb_hid_desc_index")) + usb_subdriver.hid_desc_index = 1; } upsdebugx(2, "Trying to match device"); @@ -299,6 +477,9 @@ static int libusb_open(usb_dev_handle **udevp, goto next_device; } } + + /* If we got here, none of the matchers said + * that the device is not what we want. */ upsdebugx(2, "Device matches"); /* Now we have matched the device we wanted. Claim it. */ @@ -307,12 +488,20 @@ 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; - while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { + retries = MAX_RETRY; +#ifdef WIN32 + usb_set_configuration(udev, 1); +#endif + while ((ret = usb_claim_interface(udev, usb_subdriver.hid_rep_index)) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); + if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) { + upsdebugx(2, "Configured to allow_duplicates so looking for another similar device"); + goto next_device; + } + if (usb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index) < 0) { upsdebugx(2, "failed to detach kernel driver from USB device: %s", usb_strerror()); @@ -325,17 +514,24 @@ static int libusb_open(usb_dev_handle **udevp, } fatalx(EXIT_FAILURE, - "Can't claim USB device [%04x:%04x]@%d/%d: %s", + "Can't claim USB device [%04x:%04x]@%d/%d/%d: %s", curDevice->VendorID, curDevice->ProductID, + usb_subdriver.usb_config_index, usb_subdriver.hid_rep_index, usb_subdriver.hid_desc_index, usb_strerror()); } #else - if (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { + if ((ret = usb_claim_interface(udev, usb_subdriver.hid_rep_index)) < 0) { + if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) { + upsdebugx(2, "Configured to allow_duplicates so looking for another similar device"); + goto next_device; + } + fatalx(EXIT_FAILURE, - "Can't claim USB device [%04x:%04x]@%d/%d: %s", + "Can't claim USB device [%04x:%04x]@%d/%d/%d: %s", curDevice->VendorID, curDevice->ProductID, + usb_subdriver.usb_config_index, usb_subdriver.hid_rep_index, usb_subdriver.hid_desc_index, usb_strerror()); @@ -372,12 +568,12 @@ static int libusb_open(usb_dev_handle **udevp, upsdebugx(2, "Unable to get HID descriptor (%s)", usb_strerror()); } else if (res < 9) { - upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); + upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 9, res); } else { - + upsdebugx(2, "Retrieved HID descriptor (expected %d, got %d)", 9, res); 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) { @@ -393,10 +589,7 @@ static int libusb_open(usb_dev_handle **udevp, /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), only this second method gives the correct result */ - - /* for now, we always assume configuration 0, interface 0, - altsetting 0, as above. */ - iface = &dev->config[0].interface[usb_subdriver.hid_rep_index].altsetting[0]; + iface = &dev->config[usb_subdriver.usb_config_index].interface[usb_subdriver.hid_rep_index].altsetting[0]; for (i=0; iextralen; i+=iface->extra[i]) { upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, iface->extra[i], iface->extra[i+1]); @@ -407,7 +600,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; } } @@ -449,7 +642,7 @@ static int libusb_open(usb_dev_handle **udevp, if ((uintmax_t)rdlen > sizeof(rdbuf)) { upsdebugx(2, "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE - " (max %zu)", + " (max %" PRIuSIZE ")", rdlen, sizeof(rdbuf)); goto next_device; } @@ -462,7 +655,7 @@ static int libusb_open(usb_dev_handle **udevp, USB_REQ_GET_DESCRIPTOR, (USB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, usb_subdriver.hid_rep_index, - rdbuf, (unsigned)rdlen, USB_TIMEOUT); + rdbuf, rdlen, USB_TIMEOUT); if (res < 0) { @@ -472,9 +665,23 @@ static int libusb_open(usb_dev_handle **udevp, if (res < rdlen) { +#ifndef WIN32 upsdebugx(2, "Warning: report descriptor too short " "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE ", got %d)", rdlen, res); +#else + /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ + upsdebugx(0, "Warning: report descriptor too short " + "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE + ", got %d)", rdlen, res); + upsdebugx(0, "Please check your Windows Device Manager: " + "perhaps the UPS was recognized by default OS\n" + "driver such as HID UPS Battery (hidbatt.sys, " + "hidusb.sys or similar). It could have been\n" + "\"restored\" by Windows Update. You can try " + "https://zadig.akeo.ie/ to handle it with\n" + "either WinUSB, libusb0.sys or libusbK.sys."); +#endif /* WIN32 */ rdlen = res; /* correct rdlen if necessary */ } @@ -488,6 +695,18 @@ static int libusb_open(usb_dev_handle **udevp, "Report descriptor retrieved (Reportlen = %" PRI_NUT_USB_CTRL_CHARBUFSIZE ")", rdlen); upsdebugx(2, "Found HID device"); + + upsdebugx(3, "Using default, detected or customized USB HID numbers: " + "usb_config_index=%d usb_hid_rep_index=%d " + "usb_hid_desc_index=%d " + "usb_hid_ep_in=%d usb_hid_ep_out=%d", + usb_subdriver.usb_config_index, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + usb_subdriver.hid_ep_in, + usb_subdriver.hid_ep_out + ); + fflush(stdout); return rdlen; @@ -506,6 +725,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; } @@ -537,12 +770,16 @@ static int libusb_strerror(const int ret, const char *desc) upsdebugx(2, "%s: Connection timed out", desc); return 0; +/* libusb-win32 does not know EPROTO and EOVERFLOW, + * it only returns EIO for any IO errors */ +#ifndef WIN32 case -EOVERFLOW: /* Value too large for defined data type */ -#ifdef EPROTO +# ifdef EPROTO case -EPROTO: /* Protocol error */ -#endif +# endif upsdebugx(2, "%s: %s", desc, usb_strerror()); return 0; +#endif /* WIN32 */ default: /* Undetermined, log only */ upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); @@ -579,6 +816,10 @@ static int libusb_get_report( usb_subdriver.hid_rep_index, raw_buf, ReportSize, USB_TIMEOUT); +#ifdef WIN32 + errno = -ret; +#endif + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; @@ -610,6 +851,10 @@ static int libusb_set_report( usb_subdriver.hid_rep_index, raw_buf, ReportSize, USB_TIMEOUT); +#ifdef WIN32 + errno = -ret; +#endif + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { return 0; @@ -658,6 +903,10 @@ static int libusb_get_string( ret = usb_get_string_simple(udev, StringIdx, buf, (size_t)buflen); +#ifdef WIN32 + errno = -ret; +#endif + return libusb_strerror(ret, __func__); } @@ -680,6 +929,10 @@ static int libusb_get_interrupt( /* Interrupt EP is USB_ENDPOINT_IN with offset defined in hid_ep_in, which is 0 by default, unless overridden in subdriver. */ ret = usb_interrupt_read(udev, USB_ENDPOINT_IN + usb_subdriver.hid_ep_in, (char *)buf, bufsize, timeout); +#ifdef WIN32 + errno = -ret; +#endif + /* Clear stall condition */ if (ret == -EPIPE) { ret = usb_clear_halt(udev, 0x81); @@ -701,6 +954,37 @@ static void libusb_close(usb_dev_handle *udev) usb_close(udev); } +#if (!HAVE_STRCASESTR) && (HAVE_STRSTR && HAVE_STRLWR && HAVE_STRDUP) +static char *strcasestr(const char *haystack, const char *needle) { + /* work around "const char *" and guarantee the original is not + * touched... not efficient but we have few uses for this method */ + char * dH = NULL, *dN = NULL, *lH = NULL, *lN = NULL, *first = NULL; + + dH = strdup(haystack); + if (dH == NULL) goto err; + dN = strdup(needle); + if (dN == NULL) goto err; + lH = strlwr(dH); + if (lH == NULL) goto err; + lN = strlwr(dN); + if (lN == NULL) goto err; + first = strstr(lH, lN); + +err: + if (dH != NULL) free(dH); + if (dN != NULL) free(dN); + /* Does this implementation of strlwr() change original buffer? */ + if (lH != dH && lH != NULL) free(lH); + if (lN != dN && lN != NULL) free(lN); + if (first == NULL) { + return NULL; + } + + /* Pointer to first char of the needle found in original haystack */ + return (char *)(haystack + (first - lH)); +} +#endif + usb_communication_subdriver_t usb_subdriver = { USB_DRIVER_NAME, USB_DRIVER_VERSION, @@ -710,6 +994,7 @@ usb_communication_subdriver_t usb_subdriver = { libusb_set_report, libusb_get_string, libusb_get_interrupt, + LIBUSB_DEFAULT_CONF_INDEX, LIBUSB_DEFAULT_INTERFACE, LIBUSB_DEFAULT_DESC_INDEX, LIBUSB_DEFAULT_HID_EP_IN, diff --git a/drivers/libusb1.c b/drivers/libusb1.c index dd07435c33..5bb012eaff 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -8,7 +8,7 @@ * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for - * Network UPS Tools + * Network UPS Tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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.47" /* 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); @@ -65,13 +66,41 @@ void nut_usb_addvars(void) addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); addvar(VAR_VALUE, "device", "Regular expression to match USB device name"); + addvar(VAR_VALUE, "busport", "Regular expression to match USB bus port name" +#if (!defined WITH_USB_BUSPORT) || (!WITH_USB_BUSPORT) + /* Not supported by this version of libusb1, + * but let's not crash config parsing on + * unknown keywords due to such nuances! :) + */ + " (tolerated but ignored in this build)" +#endif + ); + + /* Warning: this feature is inherently non-deterministic! + * If you only care to know that at least one of your no-name UPSes is online, + * this option can help. If you must really know which one, it will not! + */ + addvar(VAR_FLAG, "allow_duplicates", + "If you have several UPS devices which may not be uniquely " + "identified by options above, allow each driver instance with this " + "option to take the first match if available, or try another " + "(association of driver to device may vary between runs)"); + addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); + addvar(VAR_VALUE, "usb_config_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_rep_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_desc_index", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_ep_in", "Deeper tuning of USB communications for complex devices"); + addvar(VAR_VALUE, "usb_hid_ep_out", "Deeper tuning of USB communications for complex devices"); + #ifdef LIBUSB_API_VERSION dstate_setinfo("driver.version.usb", "libusb-%u.%u.%u (API: 0x%x)", v->major, v->minor, v->micro, LIBUSB_API_VERSION); #else /* no LIBUSB_API_VERSION */ dstate_setinfo("driver.version.usb", "libusb-%u.%u.%u", v->major, v->minor, v->micro); #endif /* LIBUSB_API_VERSION */ + + upsdebugx(1, "Using USB implementation: %s", dstate_getinfo("driver.version.usb")); } /* invoke matcher against device */ @@ -138,10 +167,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; @@ -150,16 +179,91 @@ static int nut_libusb_open(libusb_device_handle **udevp, struct libusb_config_descriptor *conf_desc = NULL; const struct libusb_interface_descriptor *if_desc; libusb_device_handle *udev; - uint8_t bus; + uint8_t bus_num, device_addr; +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + uint8_t bus_port; +#endif int ret, res; unsigned char buf[20]; const unsigned char *p; char string[256]; int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; - int rdlen; + int32_t rdlen; + + static int usb_hid_number_opts_parsed = 0; + if (!usb_hid_number_opts_parsed) { + const char *s; + unsigned short us = 0; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((s = getval("usb_config_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_CFGINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_config_index", __func__); + } + usb_subdriver.usb_config_index = (usb_ctrl_cfgindex)us; + } + if ((s = getval("usb_hid_rep_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_REPINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_rep_index", __func__); + } + usb_subdriver.hid_rep_index = (usb_ctrl_repindex)us; + } + if ((s = getval("usb_hid_desc_index"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_DESCINDEX_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_desc_index", __func__); + } + usb_subdriver.hid_desc_index = (usb_ctrl_descindex)us; + } + if ((s = getval("usb_hid_ep_in"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_in", __func__); + } + usb_subdriver.hid_ep_in = (usb_ctrl_endpoint)us; + } + if ((s = getval("usb_hid_ep_out"))) { + if (!str_to_ushort(s, &us, 16) || (us > USB_CTRL_ENDPOINT_MAX)) { + fatalx(EXIT_FAILURE, "%s: could not parse usb_hid_ep_out", __func__); + } + usb_subdriver.hid_ep_out = (usb_ctrl_endpoint)us; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + usb_hid_number_opts_parsed = 1; + } /* libusb base init */ if (libusb_init(NULL) < 0) { @@ -167,6 +271,14 @@ static int nut_libusb_open(libusb_device_handle **udevp, fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); } +/* TODO: Find a place for this, from Windows branch made for libusb0.c */ +/* +#ifdef WIN32 + struct usb_bus *busses; + busses = usb_get_busses(); +#endif + */ + #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ /* nut_libusb_close(*udevp); */ @@ -183,7 +295,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, libusb_device *device = devlist[devnum]; libusb_get_device_descriptor(device, &dev_desc); - upsdebugx(2, "Checking device %zu of %zu (%04X/%04X)", + upsdebugx(2, "Checking device %" PRIuSIZE " of %" PRIuSIZE " (%04X/%04X)", devnum + 1, devcount, dev_desc.idVendor, dev_desc.idProduct); @@ -196,6 +308,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; @@ -211,52 +327,119 @@ static int nut_libusb_open(libusb_device_handle **udevp, free(curDevice->Serial); free(curDevice->Bus); free(curDevice->Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(curDevice->BusPort); +#endif memset(curDevice, '\0', sizeof(*curDevice)); - bus = libusb_get_bus_number(device); + /* Keep the list of items in sync with those matched by + * drivers/libusb0.c and tools/nut-scanner/scan_usb.c: + */ + bus_num = libusb_get_bus_number(device); curDevice->Bus = (char *)malloc(4); if (curDevice->Bus == NULL) { libusb_free_device_list(devlist, 1); fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - sprintf(curDevice->Bus, "%03d", bus); + sprintf(curDevice->Bus, "%03d", bus_num); + + device_addr = libusb_get_device_address(device); + curDevice->Device = (char *)malloc(4); + if (curDevice->Device == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + if (device_addr > 0) { + /* 0 means not available, e.g. lack of platform support */ + sprintf(curDevice->Device, "%03d", device_addr); + } else { + if (devnum <= 999) { + /* Log visibly so users know their number discovered + * from `lsusb` or `dmesg` (if any) was ignored */ + upsdebugx(0, "%s: invalid libusb device address %" PRIu8 ", " + "falling back to enumeration order counter %" PRIuSIZE, + __func__, device_addr, devnum); + sprintf(curDevice->Device, "%03d", (int)devnum); + } else { + upsdebugx(1, "%s: invalid libusb device address %" PRIu8, + __func__, device_addr); + free(curDevice->Device); + curDevice->Device = NULL; + } + } + +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + bus_port = libusb_get_port_number(device); + curDevice->BusPort = (char *)malloc(4); + if (curDevice->BusPort == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + if (bus_port > 0) { + sprintf(curDevice->BusPort, "%03d", bus_port); + } else { + upsdebugx(1, "%s: invalid libusb bus number %i", + __func__, bus_port); + free(curDevice->BusPort); + curDevice->BusPort = NULL; + } +#endif + curDevice->VendorID = dev_desc.idVendor; curDevice->ProductID = dev_desc.idProduct; 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__); } } @@ -266,11 +449,15 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + upsdebugx(2, "- Bus Port: %s", curDevice->BusPort ? curDevice->BusPort : "unknown"); +#endif upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); /* FIXME: extend to Eaton OEMs (HP, IBM, ...) */ if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + if (!getval("usb_hid_desc_index")) usb_subdriver.hid_desc_index = 1; } @@ -298,12 +485,15 @@ static int nut_libusb_open(libusb_device_handle **udevp, goto next_device; } } - upsdebugx(2, "Device matches"); + /* If we got here, none of the matchers said + * that the device is not what we want. */ + upsdebugx(2, "Device matches"); - upsdebugx(2, "Reading first configuration descriptor"); + upsdebugx(2, "Reading configuration descriptor %d of %d", + usb_subdriver.usb_config_index+1, dev_desc.bNumConfigurations); ret = libusb_get_config_descriptor(device, - (uint8_t)usb_subdriver.hid_rep_index, + (uint8_t)usb_subdriver.usb_config_index, &conf_desc); /*ret = libusb_get_active_config_descriptor(device, &conf_desc);*/ if (ret < 0) @@ -334,28 +524,41 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "successfully set kernel driver auto-detach flag"); } } else { - upsdebugx(3, "libusb_kernel_driver_active() returned %d", ret); + upsdebugx(3, "libusb_kernel_driver_active() returned %d: %s", + ret, libusb_strerror((enum libusb_error)ret)); } #endif #if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) /* Then, try the explicit detach method. * This function is available on FreeBSD 10.1-10.3 */ - retries = 3; + retries = MAX_RETRY; +#ifdef WIN32 + /* TODO: Align with libusb1 - initially from Windows branch made against libusb0 */ + libusb_set_configuration(udev, 1); +#endif + 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)); + if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) { + upsdebugx(2, "Configured to allow_duplicates so looking for another similar device"); + goto next_device; + } + # ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER - if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { + if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { # else /* if defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) */ - if ((ret = libusb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { + if ((ret = libusb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { # endif - if (ret == LIBUSB_ERROR_NOT_FOUND) + if (ret == LIBUSB_ERROR_NOT_FOUND) { + /* logged as "Entity not found" if this persists */ upsdebugx(2, "Kernel driver already detached"); - else + } else { upsdebugx(1, "failed to detach kernel driver from USB device: %s", libusb_strerror((enum libusb_error)ret)); + } } else { upsdebugx(2, "detached kernel driver from USB device..."); } @@ -367,19 +570,26 @@ static int nut_libusb_open(libusb_device_handle **udevp, libusb_free_config_descriptor(conf_desc); libusb_free_device_list(devlist, 1); fatalx(EXIT_FAILURE, - "Can't claim USB device [%04x:%04x]@%d/%d: %s", + "Can't claim USB device [%04x:%04x]@%d/%d/%d: %s", curDevice->VendorID, curDevice->ProductID, + usb_subdriver.usb_config_index, usb_subdriver.hid_rep_index, usb_subdriver.hid_desc_index, libusb_strerror((enum libusb_error)ret)); } #else if ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS ) { + if (ret == LIBUSB_ERROR_BUSY && testvar("allow_duplicates")) { + upsdebugx(2, "Configured to allow_duplicates so looking for another similar device"); + goto next_device; + } + libusb_free_config_descriptor(conf_desc); libusb_free_device_list(devlist, 1); fatalx(EXIT_FAILURE, - "Can't claim USB device [%04x:%04x]@%d/%d: %s", + "Can't claim USB device [%04x:%04x]@%d/%d/%d: %s", curDevice->VendorID, curDevice->ProductID, + usb_subdriver.usb_config_index, usb_subdriver.hid_rep_index, usb_subdriver.hid_desc_index, libusb_strerror((enum libusb_error)ret)); @@ -398,7 +608,10 @@ static int nut_libusb_open(libusb_device_handle **udevp, } if (!conf_desc) { /* ?? this should never happen */ - upsdebugx(2, " Couldn't retrieve descriptors"); + upsdebugx(2, " Couldn't retrieve config descriptor [%04x:%04x]@%d", + curDevice->VendorID, curDevice->ProductID, + usb_subdriver.usb_config_index + ); goto next_device; } @@ -420,12 +633,12 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "Unable to get HID descriptor (%s)", libusb_strerror((enum libusb_error)res)); } else if (res < 9) { - upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); + upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 9, res); } else { - + upsdebugx(2, "Retrieved HID descriptor (expected %d, got %d)", 9, res); 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 +663,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; } } @@ -529,8 +742,21 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (res < rdlen) { +#ifndef WIN32 upsdebugx(2, "Warning: report descriptor too short " "(expected %d, got %d)", rdlen, res); +#else + /* https://github.com/networkupstools/nut/issues/1690#issuecomment-1455206002 */ + upsdebugx(0, "Warning: report descriptor too short " + "(expected %d, got %d)", rdlen, res); + upsdebugx(0, "Please check your Windows Device Manager: " + "perhaps the UPS was recognized by default OS\n" + "driver such as HID UPS Battery (hidbatt.sys, " + "hidusb.sys or similar). It could have been\n" + "\"restored\" by Windows Update. You can try " + "https://zadig.akeo.ie/ to handle it with\n" + "either WinUSB, libusb0.sys or libusbK.sys."); +#endif /* WIN32 */ rdlen = res; /* correct rdlen if necessary */ } @@ -539,7 +765,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, ) { upsdebugx(2, "Report descriptor length is out of range on this device: " - "should be %ji < %d < %ju", + "should be %" PRIdMAX " < %d < %" PRIuMAX, (intmax_t)USB_CTRL_CHARBUFSIZE_MIN, rdlen, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); goto next_device; @@ -553,6 +779,18 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); upsdebugx(2, "Found HID device"); + + upsdebugx(3, "Using default, detected or customized USB HID numbers: " + "usb_config_index=%d usb_hid_rep_index=%d " + "usb_hid_desc_index=%d " + "usb_hid_ep_in=%d usb_hid_ep_out=%d", + usb_subdriver.usb_config_index, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + usb_subdriver.hid_ep_in, + usb_subdriver.hid_ep_out + ); + fflush(stdout); libusb_free_device_list(devlist, 1); @@ -571,6 +809,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; } @@ -610,13 +862,15 @@ static int nut_libusb_strerror(const int ret, const char *desc) upsdebugx(2, "%s: Connection timed out", desc); return 0; +#ifndef WIN32 case LIBUSB_ERROR_OVERFLOW: /** Overflow */ -#ifdef EPROTO +# ifdef EPROTO /* FIXME: not sure how to map this one! */ case -EPROTO: /* Protocol error */ -#endif +# endif upsdebugx(2, "%s: %s", desc, libusb_strerror((enum libusb_error)ret)); return 0; +#endif /* WIN32 */ case LIBUSB_ERROR_OTHER: /** Other error */ default: /** Undetermined, log only */ @@ -861,6 +1115,7 @@ usb_communication_subdriver_t usb_subdriver = { nut_libusb_set_report, nut_libusb_get_string, nut_libusb_get_interrupt, + LIBUSB_DEFAULT_CONF_INDEX, LIBUSB_DEFAULT_INTERFACE, LIBUSB_DEFAULT_DESC_INDEX, LIBUSB_DEFAULT_HID_EP_IN, diff --git a/drivers/liebert-esp2.c b/drivers/liebert-esp2.c index f8e8e8567c..ef27cc871a 100644 --- a/drivers/liebert-esp2.c +++ b/drivers/liebert-esp2.c @@ -28,7 +28,8 @@ #define IsBitSet(val, bit) ((val) & (1 << (bit))) #define DRIVER_NAME "Liebert ESP-II serial UPS driver" -#define DRIVER_VERSION "0.05" +#define DRIVER_VERSION "0.06" + #define UPS_SHUTDOWN_DELAY 12 /* it means UPS will be shutdown 120 sec */ #define SHUTDOWN_CMD_LEN 8 @@ -201,7 +202,7 @@ void upsdrv_initinfo(void) } buf[i<<1] = 0; - upsdebugx(1, "return: %zd (8=success)", ret); + upsdebugx(1, "return: %" PRIiSIZE " (8=success)", ret); if (ret == 8) { /* last command successful */ dstate_setinfo(vartab[vari].var,"%s",buf); diff --git a/drivers/liebert-hid.c b/drivers/liebert-hid.c index 1f05ebdd48..7b87706fc5 100644 --- a/drivers/liebert-hid.c +++ b/drivers/liebert-hid.c @@ -4,6 +4,8 @@ * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger * 2007 Charles Lepple + * 2018 Markus "Links2004" + * 2023 Blaž ZakrajÅ”ek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +28,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 */ @@ -67,37 +69,76 @@ static usage_tables_t liebert_utab[] = { static hid_info_t liebert_hid2nut[] = { -#if 0 +#if WITH_UNMAPPED_DATA_POINTS { "unmapped.ups.powersummary.flowid", 0, 0, "UPS.PowerSummary.FlowID", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, - { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.product", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", 0, stringid_conversion }, { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, -#endif + { "unmapped.ups.powersummary.manufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", 0, stringid_conversion }, + { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.serialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ + /* Battery page */ { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, - { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", 0, NULL }, { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "experimental.battery.capacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, + { "experimental.battery.capacity.nominal", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, - { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + /* UPS page */ + { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.power.nominal", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, + + { "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", HU_FLAG_SEMI_STATIC, beeper_info }, + + /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, + { "output.voltage.nominal", 0, 0, "UPS.Flow.[4].ConfigVoltage", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.2f", 0, NULL }, + { "output.frequency.nominal", 0, 0, "UPS.Flow.[4].ConfigFrequency", NULL, "%.0f", HU_FLAG_SEMI_STATIC, 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 page */ { "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}, + + /* Status page */ { "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 }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, "%.0f", HU_FLAG_QUICK_POLL, discharging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, "%.0f", 0, overload_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, "%.0f", 0, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, "%.0f", HU_FLAG_QUICK_POLL, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, HU_FLAG_QUICK_POLL, off_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, "%.0f", HU_FLAG_QUICK_POLL, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Buck", NULL, NULL, 0, trim_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Boost", NULL, NULL, 0, boost_info }, + + /* Variables */ + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, + + /* Instant commands */ + { "test.battery.start", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, + { "beeper.toggle", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } diff --git a/drivers/liebert.c b/drivers/liebert.c index 65bb539700..1c21e6e0da 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -27,7 +27,7 @@ #include "attribute.h" #define DRIVER_NAME "Liebert MultiLink UPS driver" -#define DRIVER_VERSION "1.03" +#define DRIVER_VERSION "1.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -41,16 +41,14 @@ upsdrv_info_t upsdrv_info = { #define ML_ONBATTERY 0x55 -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* XXX: replace with a proper shutdown function (raise DTR) */ /* worse yet: stock cables don't support shutdown at all */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } void upsdrv_initinfo(void) diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c index 94b8b01e3f..1740320c29 100644 --- a/drivers/macosx-ups.c +++ b/drivers/macosx-ups.c @@ -29,7 +29,7 @@ #include "IOKit/ps/IOPSKeys.h" #define DRIVER_NAME "Mac OS X UPS meta-driver" -#define DRIVER_VERSION "1.3" +#define DRIVER_VERSION "1.40" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -109,7 +109,7 @@ void upsdrv_initinfo(void) { /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ - char device_name[80] = ""; + char device_name_buf[80] = ""; CFStringRef device_type_cfstr, device_name_cfstr; CFPropertyListRef power_dictionary; CFNumberRef max_capacity; @@ -136,13 +136,13 @@ void upsdrv_initinfo(void) CFRetain(device_name_cfstr); - CFStringGetCString(device_name_cfstr, device_name, sizeof(device_name), kCFStringEncodingUTF8); - upsdebugx(2, "Got name: %s", device_name); + CFStringGetCString(device_name_cfstr, device_name_buf, sizeof(device_name_buf), kCFStringEncodingUTF8); + upsdebugx(2, "Got name: %s", device_name_buf); CFRelease(device_name_cfstr); - dstate_setinfo("device.model", "%s", device_name); - dstate_setinfo("ups.model", "%s", device_name); + dstate_setinfo("device.model", "%s", device_name_buf); + dstate_setinfo("ups.model", "%s", device_name_buf); max_capacity = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSMaxCapacityKey)); if(max_capacity) { @@ -152,7 +152,7 @@ void upsdrv_initinfo(void) CFRelease(max_capacity); upsdebugx(3, "Max Capacity = %.f units (usually 100)", max_capacity_value); - if(max_capacity_value != 100) { + if(max_capacity_value != 100.0) { upsdebugx(1, "Max Capacity: %f != 100", max_capacity_value); } } @@ -168,7 +168,8 @@ void upsdrv_updateinfo(void) CFNumberRef battery_voltage, battery_runtime; CFNumberRef current_capacity; CFBooleanRef is_charging; - double max_capacity_value = 100.0, current_capacity_value; + /* double max_capacity_value = 100.0; */ + double current_capacity_value; upsdebugx(1, "upsdrv_updateinfo()"); @@ -198,7 +199,7 @@ void upsdrv_updateinfo(void) /* Retrieve CHRG state */ is_charging = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSIsChargingKey)); - if(is_charging) { + if(is_charging) { Boolean is_charging_value; is_charging_value = CFBooleanGetValue(is_charging); @@ -251,7 +252,7 @@ void upsdrv_updateinfo(void) /* TODO: it should be possible to set poll_interval (and maxage in the * server) to an absurdly large value, and use notify(3) to get * updates. - */ + */ /* * poll_interval = 2; @@ -261,9 +262,6 @@ void upsdrv_updateinfo(void) CFRelease(power_dictionary); } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -272,9 +270,10 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* NOTE: Mac OS X already has shutdown routines - this driver is more - for monitoring and notification purposes. Still, there is a key that - might be useful to set in SystemConfiguration land. */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + for monitoring and notification purposes. Still, there is a key that + might be useful to set in SystemConfiguration land. */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ @@ -347,11 +346,11 @@ void upsdrv_initups(void) CFIndex num_keys, index; CFDictionaryRef power_dictionary; CFTypeRef power_blob; - CFStringRef potential_key, potential_model; + CFStringRef potential_key, potential_model = NULL; char *model_name; /* regex(3) */ char potential_model_name[256]; - regex_t model_regex; - int ret; + regex_t model_regex; + int ret = 0; upsdebugx(3, "upsdrv_initups(): Power Sources blob:"); /* upsfd = ser_open(device_path); */ @@ -363,15 +362,18 @@ void upsdrv_initups(void) if(nut_debug_level >= 3) CFShow(power_blob); -/* The CFDictionary through 10.9 has changed to a CFArray, so this part is no longer applicable: */ +/* The CFDictionary through 10.9 has changed to a CFArray, + * so this part is no longer applicable: */ #if 0 + /* Note: original value of device_name is derived from + * device_path (value of port parameter) in main.c */ if(!strcmp(device_name, "auto")) { device_name = "/UPS"; } upsdebugx(2, "Matching power supply key names against regex '%s'", device_name); - ret = regcomp(&name_regex, device_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); + ret = regcomp(&name_regex, device_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); if(ret) { fatalx(EXIT_FAILURE, diff --git a/drivers/main.c b/drivers/main.c index a3f7026449..cc33fd5f1f 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 @@ -25,36 +26,113 @@ #include "nut_stdint.h" #include "dstate.h" #include "attribute.h" +#include "upsdrvquery.h" + +#ifndef WIN32 +# include +#endif +#include +#include +#include /* data which may be useful to the drivers */ -int upsfd = -1; +TYPE_FD upsfd = ERROR_FD; + char *device_path = NULL; const char *progname = NULL, *upsname = NULL, *device_name = NULL; /* may be set by the driver to wake up while in dstate_poll_fds */ -int extrafd = -1; +TYPE_FD extrafd = ERROR_FD; +#ifdef WIN32 +static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif /* 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; +# ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +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; - +/* reload_flag is 0 most of the time (including initial config reading), + * and is briefly 1 when a reload signal is received and is being handled, + * or 2 if the reload attempt is allowed to exit the current driver (e.g. + * changed some ups.conf settings that can not be re-applied on the fly) + * assuming it gets restarted by external framework (systemd) or caller + * (like NUT driver CLI `-c reload-or-restart` handling), if needed. + */ +static int reload_flag = 0; + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +/* Should this driver instance go to background (default) + * or stay foregrounded (default if -D/-d options are set on + * command line)? Note that debug_min in ups.conf allows for + * verbosity while backgrounded by default. + * Value is multi-state (FIXME: enum?): + * -1 (default) Decide based on debug verbosity or dump_mode + * 0 User required to background even if with -D or dump_mode, + * or did not require foregrounding/dumping/debug on CLI + * 1 User required to not background explicitly, + * or passed -D (or -d) and current value was -1 + * 2 User required to not background explicitly, + * and yet to write the PID file, with -FF option + */ +static int foreground = -1; +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ + +/* 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). Finally, it can also be set over socket + * protocol, taking precedence over other inputs. + */ +static int nut_debug_level_args = -1; +static int nut_debug_level_global = -1; +static int nut_debug_level_driver = -1; +static int nut_debug_level_protocol = -1; + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN /* everything else */ static char *pidfn = NULL; static int dump_data = 0; /* Store the update_count requested */ +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ + +/* pre-declare some private methods used */ +static void assign_debug_level(void); +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ +static void set_reload_flag( +#ifndef WIN32 + int +#else + char * +#endif + sig); +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +/* Returns a result code from INSTCMD enum values */ +static int handle_reload_flag(void); +#endif + +/* Set in do_ups_confargs() for consumers like handle_reload_flag() */ +static int reload_requires_restart = -1; /* print the driver banner */ void upsdrv_banner (void) @@ -79,6 +157,7 @@ void upsdrv_banner (void) } } +#ifndef DRIVERS_MAIN_WITHOUT_MAIN /* power down the attached load immediately */ static void forceshutdown(void) __attribute__((noreturn)); @@ -89,7 +168,9 @@ static void forceshutdown(void) /* the driver must not block in this function */ upsdrv_shutdown(); - exit(EXIT_SUCCESS); + + /* the driver always exits here, to not block probable ongoing shutdown */ + exit(exit_flag == -1 ? EXIT_FAILURE : EXIT_SUCCESS); } /* this function only prints the usage message; it does not call exit() */ @@ -97,6 +178,8 @@ static void help_msg(void) { vartab_t *tmp; + nut_report_config_flags(); + printf("\nusage: %s (-a |-s ) [OPTIONS]\n", progname); printf(" -a - autoconfig using ups.conf section \n"); @@ -112,14 +195,45 @@ 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(" -FF - stay foregrounded and still save the PID file\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(" -c - send via signal to background process\n"); + printf(" Supported commands:\n"); +# ifndef WIN32 +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ + printf(" - reload: re-read configuration files, ignoring changed\n"); + printf(" values which require a driver restart (can not be changed\n"); + printf(" on the fly)\n"); +# endif /* WIN32 */ +/* Note: this one is beside signal-sending (goes via socket protocol): */ + printf(" - reload-or-error: re-read configuration files, ignoring but\n"); + printf(" counting changed values which require a driver restart (can\n"); + printf(" not be changed on the fly), and return a success/fail code\n"); + printf(" based on that count, so the caller can decide the fate of\n"); + printf(" the currently running driver instance\n"); +# ifndef WIN32 +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ +# ifdef SIGCMD_RELOAD_OR_RESTART + printf(" - reload-or-restart: re-read configuration files (close the\n"); + printf(" old driver instance device connection if needed, and have\n"); + printf(" it effectively restart)\n"); +# endif + printf(" - reload-or-exit: re-read configuration files (exit the old\n"); + printf(" driver instance if needed, so an external caller like the\n"); + printf(" systemd or SMF frameworks would start another copy)\n"); + /* NOTE for FIXME above: PID-signalling is non-WIN32-only for us */ + printf(" -P - send the signal above to specified PID (bypassing PID file)\n"); +# endif /* WIN32 */ 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"); @@ -140,9 +254,13 @@ static void help_msg(void) upsdrv_help(); } +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ /* store these in dstate as driver.(parameter|flag) */ -static void dparam_setinfo(const char *var, const char *val) +# ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void dparam_setinfo(const char *var, const char *val) { char vtmp[SMALLBUF]; @@ -160,18 +278,35 @@ static void dparam_setinfo(const char *var, const char *val) } /* cram var [= ] data into storage */ -static void storeval(const char *var, char *val) +# ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void storeval(const char *var, char *val) { vartab_t *tmp, *last; + /* NOTE: (FIXME?) The override and default mechanisms here + * effectively bypass both VAR_SENSITIVE protections and + * the constraint of having previously defined the name by + * addvar() in a driver codebase, or of having a dot in it. + * See https://github.com/networkupstools/nut/issues/1891 + * if this would need solving eventually. At the moment the + * sensitivity impacts certain auth values for netxml-ups + * and snmp-ups reading from vartab directly, and overrides + * are ignored - so no practical problem to solve right now. + */ if (!strncasecmp(var, "override.", 9)) { + /* NOTE: No regard for VAR_SENSITIVE here */ dstate_setinfo(var+9, "%s", val); dstate_setflags(var+9, ST_FLAG_IMMUTABLE); + dparam_setinfo(var, val); return; } if (!strncasecmp(var, "default.", 8)) { + /* NOTE: No regard for VAR_SENSITIVE here */ dstate_setinfo(var+8, "%s", val); + dparam_setinfo(var, val); return; } @@ -194,8 +329,13 @@ static void storeval(const char *var, char *val) tmp->val = xstrdup(val); /* don't keep things like SNMP community strings */ - if ((tmp->vartype & VAR_SENSITIVE) == 0) + if ((tmp->vartype & VAR_SENSITIVE) == 0) { dparam_setinfo(var, val); + } else { + upsdebugx(4, "%s: skip dparam_setinfo() " + "for sensitive variable '%s'", + __func__, var); + } tmp->found = 1; return; @@ -211,6 +351,36 @@ static void storeval(const char *var, char *val) printf("Look in the man page or call this driver with -h for a list of\n"); printf("valid variable names and flags.\n"); + if (!strcmp(progname, "nutdrv_qx")) { + /* First many entries are from nut_usb_addvars() implementations; + * the latter two (about langid) are from nutdrv_qx.c + */ + if (!strcmp(var, "vendor") + || !strcmp(var, "product") + || !strcmp(var, "serial") + || !strcmp(var, "vendorid") + || !strcmp(var, "productid") + || !strcmp(var, "bus") + || !strcmp(var, "device") + || !strcmp(var, "busport") + || !strcmp(var, "usb_set_altinterface") + || !strcmp(var, "usb_config_index") + || !strcmp(var, "usb_hid_rep_index") + || !strcmp(var, "usb_hid_desc_index") + || !strcmp(var, "usb_hid_ep_in") + || !strcmp(var, "usb_hid_ep_out") + || !strcmp(var, "allow_duplicates") + || !strcmp(var, "langid_fix") + || !strcmp(var, "noscanlangid") + ) { + printf("\nNOTE: for driver '%s', options like '%s' are only available\n" + "if it was built with USB support. If you are running a custom build of NUT,\n" + "please check results of the `configure` checks, and consider an explicit\n" + "`--with-usb` option. Also make sure that both libusb library and headers\n" + "are installed in your build environment.\n\n", progname, var); + } + } + exit(EXIT_SUCCESS); } @@ -242,8 +412,271 @@ int testvar(const char *var) return 0; /* not found */ } -/* callback from driver - create the table for -x/conf entries */ -void addvar(int vartype, const char *name, const char *desc) +/* See if can be (re-)loaded now: either is reloadable by definition, + * or no value has been given to it yet. Returns "-1" if nothing needs to + * be done and that is not a failure (e.g. value not modified so we do not + * care if we may change it or not). + */ +int testvar_reloadable(const char *var, const char *val, int vartype) +{ + vartab_t *tmp = vartab_h; + int verdict = -2; + + /* FIXME: handle VAR_FLAG typed (bitmask) values specially somehow? + * Either we set the flag at some point (because its name is mentioned) + * or we do not (initially set - no way so far to know it got commented + * away before a reload on the fly). Might load new config info into a + * separate list and then compare missing points?.. + */ + upsdebugx(6, "%s: searching for var=%s, vartype=%d, reload_flag=%d", + __func__, NUT_STRARG(var), vartype, reload_flag); + + while (tmp) { + if (!strcasecmp(tmp->var, var)) { + /* variable name is known */ + upsdebugx(6, "%s: found var=%s, val='%s' => '%s', vartype=%d => %d, found=%d, reloadable=%d, reload_flag=%d", + __func__, NUT_STRARG(var), + NUT_STRARG(tmp->val), NUT_STRARG(val), + tmp->vartype, vartype, + tmp->found, tmp->reloadable, reload_flag); + + if (val && tmp->val) { + /* a value is already known by name + * and bitmask for VAR_FLAG/VAR_VALUE matches + */ + if ((vartype & tmp->vartype) && !strcasecmp(tmp->val, val)) { + if ((tmp->vartype & VAR_FLAG) && val == NULL) { + if (reload_flag) { + upsdebugx(1, "%s: setting '%s' " + "exists and is a flag; " + "new value was not specified", + __func__, var); + } + + /* by default: apply flags initially, ignore later */ + verdict = ( + (!reload_flag) /* For initial config reads, legacy code trusted what it saw */ + || tmp->reloadable /* set in addvar*() */ + ); + goto finish; + } + + if (reload_flag) { + upsdebugx(1, "%s: setting '%s' " + "exists and is unmodified", + __func__, var); + } + + verdict = -1; /* no-op for caller */ + goto finish; + } else { + /* warn loudly if we are reloading and + * can not change this modified value */ + upsdebugx((reload_flag ? (tmp->reloadable ? 1 : 0) : 1), + "%s: setting '%s' exists and differs: " + "new type bitmask %d vs. %d, " + "new value '%s' vs. '%s'%s", + __func__, var, + vartype, tmp->vartype, + val, tmp->val, + ((!reload_flag || tmp->reloadable) ? "" : + " (driver restart is needed to apply)") + ); + /* FIXME: Define a special EXIT_RELOAD or something, + * for "not quite a failure"? Or close connections + * and re-exec() this driver from scratch (and so to + * keep MAINPID for systemd et al)? + */ + if (reload_flag == 2 && !tmp->reloadable) + fatalx( +#ifndef WIN32 + (128 + SIGCMD_RELOAD_OR_EXIT) +#else + EXIT_SUCCESS +#endif + , "NUT driver reload-or-exit: setting %s was changed and requires a driver restart", var); + + verdict = ( + (!reload_flag) /* For initial config reads, legacy code trusted what it saw */ + || tmp->reloadable /* set in addvar*() */ + ); + + /* handle reload-or-error reports */ + if (verdict == 0) { + if (reload_requires_restart < 1) + reload_requires_restart = 1; + else + reload_requires_restart++; + } + + goto finish; + } + } + + /* okay to redefine if not yet defined, or if reload is allowed, + * or if initially loading the configs + */ + verdict = ( + (!reload_flag) + || ((!tmp->found) || tmp->reloadable) + ); + goto finish; + } + tmp = tmp->next; + } + + verdict = 1; /* not found, may (re)load the definition */ + +finish: + switch (verdict) { + case -1: /* no-op for caller, same value remains */ + case 1: /* value may be (re-)applied */ + if (reload_requires_restart < 0) + reload_requires_restart = 0; + break; + + case 0: /* value may not be (re-)applied, but it may not have been required */ + break; + } + + upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d", + __func__, NUT_STRARG(var), verdict); + return verdict; +} + +/* Similar to testvar_reloadable() above which is for addvar*() defined + * entries, but for less streamlined stuff defined right here in main.c. + * See if value (probably saved in dstate) can be (re-)loaded now: either + * it is reloadable by parameter definition, or no value has been saved + * into it yet ( is NULL). + * Returns "-1" if nothing needs to be done and that is not a failure + * (e.g. value not modified so we do not care if we may change it or not). + */ +int testval_reloadable(const char *var, const char *oldval, const char *newval, int reloadable) +{ + int verdict = -2; + + upsdebugx(6, "%s: var=%s, oldval=%s, newval=%s, reloadable=%d, reload_flag=%d", + __func__, NUT_STRARG(var), NUT_STRARG(oldval), NUT_STRARG(newval), + reloadable, reload_flag); + + /* Nothing saved yet? Okay to store new value! */ + if (!oldval) { + verdict = 1; + goto finish; + } + + /* Should not happen? Or... (commented-away etc.) */ + if (!newval) { + upslogx(LOG_WARNING, "%s: new setting for '%s' is NULL", __func__, var); + verdict = ((!reload_flag) || reloadable); + goto finish; + } + + /* a value is already known, another is desired */ + if (!strcasecmp(oldval, newval)) { + if (reload_flag) { + upsdebugx(1, "%s: setting '%s' " + "exists and is unmodified", + __func__, var); + } + verdict = -1; /* no-op for caller */ + goto finish; + } else { + /* warn loudly if we are reloading and + * can not change this modified value */ + upsdebugx((reload_flag ? (reloadable ? 1 : 0) : 1), + "%s: setting '%s' exists and differs: " + "new value '%s' vs. '%s'%s", + __func__, var, + newval, oldval, + ((!reload_flag || reloadable) ? "" : + " (driver restart is needed to apply)") + ); + /* FIXME: Define a special EXIT_RELOAD or something, + * for "not quite a failure"? Or close connections + * and re-exec() this driver from scratch (and so to + * keep MAINPID for systemd et al)? + */ + if (reload_flag == 2 && !reloadable) + fatalx( +#ifndef WIN32 + (128 + SIGCMD_RELOAD_OR_EXIT) +#else + EXIT_SUCCESS +#endif + , "NUT driver reload-or-exit: setting %s was changed and requires a driver restart", var); + /* For initial config reads, legacy code trusted what it saw */ + verdict = ((!reload_flag) || reloadable); + + /* handle reload-or-error reports */ + if (verdict == 0) { + if (reload_requires_restart < 1) + reload_requires_restart = 1; + else + reload_requires_restart++; + } + + goto finish; + } + +finish: + switch (verdict) { + case -1: /* no-op for caller, same value remains */ + case 1: /* value may be (re-)applied */ + if (reload_requires_restart < 0) + reload_requires_restart = 0; + break; + + case 0: /* value may not be (re-)applied, but it may not have been required */ + break; + } + + upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d", + __func__, NUT_STRARG(var), verdict); + return verdict; +} + +/* Similar to testvar_reloadable() above which is for addvar*() defined + * entries, but for less streamlined stuff defined right here in main.c. + * See if (by name saved in dstate) can be (re-)loaded now: + * either it is reloadable by parameter definition, or no value has been + * saved into it yet ( is NULL). + * Returns "-1" if nothing needs to be done and that is not a failure + * (e.g. value not modified so we do not care if we may change it or not). + */ +int testinfo_reloadable(const char *var, const char *infoname, const char *newval, int reloadable) +{ + int verdict = -2; + + upsdebugx(6, "%s: var=%s, infoname=%s, newval=%s, reloadable=%d, reload_flag=%d", + __func__, NUT_STRARG(var), NUT_STRARG(infoname), NUT_STRARG(newval), + reloadable, reload_flag); + + /* Keep legacy behavior: not reloading, trust the initial config */ + if (!reload_flag || !infoname) { + verdict = 1; + goto finish; + } + + /* Suffer the overhead of lookups only if reloading */ + + /* FIXME: handle "driver.flag.*" prefixed values specially somehow? + * Either we set the flag at some point (because its name is mentioned) + * or we do not (initially set - no way so far to know it got commented + * away before a reload on the fly). Might load new config info into a + * separate list and then compare missing points?.. + */ + verdict = testval_reloadable(var, dstate_getinfo(infoname), newval, reloadable); + +finish: + upsdebugx(6, "%s: verdict for (re)loading var=%s value: %d", + __func__, NUT_STRARG(var), verdict); + return verdict; +} + +/* implement callback from driver - create the table for -x/conf entries */ +static void do_addvar(int vartype, const char *name, const char *desc, int reloadable) { vartab_t *tmp, *last; @@ -261,6 +694,7 @@ void addvar(int vartype, const char *name, const char *desc) tmp->val = NULL; tmp->desc = xstrdup(desc); tmp->found = 0; + tmp->reloadable = reloadable; tmp->next = NULL; if (last) @@ -269,32 +703,258 @@ void addvar(int vartype, const char *name, const char *desc) vartab_h = tmp; } +/* public callback from driver - create the table for -x/conf entries for reloadable values */ +void addvar_reloadable(int vartype, const char *name, const char *desc) +{ + do_addvar(vartype, name, desc, 1); +} + +/* public callback from driver - create the table for -x/conf entries for set-once values */ +void addvar(int vartype, const char *name, const char *desc) +{ + do_addvar(vartype, name, desc, 0); +} + +/* handle instant commands common for all drivers */ +int main_instcmd(const char *cmdname, const char *extra, conn_t *conn) { + char buf[SMALLBUF]; + if (conn) +#ifndef WIN32 + snprintf(buf, sizeof(buf), "socket %d", conn->fd); +#else + snprintf(buf, sizeof(buf), "handle %p", conn->fd); +#endif + else + snprintf(buf, sizeof(buf), "(null)"); + + upsdebugx(2, "entering main_instcmd(%s, %s) for [%s] on %s", + cmdname, extra, NUT_STRARG(upsname), buf); + + if (!strcmp(cmdname, "driver.killpower")) { + if (!strcmp("1", dstate_getinfo("driver.flag.allow_killpower"))) { + upslogx(LOG_WARNING, "Requesting UPS [%s] to power off, " + "as/if handled by its driver by default (may exit), " + "due to socket protocol request", NUT_STRARG(upsname)); + upsdrv_shutdown(); + return STAT_INSTCMD_HANDLED; + } else { + upslogx(LOG_WARNING, "Got socket protocol request for UPS [%s] " + "to power off, but driver.flag.allow_killpower does not" + "permit this - request was currently ignored!", + NUT_STRARG(upsname)); + return STAT_INSTCMD_INVALID; + } + } + +#ifndef WIN32 +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ + if (!strcmp(cmdname, "driver.reload")) { + set_reload_flag(SIGCMD_RELOAD); + /* TODO: sync mode to track that reload finished, and how? + * Especially to know if there were values we can not change + * on the fly, so caller may want to restart the driver itself. + */ + return STAT_INSTCMD_HANDLED; + } + + if (!strcmp(cmdname, "driver.reload-or-exit")) { + set_reload_flag(SIGCMD_RELOAD_OR_EXIT); + return STAT_INSTCMD_HANDLED; + } + +# ifdef SIGCMD_RELOAD_OR_RESTART + if (!strcmp(cmdname, "driver.reload-or-restart")) { + set_reload_flag(SIGCMD_RELOAD_OR_RESTART); + return STAT_INSTCMD_HANDLED; + } +# endif +#endif /* WIN32 */ + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN + if (!strcmp(cmdname, "driver.reload-or-error")) { + /* sync-capable handling */ + set_reload_flag(SIGCMD_RELOAD_OR_ERROR); + /* Returns a result code from INSTCMD enum values */ + return handle_reload_flag(); + } +#endif + + /* By default, the driver-specific values are + * unknown to shared standard handler */ + upsdebugx(2, "shared %s() does not handle command %s, " + "proceeding to driver-specific handler", + __func__, cmdname); + return STAT_INSTCMD_UNKNOWN; +} + +/* handle setting variables common for all drivers */ +int main_setvar(const char *varname, const char *val, conn_t *conn) { + char buf[SMALLBUF]; + if (conn) +#ifndef WIN32 + snprintf(buf, sizeof(buf), "socket %d", conn->fd); +#else + snprintf(buf, sizeof(buf), "handle %p", conn->fd); +#endif + else + snprintf(buf, sizeof(buf), "(null)"); + + upsdebugx(2, "entering main_setvar(%s, %s) for [%s] on %s", + varname, val, NUT_STRARG(upsname), buf); + + if (!strcmp(varname, "driver.debug")) { + int num; + if (str_to_int(val, &num, 10)) { + if (num < 0) { + upsdebugx(nut_debug_level > 0 ? 1 : 0, + "NOTE: Will fall back to CLI/DriverConfig/GlobalConfig debug verbosity preference now"); + num = -1; + } + if (nut_debug_level > 0 && num == 0) + upsdebugx(1, "NOTE: Will disable verbose debug now, due to socket protocol request"); + nut_debug_level_protocol = num; + assign_debug_level(); + return STAT_SET_HANDLED; + } else { + goto invalid; + } + } + + if (!strcmp(varname, "driver.flag.allow_killpower")) { + int num = 0; + if (str_to_int(val, &num, 10)) { + if (num <= 0) { + num = 0; + } else num = 1; + } else { + /* support certain strings */ + if (!strncmp(val, "enable", 6) /* "enabled" matches too */ + || !strcmp(val, "true") + || !strcmp(val, "yes") + || !strcmp(val, "on") + ) num = 1; + } + + upsdebugx(1, "%s: Setting %s=%d", __func__, varname, num); + dstate_setinfo("driver.flag.allow_killpower", "%d", num); + return STAT_SET_HANDLED; + } + + /* By default, the driver-specific values are + * unknown to shared standard handler */ + upsdebugx(2, "shared %s() does not handle variable %s, " + "proceeding to driver-specific handler", + __func__, varname); + return STAT_SET_UNKNOWN; + +invalid: + upsdebugx(1, "Error: UPS [%s]: invalid %s value: %s", + NUT_STRARG(upsname), varname, val); + return STAT_SET_INVALID; +} + /* handle -x / ups.conf config details that are for this part of the code */ static int main_arg(char *var, char *val) { + int do_handle = -2; + /* flags for main */ + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); + + /* !reload_flag simply forbids changing this flag on the fly, as + * it would have no effect anyway without a (serial) reconnection + */ if (!strcmp(var, "nolock")) { - do_lock_port = 0; - dstate_setinfo("driver.flag.nolock", "enabled"); + if (reload_flag) { + upsdebugx(6, "%s: SKIP: flag var='%s' can not be reloaded", __func__, var); + } else { + do_lock_port = 0; + dstate_setinfo("driver.flag.nolock", "enabled"); + } return 1; /* handled */ } + /* FIXME: this one we could potentially reload, but need to figure + * out that the flag line was commented away or deleted -- there is + * no setting value to flip in configs here + */ if (!strcmp(var, "ignorelb")) { - dstate_setinfo("driver.flag.ignorelb", "enabled"); + if (reload_flag) { + upsdebugx(6, "%s: SKIP: flag var='%s' currently can not be reloaded", __func__, var); + } else { + dstate_setinfo("driver.flag.ignorelb", "enabled"); + } + return 1; /* handled */ + } + + if (!strcmp(var, "allow_killpower")) { + if (reload_flag) { + upsdebugx(6, "%s: SKIP: flag var='%s' currently can not be reloaded " + "(but may be changed by protocol SETVAR)", __func__, var); + } else { + dstate_setinfo("driver.flag.allow_killpower", "1"); + } return 1; /* handled */ } /* any other flags are for the driver code */ if (!val) - return 0; + return 0; /* unhandled, pass it through to the driver */ + + /* In checks below, testinfo_reloadable(..., 0) should forbid + * re-population of the setting with a new value, but emit a + * warning if it did change (so driver restart is needed to apply) + */ /* variables for main: port */ if (!strcmp(var, "port")) { - device_path = xstrdup(val); - device_name = xbasename(device_path); - dstate_setinfo("driver.parameter.port", "%s", val); + if (testinfo_reloadable(var, "driver.parameter.port", val, 0) > 0) { + device_path = xstrdup(val); + device_name = xbasename(device_path); + dstate_setinfo("driver.parameter.port", "%s", 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 (testval_reloadable(var, user, val, 0) > 0) { + 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 (testval_reloadable(var, group, val, 0) > 0) { + 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 */ } @@ -303,12 +963,58 @@ static int main_arg(char *var, char *val) return 1; /* handled */ } - /* allow per-driver overrides of the global setting */ + /* Allow per-driver overrides of the global setting + * and allow to reload this, why not. + * Note: having both global+driver section definitions may + * cause noise, but it allows either to be commented away + * and the other to take hold. Both disappearing would not + * be noticed by the reload operation currently, however. + */ + if (!strcmp(var, "pollinterval")) { + char buf[SMALLBUF]; + + /* log a message if value changed; skip if no good buf */ + if (snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)poll_interval)) { + if ((do_handle = testval_reloadable(var, buf, val, 1)) == 0) { + /* Should not happen, but... */ + fatalx(EXIT_FAILURE, "Error: failed to check " + "testval_reloadable() for pollinterval: " + "old %s vs. new %s", buf, NUT_STRARG(val)); + } + } + + if (do_handle > 0) { + int ipv = atoi(val); + if (ipv > 0) { + poll_interval = (time_t)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: UPS [%s]: invalid pollinterval: %d", + NUT_STRARG(upsname), ipv); + } + } /* else: no-op */ + + return 1; /* handled */ + } + + /* Allow per-driver overrides of the global setting + * and allow to reload this, why not. + * Note: this may cause "spurious" redefinitions of the + * "no" setting which is the fallback for random values. + * Also note that global+driver section definitions may + * cause noise, but it allows either to be commented away + * and the other to take hold. Both disappearing would not + * be noticed by the reload operation currently, however. + */ if (!strcmp(var, "synchronous")) { - if (!strcmp(val, "yes")) - do_synchronous=1; - else - do_synchronous=0; + if (testval_reloadable(var, ((do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")), val, 1) > 0) { + if (!strcmp(val, "yes")) + do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; + else + do_synchronous=0; + } return 1; /* handled */ } @@ -321,38 +1027,147 @@ 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. + * Note: during reload_flag!=0 handling this is reset to -1, to + * catch commented-away settings, so not checking previous value. + */ + 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) { + char buf[SMALLBUF]; + int do_handle = 1; + + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); + + /* Allow to reload this, why not */ if (!strcmp(var, "pollinterval")) { - int ipv = atoi(val); - if (ipv > 0) { - poll_interval = (time_t)ipv; - } else { - fatalx(EXIT_FAILURE, "Error: invalid pollinterval: %d", ipv); + /* log a message if value changed; skip if no good buf */ + if (snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)poll_interval)) { + if ((do_handle = testval_reloadable(var, buf, val, 1)) == 0) { + /* Should not happen, but... */ + fatalx(EXIT_FAILURE, "Error: failed to check " + "testval_reloadable() for pollinterval: " + "old %s vs. new %s", buf, val); + } } + + if (do_handle > 0) { + int ipv = atoi(val); + if (ipv > 0) { + poll_interval = (time_t)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: invalid pollinterval: %d", ipv); + } + } /* else: no-op */ + return; } + /* In checks below, testinfo_reloadable(..., 0) should forbid + * re-population of the setting with a new value, but emit a + * warning if it did change (so driver restart is needed to apply) + */ + if (!strcmp(var, "chroot")) { - free(chroot_path); - chroot_path = xstrdup(val); + if (testval_reloadable(var, chroot_path, val, 0) > 0) { + free(chroot_path); + chroot_path = xstrdup(val); + } + + return; } if (!strcmp(var, "user")) { - free(user); - user = xstrdup(val); + if (testval_reloadable(var, user, val, 0) > 0) { + 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); + } + } + + return; + } + + if (!strcmp(var, "group")) { + if (testval_reloadable(var, group, val, 0) > 0) { + 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); + } + } + + return; } + /* Allow to reload this, why not + * Note: this may cause "spurious" redefinitions of the + * "no" setting which is the fallback for random values. + * Also note that global+driver section definitions may + * cause noise, but it allows either to be commented away + * and the other to take hold. Both disappearing would not + * be noticed by the reload operation currently, however. + */ if (!strcmp(var, "synchronous")) { - if (!strcmp(val, "yes")) - do_synchronous=1; - else - do_synchronous=0; + if (testval_reloadable(var, ((do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")), val, 1) > 0) { + if (!strcmp(val, "yes")) + do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; + else + do_synchronous=0; + } + + return; } + /* 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. + * Note: during reload_flag!=0 handling this is reset to -1, to + * catch commented-away settings, so not checking previous value. + */ + 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"); + } + + return; + } /* unrecognized */ } @@ -361,8 +1176,12 @@ void do_upsconf_args(char *confupsname, char *var, char *val) { char tmp[SMALLBUF]; + upsdebugx(5, "%s: confupsname=%s, var=%s, val=%s", + __func__, NUT_STRARG(confupsname), NUT_STRARG(var), NUT_STRARG(val)); + /* handle global declarations */ if (!confupsname) { + upsdebugx(5, "%s: call do_global_args()", __func__); do_global_args(var, val); return; } @@ -373,42 +1192,214 @@ void do_upsconf_args(char *confupsname, char *var, char *val) upsname_found = 1; + upsdebugx(5, "%s: call main_arg()", __func__); if (main_arg(var, val)) return; + upsdebugx(5, "%s: not a main_arg()", __func__); /* flags (no =) now get passed to the driver-level stuff */ if (!val) { + upsdebugx(5, "%s: process as flag", __func__); /* also store this, but it's a bit different */ snprintf(tmp, sizeof(tmp), "driver.flag.%s", var); - dstate_setinfo(tmp, "enabled"); - storeval(var, NULL); + /* allow reloading if defined and permitted via addvar() + * or not defined there (FIXME?) + */ + if (testvar_reloadable(var, NULL, VAR_FLAG) > 0) { + dstate_setinfo(tmp, "enabled"); + storeval(var, NULL); + } + return; } - /* don't let the user shoot themselves in the foot */ + /* In checks below, testval_reloadable(..., 0) should forbid + * re-population of the setting with a new value, but emit a + * warning if it did change (so driver restart is needed to apply) + */ + + /* don't let the user shoot themselves in the foot + * reload should not allow changes here, but would report + */ if (!strcmp(var, "driver")) { - if (strcmp(val, progname) != 0) + int do_handle; + + upsdebugx(5, "%s: this is a 'driver' setting, may we proceed?", __func__); + do_handle = testval_reloadable(var, progname, val, 0); + + if (do_handle == -1) { + upsdebugx(5, "%s: 'driver' setting already applied with this value", __func__); + return; + } + + /* Acceptable progname is only set once during start-up + * val is from ups.conf + */ + if (!reload_flag || do_handle > 0) { + /* Accomodate for libtool wrapped developer iterations + * running e.g. `drivers/.libs/lt-dummy-ups` filenames + */ + size_t tmplen = strlen("lt-"); + if (strncmp("lt-", progname, tmplen) == 0 + && strcmp(val, progname + tmplen) == 0) { + /* debug level may be not initialized yet, and situation + * should not happen in end-user builds, so ok to yell: */ + upsdebugx(0, "Seems this driver binary %s is a libtool " + "wrapped build for driver %s", progname, val); + /* progname points to xbasename(argv[0]) in-place; + * roll the pointer forward a bit, we know we can: + */ + progname = progname + tmplen; + } + } + + if (strcmp(val, progname) != 0) { fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); + } return; } - /* allow per-driver overrides of the global setting */ - if (!strcmp(var, "pollinterval")) { - int ipv = atoi(val); - if (ipv > 0) { - poll_interval = (time_t)ipv; - } else { - fatalx(EXIT_FAILURE, "Error: UPS [%s]: invalid pollinterval: %d", - confupsname, ipv); + /* everything else must be for the driver */ + + /* allow reloading if defined and permitted via addvar() + * or not defined there (FIXME?) + */ + upsdebugx(5, "%s: process as value", __func__); + if (testvar_reloadable(var, val, VAR_VALUE) > 0) { + storeval(var, val); + } +} + +static void assign_debug_level(void) { + /* 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_protocol >= 0) { + upslogx(LOG_INFO, + "Applying debug level %d received during run-time " + "via socket protocol, ignoring other settings", + nut_debug_level_protocol); + nut_debug_level = nut_debug_level_protocol; + goto finish; + } + + if (nut_debug_level_global >= 0 && nut_debug_level_driver >= 0) { + /* Use nearest-defined fit */ + nut_debug_level_upsconf = nut_debug_level_driver; + if (reload_flag) { + upslogx(LOG_INFO, + "Applying debug_min=%d from ups.conf" + " driver section (overriding global %d)", + nut_debug_level_upsconf, nut_debug_level_global); + } + } else { + if (nut_debug_level_global >= 0) { + nut_debug_level_upsconf = nut_debug_level_global; + if (reload_flag) upslogx(LOG_INFO, + "Applying debug_min=%d from ups.conf" + " global section", + nut_debug_level_upsconf); + } + if (nut_debug_level_driver >= 0) { + nut_debug_level_upsconf = nut_debug_level_driver; + if (reload_flag) upslogx(LOG_INFO, + "Applying debug_min=%d from ups.conf" + " driver section", + nut_debug_level_upsconf); } - return; } - /* everything else must be for the driver */ - storeval(var, val); + if (reload_flag && nut_debug_level_upsconf <= nut_debug_level_args) { + /* DEBUG_MIN is absent or commented-away in ups.conf, + * or is smaller than te CLI arg '-D' count */ + upslogx(LOG_INFO, + "Applying debug level %d from " + "original command line arguments", + nut_debug_level_args); + } + + /* at minimum, the verbosity we started with - via CLI arguments; + * maybe a greater debug_min is set in current config file + */ + nut_debug_level = nut_debug_level_args; + if (nut_debug_level_upsconf > nut_debug_level) + nut_debug_level = nut_debug_level_upsconf; + +finish: + upsdebugx(1, "debug level is '%d'", nut_debug_level); + dstate_setinfo("driver.debug", "%d", nut_debug_level); + dstate_setflags("driver.debug", ST_FLAG_RW | ST_FLAG_NUMBER); +} + +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +/* Returns a result code from INSTCMD enum values */ +static int handle_reload_flag(void) { + int ret; + + if (!reload_flag || exit_flag) + return STAT_INSTCMD_INVALID; + + upslogx(LOG_INFO, "Handling requested live reload of NUT driver configuration for [%s]", upsname); + dstate_setinfo("driver.state", "reloading"); + upsnotify(NOTIFY_STATE_RELOADING, NULL); + + /* If commented away or deleted in config, debug_min + * should "disappear" for us (a CLI argument, if any, + * would still be honoured); if it is (re-)defined in + * config, then it gets considered. + */ + nut_debug_level_global = -1; + nut_debug_level_driver = -1; + + /* Call actual config reloading activity, which + * eventually calls back do_upsconf_args() from + * this program. + */ + reload_requires_restart = -1; + /* 0 - Do not abort drivers started with '-s TMP_UPS_NAME' */ + if (read_upsconf(0) < 0) { + upsdebugx(1, "%s: read_upsconf() failed fundamentally; " + "is this driver running via ups.conf at all?", + __func__); + } + + upsdebugx(1, "%s: read_upsconf() for [%s] completed, restart-required verdict was: %d", + __func__, upsname, reload_requires_restart); + + /* handle reload-or-error reports */ + if (reload_requires_restart < 1) { + /* -1 unchanged, 0 nobody complained and everyone confirmed */ + ret = STAT_INSTCMD_HANDLED; + } else { + /* 1+ entries required a restart */ + ret = STAT_INSTCMD_INVALID; + } + + /* TODO: Callbacks in drivers to re-parse configs? + * Currently this reloadability relies on either + * explicit reload_flag aware code called from the + * read_upsconf() method, or on drivers continuously + * reading dstate_getinfo() and not caching once + * their C variables. + */ + + /* Re-mix currently known debug verbosity desires */ + assign_debug_level(); + + /* Wrap it up */ + reload_flag = 0; + dstate_setinfo("driver.state", "quiet"); + upsnotify(NOTIFY_STATE_READY, NULL); + upslogx(LOG_INFO, "Completed requested live reload of NUT driver configuration for [%s]: %d", upsname, ret); + + return ret; } /* split -x foo=bar into 'foo' and 'bar' */ @@ -459,8 +1450,12 @@ static void listxarg(void) tmp = tmp->next; } } +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ -static void vartab_free(void) +# ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void vartab_free(void) { vartab_t *tmp, *next; @@ -478,11 +1473,25 @@ static void vartab_free(void) } } +#ifndef DRIVERS_MAIN_WITHOUT_MAIN +static void exit_upsdrv_cleanup(void) +{ + dstate_setinfo("driver.state", "cleanup.upsdrv"); + upsdrv_cleanup(); +} + static void exit_cleanup(void) { + dstate_setinfo("driver.state", "cleanup.exit"); + + if (!dump_data) { + upsnotify(NOTIFY_STATE_STOPPING, "exit_cleanup()"); + } + free(chroot_path); free(device_path); free(user); + free(group); if (pidfn) { unlink(pidfn); @@ -491,25 +1500,107 @@ static void exit_cleanup(void) dstate_free(); vartab_free(); + +#ifdef WIN32 + if(mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ -static void set_exit_flag(int sig) +void set_exit_flag(int sig) { + switch (exit_flag) { + case -2: + upsdebugx(1, "%s: raising exit flag due to programmatic abort: EXIT_SUCCESS", __func__); + break; + case -1: + upsdebugx(1, "%s: raising exit flag due to programmatic abort: EXIT_FAILURE", __func__); + break; + default: + upsdebugx(1, "%s: raising exit flag due to signal %d", __func__, sig); + } exit_flag = sig; } -static void setup_signals(void) +static void set_reload_flag( +#ifndef WIN32 + int +#else + char * +#endif + sig) +{ +#ifndef WIN32 +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ + switch (sig) { + case SIGCMD_RELOAD_OR_EXIT: /* SIGUSR1 */ + /* reload-or-exit (this driver instance may die) */ + reload_flag = 2; + break; + +#ifdef SIGCMD_RELOAD_OR_RESTART + case SIGCMD_RELOAD_OR_RESTART: /* SIGUSR2 */ + /* reload-or-restart (this driver instance may recycle itself) */ + /* FIXME: Not implemented yet */ + reload_flag = 3; + break; +#endif + + case SIGCMD_RELOAD: /* SIGHUP */ + case SIGCMD_RELOAD_OR_ERROR: /* Not even a signal, but a socket protocol action */ + default: + /* reload what we can, log what needs a restart so skipped */ + reload_flag = 1; + } + + upsdebugx(1, "%s: raising reload flag due to signal %d (%s) => reload_flag=%d", + __func__, sig, strsignal(sig), reload_flag); +#else + if (sig && !strcmp(sig, SIGCMD_RELOAD_OR_ERROR)) { + /* reload what we can, log what needs a restart so skipped */ + reload_flag = 1; + } else { + /* non-fatal reload as a fallback */ + reload_flag = 1; + } + + upsdebugx(1, "%s: raising reload flag due to command %s => reload_flag=%d", + __func__, sig, reload_flag); +#endif /* WIN32 */ +} + +#ifndef WIN32 +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ +static void handle_dstate_dump(int sig) { + /* no set_dump_flag() here, make it instant */ + upsdebugx(1, "%s: starting driver state dump for [%s] due to signal %d", + __func__, upsname, sig); + /* FIXME: upslogx() instead of printf() when backgrounded, if STDOUT got closed? */ + dstate_dump(); + upsdebugx(1, "%s: finished driver state dump for [%s]", + __func__, upsname); +} + +# ifndef DRIVERS_MAIN_WITHOUT_MAIN +static +# endif /* DRIVERS_MAIN_WITHOUT_MAIN */ +void setup_signals(void) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; + /* handle shutdown signals */ sa.sa_handler = set_exit_flag; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); + /* basic signal setup to ignore SIGPIPE */ #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstrict-prototypes" @@ -518,22 +1609,130 @@ static void setup_signals(void) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) # pragma GCC diagnostic pop #endif - sigaction(SIGHUP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); + + /* handle reloading */ + sa.sa_handler = set_reload_flag; + sigaction(SIGCMD_RELOAD, &sa, NULL); /* SIGHUP */ + sigaction(SIGCMD_RELOAD_OR_EXIT, &sa, NULL); /* SIGUSR1 */ +# ifdef SIGCMD_RELOAD_OR_RESTART +/* FIXME: Want SIGCMD_RELOAD_OR_RESTART implemented */ + sigaction(SIGCMD_RELOAD_OR_RESTART, &sa, NULL); /* SIGUSR2 */ +# endif + +# ifdef SIGCMD_DATA_DUMP + /* handle run-time data dump (may be limited to non-backgrounding lifetimes) */ + sa.sa_handler = handle_dstate_dump; + sigaction(SIGCMD_DATA_DUMP, &sa, NULL); /* SIGURG or SIGWINCH something else on obscure systems */ +# endif } +#endif /* WIN32*/ +/* This source file is used in some unit tests to mock realistic driver + * behavior - using a production driver skeleton, but their own main(). + */ +#ifndef DRIVERS_MAIN_WITHOUT_MAIN int main(int argc, char **argv) { struct passwd *new_uid = NULL; int i, do_forceshutdown = 0; int update_count = 0; +#ifndef WIN32 + int cmd = 0; + pid_t oldpid = -1; +#else +/* FIXME: *actually* handle WIN32 builds too */ + const char * cmd = NULL; +#endif + + const char optstring[] = "+a:s:kDFBd:hx:Lqr:u:g:Vi:c:" +#ifndef WIN32 + "P:" +#endif + ; + + /* init verbosity from default in common.c (0 probably) */ + nut_debug_level_args = nut_debug_level; + + /* handle CLI-driven debug level in advance, to trace initialization if needed */ + while ((i = getopt(argc, argv, optstring)) != -1) { + switch (i) { + case 'D': + /* bump right here, may impact reporting of other CLI args */ + nut_debug_level++; + nut_debug_level_args++; + break; + } + } + /* Reset the index, read argv[1] next time (loop below) + * https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html + */ + optind = 1; + + if (foreground < 0) { + /* Guess a default */ + /* Note: only care about CLI-requested debug verbosity here */ + if (nut_debug_level > 0 || dump_data) { + /* Only flop from default - stay foreground with debug on */ + foreground = 1; + } else { + /* Legacy default - stay background and quiet */ + foreground = 0; + } + } else { + /* Follow explicit user -F/-B request */ + upsdebugx (0, + "Debug level is %d, dump data count is %s, " + "but backgrounding mode requested as %s", + nut_debug_level, + dump_data ? "on" : "off", + foreground ? "off" : "on" + ); + } + + { /* scoping */ + char *s = getenv("NUT_DEBUG_LEVEL"); + int l; + if (s && str_to_int(s, &l, 10)) { + if (l > 0 && nut_debug_level_args < 1) { + upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d " + "since none was requested by command-line options", l); + nut_debug_level = l; + nut_debug_level_args = l; + } /* else follow -D settings */ + } /* else nothing to bother about */ + } + + dstate_setinfo("driver.state", "init.starting"); + atexit(exit_cleanup); /* pick up a default from configure --with-user */ user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */ + /* pick up a default from configure --with-group */ + group = xstrdup(RUN_AS_GROUP); /* xstrdup: this gets freed at exit */ + progname = xbasename(argv[0]); + +#ifdef WIN32 + const char * drv_name; + drv_name = xbasename(argv[0]); + /* remove trailing .exe */ + char * dot = strrchr(drv_name,'.'); + if( dot != NULL ) { + if(strcasecmp(dot, ".exe") == 0 ) { + progname = strdup(drv_name); + char * t = strrchr(progname,'.'); + *t = 0; + } + } + else { + progname = strdup(drv_name); + } +#endif + open_syslog(progname); upsdrv_banner(); @@ -546,23 +1745,42 @@ 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, optstring)) != -1) { switch (i) { case 'a': + if (upsname) + fatalx(EXIT_FAILURE, "Error: options '-a id' and '-s id' " + "are mutually exclusive and single-use only."); + upsname = optarg; - read_upsconf(); + read_upsconf(1); if (!upsname_found) fatalx(EXIT_FAILURE, "Error: Section %s not found in ups.conf", optarg); break; case 's': + if (upsname) + fatalx(EXIT_FAILURE, "Error: options '-a id' and '-s id' " + "are mutually exclusive and single-use only."); + upsname = optarg; upsname_found = 1; 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 'D': - nut_debug_level++; + /* Processed above */ break; case 'd': dump_data = atoi(optarg); @@ -581,6 +1799,57 @@ int main(int argc, char **argv) do_lock_port = 0; do_forceshutdown = 1; break; +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ + case 'c': + if (cmd) { + help_msg(); + fatalx(EXIT_FAILURE, + "Error: only one command per run can be " + "sent with option -%c. Try -h for help.", i); + } + + if (!strncmp(optarg, "reload-or-error", strlen(optarg))) { + cmd = SIGCMD_RELOAD_OR_ERROR; + } +#ifndef WIN32 + else + if (!strncmp(optarg, "reload", strlen(optarg))) { + cmd = SIGCMD_RELOAD; + } else +# ifdef SIGCMD_RELOAD_OR_RESTART + if (!strncmp(optarg, "reload-or-restart", strlen(optarg))) { + cmd = SIGCMD_RELOAD_OR_RESTART; + } else +# endif + if (!strncmp(optarg, "reload-or-exit", strlen(optarg))) { + cmd = SIGCMD_RELOAD_OR_EXIT; + } +#endif /* WIN32 */ + + /* bad command given */ + if (!cmd) { + help_msg(); + fatalx(EXIT_FAILURE, + "Error: unknown argument to option -%c. Try -h for help.", i); + } +#ifndef WIN32 + upsdebugx(1, "Will send signal %d (%s) for command '%s' " + "to already-running driver %s-%s (if any) and exit", + cmd, strsignal(cmd), optarg, progname, upsname); +#else + upsdebugx(1, "Will send request '%s' for command '%s' " + "to already-running driver %s-%s (if any) and exit", + cmd, optarg, progname, upsname); +#endif /* WIN32 */ + break; + +#ifndef WIN32 + /* NOTE for FIXME above: PID-signalling is non-WIN32-only for us */ + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help_msg(); + break; +#endif /* WIN32 */ case 'L': listxarg(); exit(EXIT_SUCCESS); @@ -591,11 +1860,40 @@ 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 */ + /* already printed the banner for program name */ + nut_report_config_flags(); exit(EXIT_SUCCESS); case 'x': splitxarg(optarg); @@ -609,6 +1907,15 @@ int main(int argc, char **argv) } } + /* Since debug mode dumps from drivers are often posted to mailing list + * or issue tracker, as well as viewed locally, it can help to know the + * build options involved when troubleshooting (especially when needed + * to walk through building a PR branch with candidate fix for an issue). + * Reference code for such message is in common/common.c prints to log + * and/or syslog when any debug level is enabled. + */ + nut_report_config_flags(); + argc -= optind; argv += optind; @@ -622,14 +1929,14 @@ int main(int argc, char **argv) "Error: specifying '-a id' or '-s id' is now mandatory. Try -h for help."); } - /* we need to get the port from somewhere */ - if (!device_path) { + /* we need to get the port from somewhere, unless we are just sending a signal and exiting */ + if (!device_path && !cmd) { fatalx(EXIT_FAILURE, "Error: you must specify a port name in ups.conf or in '-x port=...' argument.\n" "Try -h for help."); } - upsdebugx(1, "debug level is '%d'", nut_debug_level); + assign_debug_level(); new_uid = get_user_pwent(user); @@ -638,18 +1945,191 @@ 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 */ - 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)) { - char buffer[SMALLBUF]; + /* 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 */ +#ifndef WIN32 + if ((!do_forceshutdown) && (!dump_data)) { + if (chdir(dflt_statepath())) + fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); + /* Setup signals to communicate with driver which is destined for a long run. */ setup_signals(); + } +#endif /* WIN32 */ + + if (do_forceshutdown) { + /* First try to handle this over socket protocol + * with the running older driver instance (if any); + * if this does not succeed, fall through to legacy + * approach (kill sibling if needed, recapture device, + * command it...) + */ + ssize_t cmdret = -1; + struct timeval tv; + + /* Post the query and wait for reply */ + /* FIXME: coordinate with pollfreq? */ + tv.tv_sec = 15; + tv.tv_usec = 0; + cmdret = upsdrvquery_oneshot(progname, upsname, + "SET driver.flag.allow_killpower 1\n", + NULL, 0, &tv); + + if (cmdret >= 0) { + /* FIXME: somehow mark drivers expected to loop infinitely? */ + tv.tv_sec = -1; + tv.tv_usec = -1; + cmdret = upsdrvquery_oneshot(progname, upsname, + "INSTCMD driver.killpower\n", + NULL, 0, &tv); + + if (cmdret < 0) { + upsdebugx(1, "Socket dialog with the other driver instance: %s", strerror(errno)); + } else { + upslogx(LOG_INFO, "Request to killpower via running driver returned code %" PRIiSIZE, cmdret); + if (cmdret == 0) + /* Note: many drivers would abort with + * "shutdown not supported" at this + * point... we would too, but later + * and at a higher time/processing cost. + */ + exit (EXIT_SUCCESS); + /* else fall through to legacy handling */ + } + } else { + upsdebugx(1, "Socket dialog with the other driver instance: %s", + strerror(errno)); + } + } + + /* Handle reload-or-error over socket protocol with + * the running older driver instance */ +#ifndef WIN32 + if (cmd == SIGCMD_RELOAD_OR_ERROR) +#else + if (cmd && !strcmp(cmd, SIGCMD_RELOAD_OR_ERROR)) +#endif /* WIN32 */ + { /* Not a signal, but a socket protocol action */ + ssize_t cmdret = -1; + char buf[LARGEBUF]; + struct timeval tv; + + /* Post the query and wait for reply */ + /* FIXME: coordinate with pollfreq? */ + tv.tv_sec = 15; + tv.tv_usec = 0; + cmdret = upsdrvquery_oneshot(progname, upsname, + "INSTCMD driver.reload-or-error\n", + buf, sizeof(buf), &tv); + + if (cmdret < 0) { + upslog_with_errno(LOG_ERR, "Socket dialog with the other driver instance"); + } else { + /* TODO: handle buf reply contents */ + upslogx(LOG_INFO, "Request to reload-or-error returned code %" PRIiSIZE, cmdret); + } + + /* exit((cmdret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); */ + exit(((cmdret < 0) || (((uintmax_t)cmdret) > ((uintmax_t)INT_MAX))) ? 255 : (int)cmdret); + } + +#ifndef WIN32 + /* Setup PID file to receive signals to communicate with this driver + * instance once backgrounded, and to stop a competing older instance. + * Or to send it a signal deliberately. + */ + if (cmd || ((foreground == 0) && (!do_forceshutdown))) { + char pidfnbuf[SMALLBUF]; + + snprintf(pidfnbuf, sizeof(pidfnbuf), "%s/%s-%s.pid", altpidpath(), progname, upsname); + + if (cmd) { /* Signals */ + int cmdret = -1; + /* Send a signal to older copy of the driver, if any */ + if (oldpid < 0) { + cmdret = sendsignalfn(pidfnbuf, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + + switch (cmdret) { + case 0: + upsdebugx(1, "Signaled old daemon OK"); + break; + + case -3: + case -2: + /* if starting new daemon, no competition running - + * maybe OK (or failed to detect it => problem) + * if signaling old daemon - certainly have a problem + */ + upslogx(LOG_WARNING, "Could not %s PID file '%s' " + "to see if previous driver instance is " + "already running!", + (cmdret == -3 ? "find" : "parse"), + pidfnbuf); + break; + + case -1: + case 1: /* WIN32 */ + default: + /* if cmd was nontrivial - speak up below, else be quiet */ + upsdebugx(1, "Just failed to send signal, no daemon was running"); + break; + } + + /* We were signalling a daemon, successfully or not - exit now... + * Modulo the possibility of a "reload-or-something" where we + * effectively terminate the old driver and start a new one due + * to configuration changes that were not reloadable. Such mode + * is not implemented currently. + */ + if (cmdret != 0) { + /* sendsignal*() above might have logged more details + * for troubleshooting, e.g. about lack of PID file + */ + upslogx(LOG_NOTICE, "Failed to signal the currently running daemon (if any)"); +# ifdef HAVE_SYSTEMD + switch (cmd) { + case SIGCMD_RELOAD: + upslogx(LOG_NOTICE, "Try something like " + "'systemctl reload nut-driver@%s.service'%s", + upsname, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + + case SIGCMD_RELOAD_OR_EXIT: +# ifdef SIGCMD_RELOAD_OR_RESTART + case SIGCMD_RELOAD_OR_RESTART: +# endif + upslogx(LOG_NOTICE, "Try something like " + "'systemctl reload-or-restart " + "nut-driver@%s.service'%s", + upsname, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + + default: + upslogx(LOG_NOTICE, "Try something like " + "'systemctl nut-driver@%s.service'%s", + upsname, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + } + /* ... or edit nut-server.service locally to start `upsd -FF` + * and so save the PID file for ability to manage the daemon + * beside the service framework, possibly confusing things... + */ +# else /* not HAVE_SYSTEMD */ + if (oldpid < 0) { + upslogx(LOG_NOTICE, "Try to add '-P $PID' argument"); + } +# endif /* HAVE_SYSTEMD */ + } - snprintf(buffer, sizeof(buffer), "%s/%s-%s.pid", altpidpath(), progname, upsname); + exit((cmdret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); + } /* Try to prevent that driver is started multiple times. If a PID file */ /* already exists, send a TERM signal to the process and try if it goes */ @@ -657,28 +2137,92 @@ int main(int argc, char **argv) for (i = 0; i < 3; i++) { struct stat st; - if (stat(buffer, &st) != 0) { + if (stat(pidfnbuf, &st) != 0) { /* PID file not found */ break; } - if (sendsignalfn(buffer, SIGTERM) != 0) { + upslogx(LOG_WARNING, "Duplicate driver instance detected (PID file %s exists)! Terminating other driver!", pidfnbuf); + + if (sendsignalfn(pidfnbuf, SIGTERM) != 0) { /* Can't send signal to PID, assume invalid file */ break; } - upslogx(LOG_WARNING, "Duplicate driver instance detected (PID file %s exists)! Terminating other driver!", buffer); - /* Allow driver some time to quit */ sleep(5); } + if (i > 0) { + struct stat st; + if (stat(pidfnbuf, &st) == 0) { + upslogx(LOG_WARNING, "Duplicate driver instance is still alive (PID file %s exists) after several termination attempts! Killing other driver!", pidfnbuf); + if (sendsignalfn(pidfnbuf, SIGKILL) == 0) { + sleep(5); + if (sendsignalfn(pidfnbuf, 0) == 0) { + upslogx(LOG_WARNING, "Duplicate driver instance is still alive (could signal the process)"); + /* TODO: Should we writepid() below in this case? + * Or if driver init fails, restore the old content + * for that running sibling? */ + } else { + upslogx(LOG_WARNING, "Could not signal the other driver after kill, either its process is finally dead or owned by another user!"); + } + } else { + upslogx(LOG_WARNING, "Could not signal the other driver, either its process is dead or owned by another user!"); + } + /* Note: PID file would remain here, but invalid + * as far as further killers would be concerned */ + } + } + /* Only write pid if we're not just dumping data, for discovery */ if (!dump_data) { - pidfn = xstrdup(buffer); + pidfn = xstrdup(pidfnbuf); writepid(pidfn); /* before backgrounding */ } } +#else /* WIN32 */ + char name[SMALLBUF]; + + snprintf(name,sizeof(name), "%s-%s",progname,upsname); + + if (cmd) { +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ +/* Should not really get here since cmd would remain 0 until WIN32 support is implemented */ + fatalx(EXIT_FAILURE, "Signal support not implemented for this platform"); + } + + mutex = CreateMutex(NULL,TRUE,name); + if(mutex == NULL ) { + if( GetLastError() != ERROR_ACCESS_DENIED ) { + fatalx(EXIT_FAILURE, "Can not create mutex %s : %d.\n",name,(int)GetLastError()); + } + } + + if (GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED) { + upslogx(LOG_WARNING, "Duplicate driver instance detected! Terminating other driver!"); + for(i=0;i<10;i++) { + DWORD res; + sendsignal(name, COMMAND_STOP); + if(mutex != NULL ) { + res = WaitForSingleObject(mutex,1000); + if(res==WAIT_OBJECT_0) { + break; + } + } + else { + sleep(1); + mutex = CreateMutex(NULL,TRUE,name); + if(mutex != NULL ) { + break; + } + } + } + if(i >= 10 ) { + fatalx(EXIT_FAILURE, "Can not terminate the previous driver.\n"); + } + } +#endif /* WIN32 */ /* clear out callback handler data */ memset(&upsh, '\0', sizeof(upsh)); @@ -687,10 +2231,12 @@ int main(int argc, char **argv) * when its a pdu! */ dstate_setinfo("device.type", "ups"); + dstate_setinfo("driver.state", "init.device"); upsdrv_initups(); + dstate_setinfo("driver.state", "init.quiet"); /* UPS is detected now, cleanup upon exit */ - atexit(upsdrv_cleanup); + atexit(exit_upsdrv_cleanup); /* now see if things are very wrong out there */ if (upsdrv_info.status == DRV_BROKEN) { @@ -714,8 +2260,13 @@ int main(int argc, char **argv) syslogbit_set(); /* get the base data established before allowing connections */ + dstate_setinfo("driver.state", "init.info"); upsdrv_initinfo(); + /* Note: a few drivers also call their upsdrv_updateinfo() during + * their upsdrv_initinfo(), possibly to impact the initialization */ + dstate_setinfo("driver.state", "init.updateinfo"); upsdrv_updateinfo(); + dstate_setinfo("driver.state", "init.quiet"); if (dstate_getinfo("driver.flag.ignorelb")) { int have_lb_method = 0; @@ -741,15 +2292,137 @@ 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) + ) { +#ifndef WIN32 + int allOk = 1; + /* Use file descriptor, not name, to first check and then manipulate permissions: + * https://cwe.mitre.org/data/definitions/367.html + * https://wiki.sei.cmu.edu/confluence/display/c/FIO01-C.+Be+careful+using+functions+that+use+file+names+for+identification + * Alas, Unix sockets on most systems can not be open()ed + * so there is no file descriptor to manipulate. + * Fall back to name-based "les secure" operations then. + */ + TYPE_FD fd = ERROR_FD; + + /* 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' (%i): %s", + group, errno, strerror(errno) + ); + allOk = 0; + goto sockname_ownership_finished; + } else { + struct stat statbuf; + mode_t mode; + + if (INVALID_FD((fd = open(sockname, O_RDWR | O_APPEND)))) { + upsdebugx(1, "WARNING: opening socket file for stat/chown failed " + "(%i), which is rather typical for Unix socket handling: %s", + errno, strerror(errno) + ); + allOk = 0; + } + + if ((VALID_FD(fd) && fstat(fd, &statbuf)) + || (INVALID_FD(fd) && stat(sockname, &statbuf)) + ) { + upsdebugx(1, "WARNING: stat for chown of socket file failed (%i): %s", + errno, strerror(errno) + ); + allOk = 0; + if (INVALID_FD(fd)) { + /* Can not proceed with ops below */ + goto sockname_ownership_finished; + } + } else { + /* Maybe open() and some stat() succeeed so far */ + allOk = 1; + /* Here we do a portable chgrp() essentially: */ + if ((VALID_FD(fd) && fchown(fd, statbuf.st_uid, grp->gr_gid)) + || (INVALID_FD(fd) && chown(sockname, statbuf.st_uid, grp->gr_gid)) + ) { + upsdebugx(1, "WARNING: chown of socket file failed (%i): %s", + errno, strerror(errno) + ); + allOk = 0; + } + } + + /* Refresh file info */ + if ((VALID_FD(fd) && fstat(fd, &statbuf)) + || (INVALID_FD(fd) && stat(sockname, &statbuf)) + ) { + /* Logically we'd fail chown above if file + * does not exist or is not accessible */ + upsdebugx(1, "WARNING: stat for chmod of socket file failed (%i): %s", + errno, strerror(errno) + ); + allOk = 0; + } else { + /* chmod g+rw sockname */ + mode = statbuf.st_mode; + mode |= S_IWGRP; + mode |= S_IRGRP; + if ((VALID_FD(fd) && fchmod(fd, mode)) + || (INVALID_FD(fd) && chmod(sockname, mode)) + ) { + upsdebugx(1, "WARNING: chmod of socket file failed (%i): %s", + errno, strerror(errno) + ); + allOk = 0; + } + } + } + +sockname_ownership_finished: + if (allOk) { + upsdebugx(1, "Group access for this driver successfully fixed " + "(using file %s based methods)", + VALID_FD(fd) ? "descriptor" : "name"); + } 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); + } + + if (VALID_FD(fd)) { + close(fd); + fd = ERROR_FD; + } +#else /* not WIN32 */ + upsdebugx(1, "Options for alternate user/group are not implemented on this platform"); +#endif /* WIN32 */ + } + free(sockname); + } /* The poll_interval may have been changed from the default */ - dstate_setinfo("driver.parameter.pollinterval", "%jd", (intmax_t)poll_interval); + dstate_setinfo("driver.parameter.pollinterval", "%" PRIdMAX, (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,24 +2432,75 @@ 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) ) { - background(); - writepid(pidfn); /* PID changes when backgrounding */ + switch (foreground) { + case 0: + background(); + /* We had saved a PID before backgrounding, but + * it changes when backgrounding - so save again + */ + writepid(pidfn); + break; + + /* >0: Keep the initial PID; don't care about "!dump_data" here + * currently: let users figure out their mess (or neat hacks) + */ + case 2: + if (!pidfn) { + char pidfnbuf[SMALLBUF]; + snprintf(pidfnbuf, sizeof(pidfnbuf), "%s/%s-%s.pid", altpidpath(), progname, upsname); + pidfn = xstrdup(pidfnbuf); + } + upslogx(LOG_WARNING, "Running as foreground process, but saving a PID file anyway"); + writepid(pidfn); + break; + + default: + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); } - while (!exit_flag) { + dstate_setinfo("driver.flag.allow_killpower", "0"); + dstate_setflags("driver.flag.allow_killpower", ST_FLAG_RW | ST_FLAG_NUMBER); + dstate_addcmd("driver.killpower"); + +#ifndef WIN32 +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ + dstate_addcmd("driver.reload"); + dstate_addcmd("driver.reload-or-exit"); +# ifndef DRIVERS_MAIN_WITHOUT_MAIN + dstate_addcmd("driver.reload-or-error"); +# endif +# ifdef SIGCMD_RELOAD_OR_RESTART + dstate_addcmd("driver.reload-or-restart"); +# endif +#endif + dstate_setinfo("driver.state", "quiet"); + if (dump_data) { + upsdebugx(1, "Driver initialization completed, beginning data dump (%d loops)", dump_data); + } else { + upsdebugx(1, "Driver initialization completed, beginning regular infinite loop"); + upsnotify(NOTIFY_STATE_READY_WITH_PID, NULL); + } + + while (!exit_flag) { struct timeval timeout; + if (!dump_data) { + upsnotify(NOTIFY_STATE_WATCHDOG, NULL); + } + gettimeofday(&timeout, NULL); timeout.tv_sec += poll_interval; + dstate_setinfo("driver.state", "updateinfo"); upsdrv_updateinfo(); + dstate_setinfo("driver.state", "quiet"); /* Dump the data tree (in upsc-like format) to stdout and exit */ if (dump_data) { /* Wait for 'dump_data' update loops to ensure data completion */ if (update_count == dump_data) { + dstate_setinfo("driver.state", "dumping"); dstate_dump(); exit_flag = 1; } @@ -786,14 +2510,20 @@ int main(int argc, char **argv) else { while (!dstate_poll_fds(timeout, extrafd) && !exit_flag) { /* repeat until time is up or extrafd has data */ + handle_reload_flag(); } } + + handle_reload_flag(); } /* if we get here, the exit flag was set by a signal handler */ /* however, avoid to "pollute" data dump output! */ - if (!dump_data) + if (!dump_data) { upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); + upsnotify(NOTIFY_STATE_STOPPING, "Signal %d: exiting", exit_flag); + } - exit(EXIT_SUCCESS); + exit(exit_flag == -1 ? EXIT_FAILURE : EXIT_SUCCESS); } +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ diff --git a/drivers/main.h b/drivers/main.h index 523d908304..1658ae3491 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -3,13 +3,18 @@ #include "common.h" #include "upsconf.h" +#include "upshandler.h" #include "dstate.h" #include "extstate.h" +#ifdef WIN32 +#include "wincompat.h" +#endif /* public functions & variables from main.c */ extern const char *progname, *upsname, *device_name; extern char *device_path; -extern int upsfd, extrafd, broken_driver, experimental_driver, do_lock_port, exit_flag; +extern int broken_driver, experimental_driver, do_lock_port, exit_flag; +extern TYPE_FD upsfd, extrafd; extern time_t poll_interval; /* functions & variables required in each driver */ @@ -21,8 +26,20 @@ void upsdrv_help(void); /* tack on anything useful for the -h text */ void upsdrv_banner(void); /* print your version information */ void upsdrv_cleanup(void); /* free any resources before shutdown */ +void set_exit_flag(int sig); + /* --- details for the variable/value sharing --- */ +/* handle instant commands common for all drivers + * (returns STAT_INSTCMD_* state values per enum in upshandler.h) + */ +int main_instcmd(const char *cmdname, const char *extra, conn_t *conn); + +/* handle setting variables common for all drivers + * (returns STAT_SET_* state values per enum in upshandler.h) + */ +int main_setvar(const char *varname, const char *val, conn_t *conn); + /* main calls this driver function - it needs to call addvar */ void upsdrv_makevartable(void); @@ -39,6 +56,7 @@ typedef struct vartab_s { char *val; /* right side of = */ char *desc; /* 40 character description for -h text */ int found; /* set once encountered, for testvar() */ + int reloadable; /* driver reload may redefine this value */ struct vartab_s *next; } vartab_t; @@ -50,6 +68,31 @@ typedef struct vartab_s { /* callback from driver - create the table for future -x entries */ void addvar(int vartype, const char *name, const char *desc); +void addvar_reloadable(int vartype, const char *name, const char *desc); + +/* Several helpers for driver configuration reloading follow: + * * testval_reloadable() checks if we are currently reloading (or initially + * loading) the configuration, and if strings oldval==newval or not, + * e.g. for values saved straight into driver source code variables; + * * testinfo_reloadable() checks this for a name saved as dstate_setinfo(); + * * testvar_reloadable() checks in vartab_t list as maintained by addvar(). + * + * All these methods check if value can be (re-)loaded now: + * * either it is reloadable by argument or vartab_t definition, + * * or no value has been saved into it yet (e.g. is NULL), + * * or we are handling initial loading and keep legacy behavior of trusting + * the inputs (e.g. some values may be defined as defaults in global section + * and tuned in a driver section). + * + * Return values: + * * -1 -- if nothing needs to be done and that is not a failure + * (e.g. value not modified so we do not care if we may change it or not); + * * 0 -- if can not modify this value (but it did change in config); + * * 1 -- if we can and should apply a new (maybe initial) value. + */ +int testvar_reloadable(const char *var, const char *val, int vartype); +int testval_reloadable(const char *var, const char *oldval, const char *newval, int reloadable); +int testinfo_reloadable(const char *var, const char *infoname, const char *newval, int reloadable); /* subdriver description structure */ typedef struct upsdrv_info_s { @@ -76,4 +119,41 @@ typedef struct upsdrv_info_s { /* public driver information from the driver file */ extern upsdrv_info_t upsdrv_info; +/* functions and data possibly used via libdummy_mockdrv.la for unit-tests */ +#ifdef DRIVERS_MAIN_WITHOUT_MAIN +extern vartab_t *vartab_h; +void dparam_setinfo(const char *var, const char *val); +void storeval(const char *var, char *val); +void vartab_free(void); +void setup_signals(void); +#endif /* DRIVERS_MAIN_WITHOUT_MAIN */ + +#ifndef WIN32 +# define SIGCMD_RELOAD SIGHUP +/* not a signal, so negative; relies on socket protocol */ +# define SIGCMD_RELOAD_OR_ERROR -SIGCMD_RELOAD +# define SIGCMD_RELOAD_OR_EXIT SIGUSR1 +/* // FIXME: Implement this self-recycling in drivers (keeping the PID): +# define SIGCMD_RELOAD_OR_RESTART SIGUSR2 +*/ + +/* This is commonly defined on systems we know; file bugs/PRs for + * relevant systems where it is not present (SIGWINCH might be an + * option there, though terminal resizes might cause braindumps). + * Their packaging may want to add a patch for this bit (and docs). + */ +# if (defined SIGURG) +# define SIGCMD_DATA_DUMP SIGURG +# else +# if (defined SIGWINCH) +# define SIGCMD_DATA_DUMP SIGWINCH +# else +# pragma warn "This OS lacks SIGURG and SIGWINCH, will not handle SIGCMD_DATA_DUMP" +# endif +# endif +#else +/* FIXME: handle WIN32 builds for other signals too */ +# define SIGCMD_RELOAD_OR_ERROR "driver.reload-or-error" +#endif /* WIN32 */ + #endif /* NUT_MAIN_H_SEEN */ diff --git a/drivers/masterguard.c b/drivers/masterguard.c index 89609ba2e7..37cae77651 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 @@ -21,9 +26,10 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #define DRIVER_NAME "MASTERGUARD UPS driver" -#define DRIVER_VERSION "0.25" +#define DRIVER_VERSION "0.26" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -421,7 +427,7 @@ static ssize_t ups_ident( void ) else if( ret > 0 ) { if( DEBUG ) - printf( "WH says <%s> with length %zi\n", buf, ret ); + printf( "WH says <%s> with length %" PRIiSIZE "\n", buf, ret ); upslog_with_errno( LOG_INFO, "New WH String found. Please report to maintainer\n" ); } @@ -502,7 +508,7 @@ void upsdrv_updateinfo(void) if( ret != lenRSP ) { if( DEBUG ) - printf( "buf = %s len = %zi\n", buf, ret ); + printf( "buf = %s len = %" PRIiSIZE "\n", buf, ret ); upslog_with_errno( LOG_ERR, "Error in UPS response " ); dstate_datastale(); return; @@ -562,6 +568,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/metasys.c b/drivers/metasys.c index 9c307adec1..e5003b116f 100644 --- a/drivers/metasys.c +++ b/drivers/metasys.c @@ -28,7 +28,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Metasystem UPS driver" -#define DRIVER_VERSION "0.08" +#define DRIVER_VERSION "0.09" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -189,7 +189,7 @@ static int get_answer(unsigned char *data) { /* Read STX byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res < 1) { - ser_comm_fail("Receive error (STX): %zd!!!\n", res); + ser_comm_fail("Receive error (STX): %" PRIiSIZE "!!!\n", res); return -1; } if (my_buf[0] != 0x02) { @@ -199,7 +199,7 @@ static int get_answer(unsigned char *data) { /* Read data length byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res < 1) { - ser_comm_fail("Receive error (length): %zd!!!\n", res); + ser_comm_fail("Receive error (length): %" PRIiSIZE "!!!\n", res); return -1; } packet_length = my_buf[0]; @@ -210,7 +210,7 @@ static int get_answer(unsigned char *data) { /* Try to read all the remainig bytes (packet_length) */ res = ser_get_buf_len(upsfd, my_buf, packet_length, 1, 0); if (res != packet_length) { - ser_comm_fail("Receive error (data): got %zd bytes instead of %d!!!\n", res, packet_length); + ser_comm_fail("Receive error (data): got %" PRIiSIZE " bytes instead of %d!!!\n", res, packet_length); return -1; } diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index d30f7412bf..f421fc3281 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -37,8 +37,20 @@ #include "usbhid-ups.h" #include "mge-hid.h" #include "nut_float.h" +#include "timehead.h" + +#ifdef WIN32 +# include "wincompat.h" +# ifndef LDOUBLE +# ifdef HAVE_LONG_DOUBLE +# define LDOUBLE long double +# else +# define LDOUBLE double +# endif +# endif +#endif -#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,13 +68,15 @@ /* AEG */ #define AEG_VENDORID 0x2b2d +/* Note that normally this VID is handled by Liebert/Phoenixtec HID mapping, + * here it is just for for AEG PROTECT NAS devices: */ /* Phoenixtec Power Co., Ltd */ #define PHOENIXTEC 0x06da /* IBM */ #define IBM_VENDORID 0x04b3 -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) #include "usb-common.h" /* USB IDs device table */ @@ -95,7 +109,7 @@ static usb_device_id_t mge_usb_device_table[] = { /* Terminating entry */ { 0, 0, NULL } }; -#endif +#endif /* !SHUT_MODE => USB */ typedef enum { MGE_DEFAULT_OFFLINE = 0, @@ -166,12 +180,12 @@ static char mge_scratch_buf[20]; * float mode is not important from the software's perspective, it's there to * help determine if the charger is advancing correctly. * So in float mode, the charger is charging the battery, so by definition you - * can assert the CHRG flag in NUT when in ā€œfloatā€ mode or ā€œchargeā€ mode. - * When in ā€œrestā€ mode the charger is not delivering anything to the battery, + * can assert the CHRG flag in NUT when in "float" mode or "charge" mode. + * When in "rest" mode the charger is not delivering anything to the battery, * but it will when the ABM cycle(28 days) ends, or a battery discharge occurs - * and utility returns. This is when the ABM status should be ā€œrestingā€. + * and utility returns. This is when the ABM status should be "resting". * If a battery failure is detected that disables the charger, it should be - * reporting ā€œoffā€ in the ABM charger status. + * reporting "off" in the ABM charger status. * Of course when delivering load power from the battery, the ABM status is * discharging. */ @@ -183,6 +197,27 @@ static char mge_scratch_buf[20]; /* Internal flag to process battery status (CHRG/DISCHRG) and ABM */ static int advanced_battery_monitoring = ABM_UNKNOWN; +/* TODO: Lifted from strptime.c... maybe should externalize the fallback? + * NOTE: HAVE_DECL_* are always defined, 0 or 1. Many other flags are not. + */ +#if ! HAVE_DECL_ROUND +# ifndef WIN32 +static long round (double value) +# else +static long round (LDOUBLE value) +# endif +{ + long intpart; + + intpart = (long)value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} +#endif /* HAVE_DECL_ROUND */ + /* Used to store internally if ABM is enabled or not */ static const char *eaton_abm_enabled_fun(double value) { @@ -671,7 +706,6 @@ static info_lkp_t eaton_check_country_info[] = { * compute a realpower approximation using available data */ static const char *eaton_compute_realpower_fun(double value) { - NUT_UNUSED_VARIABLE(value); const char *str_ups_load = dstate_getinfo("ups.load"); const char *str_power_nominal = dstate_getinfo("ups.power.nominal"); const char *str_powerfactor = dstate_getinfo("output.powerfactor"); @@ -679,12 +713,14 @@ static const char *eaton_compute_realpower_fun(double value) int power_nominal = 0; int ups_load = 0; double realpower = 0; + NUT_UNUSED_VARIABLE(value); + if (str_power_nominal && str_ups_load) { /* Extract needed values */ ups_load = atoi(str_ups_load); power_nominal = atoi(str_power_nominal); if (str_powerfactor) - powerfactor = atoi(str_powerfactor); + powerfactor = atof(str_powerfactor); /* Compute the value */ realpower = round(ups_load * 0.01 * power_nominal * powerfactor); snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.0f", realpower); @@ -1246,6 +1282,10 @@ static hid_info_t mge_hid2nut[] = /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[3].PresentStatus.Used", NULL, NULL, 0, mge_onbatt_info }, +#if 0 + /* NOTE: see entry with eaton_converter_online_info below now */ + { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, online_info }, +#endif /* These 2 ones are used when ABM is disabled */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_discharging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_charging_info }, @@ -1271,6 +1311,7 @@ static hid_info_t mge_hid2nut[] = * and must hence be after "UPS.PowerSummary.PresentStatus.Good" */ { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, eaton_converter_online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[2].PresentStatus.Used", NULL, NULL, 0, bypass_auto_info }, /* Automatic bypass */ + /* NOTE: entry [3] is above as mge_onbatt_info */ { "BOOL", 0, 0, "UPS.PowerConverter.Input.[4].PresentStatus.Used", NULL, NULL, 0, bypass_manual_info }, /* Manual bypass */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", NULL, NULL, 0, fanfail_info }, { "BOOL", 0, 0, "UPS.BatterySystem.Battery.PresentStatus.Present", NULL, NULL, 0, nobattery_info }, @@ -1546,7 +1587,10 @@ static const char *mge_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int mge_claim(HIDDevice_t *hd) { -#ifndef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE + NUT_UNUSED_VARIABLE(hd); + return 1; +#else /* !SHUT_MODE => USB */ int status = is_usb_device_supported(mge_usb_device_table, hd); switch (status) { @@ -1567,6 +1611,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,16 +1636,28 @@ 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: default: return 0; } -#else - NUT_UNUSED_VARIABLE(hd); - return 1; -#endif +#endif /* SHUT_MODE / USB */ } subdriver_t mge_subdriver = { diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index df8af1485a..e4b291ca8f 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -38,325 +38,106 @@ #define MGE_OID_MODEL_NAME MGE_BASE_OID ".1.1.0" static info_lkp_t mge_lowbatt_info[] = { - { 1, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "LB"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_onbatt_info[] = { - { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OB"), + info_lkp_default(2, "OL"), + info_lkp_sentinel }; static info_lkp_t mge_bypass_info[] = { - { 1, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "BYPASS"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_boost_info[] = { - { 1, "BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "BOOST"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_trim_info[] = { - { 1, "TRIM" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "TRIM"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_overload_info[] = { - { 1, "OVER" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OVER"), + info_lkp_default(2, ""), + info_lkp_sentinel }; - static info_lkp_t mge_replacebatt_info[] = { - { 1, "RB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "RB"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_output_util_off_info[] = { - { 1, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "OFF"), + info_lkp_default(2, ""), + info_lkp_sentinel }; static info_lkp_t mge_transfer_reason_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "input voltage out of range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "input frequency out of range" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "utility off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), + info_lkp_default(2, "input voltage out of range"), + info_lkp_default(3, "input frequency out of range"), + info_lkp_default(4, "utility off"), + info_lkp_sentinel }; static info_lkp_t mge_test_result_info[] = { - { 1, "done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "done and warning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "aborted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "in progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "no test initiated" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "done and passed"), + info_lkp_default(2, "done and warning"), + info_lkp_default(3, "done and error"), + info_lkp_default(4, "aborted"), + info_lkp_default(5, "in progress"), + info_lkp_default(6, "no test initiated"), + info_lkp_sentinel }; static info_lkp_t mge_beeper_status_info[] = { - { 1, "disabled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "enabled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "muted" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "disabled"), + info_lkp_default(2, "enabled"), + info_lkp_default(3, "muted"), + info_lkp_sentinel }; static info_lkp_t mge_yes_no_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel }; /* FIXME: the below may introduce status redundancy, that needs to be * addressed by the driver, as for usbhid-ups! */ static info_lkp_t mge_power_source_info[] = { - { 1, "" /* other */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OFF" /* none */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, + info_lkp_default(1, ""), /* other */ + info_lkp_default(2, "OFF"), /* none */ + #if 0 - { 3, "OL" /* normal */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -#endif - { 4, "BYPASS" /* bypass */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "OB" /* battery */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "BOOST" /* booster */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "TRIM" /* reducer */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL + info_lkp_default(3, "OL"), /* normal */ #endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + + info_lkp_default(4, "BYPASS"), /* bypass */ + info_lkp_default(5, "OB"), /* battery */ + info_lkp_default(6, "BOOST"), /* booster */ + info_lkp_default(7, "TRIM"), /* reducer */ + info_lkp_sentinel }; static info_lkp_t mge_ambient_drycontacts_info[] = { - { -1, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "opened" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unknown"), + info_lkp_default(1, "closed"), + info_lkp_default(2, "opened"), + info_lkp_sentinel }; /* Parameters default values */ @@ -374,35 +155,40 @@ static info_lkp_t mge_ambient_drycontacts_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t mge_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.12.12.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_1, NULL }, - { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.9.8.0", "", 0, mge_beeper_status_info }, - { "ups.L1.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_3, NULL }, - { "ups.L2.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.2", "", SU_OUTPUT_3, NULL }, - { "ups.L3.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.3", "", SU_OUTPUT_3, NULL }, - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, mge_test_result_info }, - { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.timer.shutdown", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", "", SU_FLAG_OK, NULL }, - { "ups.timer.start", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", "", SU_FLAG_OK, NULL }, - { "ups.timer.reboot", 0, 1, "1.3.6.1.2.1.33.1.8.4.0", "", SU_FLAG_OK, NULL }, - { "ups.start.auto", ST_FLAG_RW, 1, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, mge_yes_no_info }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("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), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.12.12.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_1, NULL), + snmp_info_default("ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.9.8.0", "", 0, mge_beeper_status_info), + snmp_info_default("ups.L1.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.L2.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.L3.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, mge_test_result_info), + snmp_info_default("ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.timer.shutdown", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.timer.start", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.timer.reboot", 0, 1, "1.3.6.1.2.1.33.1.8.4.0", "", SU_FLAG_OK, NULL), + snmp_info_default("ups.start.auto", ST_FLAG_RW, 1, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, mge_yes_no_info), /* status data */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.11.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_replacebatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.14.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.16.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_onbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.4.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_bypass_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.7.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_output_util_off_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.8.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_boost_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.10.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_overload_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.12.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_trim_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.4.1.0", "", SU_STATUS_PWR | SU_FLAG_OK, mge_power_source_info }, + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.11.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_replacebatt_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.14.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.16.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_onbatt_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.4.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_bypass_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.7.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_output_util_off_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.8.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_boost_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.10.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_overload_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.12.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_trim_info), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.4.1.0", "", SU_STATUS_PWR | SU_FLAG_OK, mge_power_source_info), /* FIXME: Alarms * - upsmgBatteryChargerFault (.1.3.6.1.4.1.705.1.5.15.0), yes (1), no (2) @@ -418,87 +204,89 @@ static snmp_info_t mge_mib[] = { */ /* Input page */ - { "input.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.6.1.0", "", 0, NULL }, - { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.3", "", SU_INPUT_3, NULL }, - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_1, NULL }, - { "input.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_3, NULL }, - { "input.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.2", "", SU_INPUT_3, NULL }, - { "input.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.3", "", SU_INPUT_3, NULL }, - { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.3", "", SU_INPUT_3, NULL }, - { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.3", "", SU_INPUT_3, NULL }, - { "input.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_1, NULL }, - { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_3, NULL }, - { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.2", "", SU_INPUT_3, NULL }, - { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.3", "", SU_INPUT_3, NULL }, - { "input.transfer.reason", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.6.4.0", "", SU_FLAG_OK, mge_transfer_reason_info }, + snmp_info_default("input.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.6.1.0", "", 0, NULL), + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_1, NULL), + snmp_info_default("input.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_1, NULL), + snmp_info_default("input.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.2", "", SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.3", "", SU_INPUT_3, NULL), + snmp_info_default("input.transfer.reason", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.6.4.0", "", SU_FLAG_OK, mge_transfer_reason_info), /* Output page */ - { "output.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.7.1.0", "", 0, NULL }, - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_1, NULL }, - { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.2", "", SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.3", "", SU_OUTPUT_3, NULL }, - { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_1, NULL }, - { "output.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.3", "", SU_OUTPUT_3, NULL }, - { "output.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_1, NULL }, - { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.3", "", SU_OUTPUT_3, NULL }, + snmp_info_default("output.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.7.1.0", "", 0, NULL), + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_1, NULL), + snmp_info_default("output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_1, NULL), + snmp_info_default("output.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.3", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_1, NULL), + snmp_info_default("output.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.2", "", SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.3", "", SU_OUTPUT_3, NULL), /* Battery page */ - { "battery.charge", 0, 1, ".1.3.6.1.4.1.705.1.5.2.0", "", SU_FLAG_OK, NULL }, - { "battery.runtime", 0, 1, ".1.3.6.1.4.1.705.1.5.1.0", "", SU_FLAG_OK, NULL }, - { "battery.runtime.low", 0, 1, ".1.3.6.1.4.1.705.1.4.7.0", "", SU_FLAG_OK, NULL }, - { "battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW, 2, ".1.3.6.1.4.1.705.1.4.8.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.5.5.0", "", SU_FLAG_OK, NULL }, + snmp_info_default("battery.charge", 0, 1, ".1.3.6.1.4.1.705.1.5.2.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime", 0, 1, ".1.3.6.1.4.1.705.1.5.1.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime.low", 0, 1, ".1.3.6.1.4.1.705.1.4.7.0", "", SU_FLAG_OK, NULL), + snmp_info_default("battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW, 2, ".1.3.6.1.4.1.705.1.4.8.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), + snmp_info_default("battery.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.5.5.0", "", SU_FLAG_OK, NULL), /* Ambient page: Environment Sensor (ref 66 846) */ - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), + snmp_info_default("ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsmgEnvironmentInput1State.1 */ - { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.9.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, + snmp_info_default("ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.9.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info), /* upsmgEnvironmentInput1State.1 */ - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.10.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, + snmp_info_default("ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.10.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info), /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "Main Outlet", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "Main Outlet", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* instant commands. */ - { "test.battery.start", 0, 1, ".1.3.6.1.4.1.705.1.10.4.0", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + snmp_info_default("test.battery.start", 0, 1, ".1.3.6.1.4.1.705.1.10.4.0", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL), /* Also use IETF OIDs - * { "test.battery.stop", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.2", SU_TYPE_CMD, NULL }, - * { "test.battery.start", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.3", SU_TYPE_CMD, NULL }, - * { "test.battery.start.quick", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.4", SU_TYPE_CMD, NULL }, - * { "test.battery.start.deep", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.5", SU_TYPE_CMD, NULL }, + * snmp_info_default("test.battery.stop", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.2", SU_TYPE_CMD, NULL), + * snmp_info_default("test.battery.start", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.3", SU_TYPE_CMD, NULL), + * snmp_info_default("test.battery.start.quick", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.4", SU_TYPE_CMD, NULL), + * snmp_info_default("test.battery.start.deep", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.5", SU_TYPE_CMD, NULL), */ - /* { "load.off", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.6.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, - * { "load.on", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.3.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, - * { "shutdown.return", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.9.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* snmp_info_default("load.off", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.6.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL), + * snmp_info_default("load.on", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.3.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL), + * snmp_info_default("shutdown.return", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.9.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL), */ /* IETF MIB fallback */ - { "beeper.disable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "1", SU_TYPE_CMD, NULL }, - { "beeper.enable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "2", SU_TYPE_CMD, NULL }, - { "beeper.mute", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "3", SU_TYPE_CMD, NULL }, - /*{ "load.off", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL }, - { "load.on", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL },*/ - { "load.off.delay", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", NULL, SU_TYPE_CMD, NULL }, - { "load.on.delay", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", NULL, SU_TYPE_CMD, NULL }, + snmp_info_default("beeper.disable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "1", SU_TYPE_CMD, NULL), + snmp_info_default("beeper.enable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "2", SU_TYPE_CMD, NULL), + snmp_info_default("beeper.mute", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "3", SU_TYPE_CMD, NULL), +/* + snmp_info_default("load.off", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL), + snmp_info_default("load.on", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL), +*/ + snmp_info_default("load.off.delay", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", NULL, SU_TYPE_CMD, NULL), + snmp_info_default("load.on.delay", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", NULL, SU_TYPE_CMD, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t mge = { "mge", MGE_MIB_VERSION, NULL, MGE_OID_MODEL_NAME, mge_mib, MGE_SYSOID, NULL }; diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 19c36616e5..9e34954199 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -55,18 +55,21 @@ #include "config.h" /* must be the first header */ #include +#ifndef WIN32 #include +#endif #include "timehead.h" #include "main.h" #include "serial.h" #include "mge-utalk.h" +#include "nut_stdint.h" /* --------------------------------------------------------------- */ /* Define "technical" constants */ /* --------------------------------------------------------------- */ #define DRIVER_NAME "MGE UPS SYSTEMS/U-Talk driver" -#define DRIVER_VERSION "0.94" +#define DRIVER_VERSION "0.95" /* driver description structure */ @@ -164,17 +167,28 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { char buf[BUFFLEN]; +#ifndef WIN32 int RTS = TIOCM_RTS; +#endif upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* read command line/conf variable that affect comm. */ +#ifndef WIN32 if (testvar ("oldmac")) RTS = ~TIOCM_RTS; /* Init serial line */ ioctl(upsfd, TIOCMBIC, &RTS); +#else + if (testvar ("oldmac")) { + EscapeCommFunction(((serial_handler_t *)upsfd)->handle,CLRRTS); + } + else { + EscapeCommFunction(((serial_handler_t *)upsfd)->handle,SETRTS); + } +#endif enable_ups_comm(); /* Try to set "Low Battery Level" (if supported and given) */ @@ -908,7 +922,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 +936,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); @@ -942,7 +956,9 @@ static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) bytes_rcvd = ser_get_line(upsfd, reply, replylen, MGE_REPLY_ENDCHAR, MGE_REPLY_IGNCHAR, 3, 0); - upsdebugx(4, "mge_command: received %zd byte(s)", bytes_rcvd); + upsdebugx(4, "mge_command: sent %" PRIiSIZE + ", received %" PRIiSIZE " byte(s)", + bytes_sent, bytes_rcvd); return bytes_rcvd; } diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 487f78cb1e..42eab857c7 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,11 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.32" +#ifdef WIN32 +#include "wincompat.h" +#endif + +#define MGE_XML_VERSION "MGEXML/0.36" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -544,7 +548,7 @@ static const char *mge_sensitivity_info(const char *arg_val) static const char *mge_test_result_info(const char *arg_val) { - STATUS_CLR(CAL); + STATUS_CLR(CALIB); switch (atoi(arg_val)) { case 1: @@ -556,7 +560,7 @@ static const char *mge_test_result_info(const char *arg_val) case 4: return "aborted"; case 5: - STATUS_SET(CAL); + STATUS_SET(CALIB); return "in progress"; case 6: return "no test initiated"; @@ -577,7 +581,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 +589,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 +600,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 ) { @@ -1535,10 +1537,10 @@ subdriver_t mge_xml_subdriver = { }; const char *vname_nut2mge_xml(const char *name) { - assert(NULL != name); - size_t i = 0; + assert(NULL != name); + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; @@ -1551,10 +1553,10 @@ const char *vname_nut2mge_xml(const char *name) { } const char *vname_mge_xml2nut(const char *name) { - assert(NULL != name); - size_t i = 0; + assert(NULL != name); + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; @@ -1567,10 +1569,10 @@ const char *vname_mge_xml2nut(const char *name) { } char *vvalue_mge_xml2nut(const char *name, const char *value, size_t len) { - assert(NULL != name); - size_t i = 0; + assert(NULL != name); + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { xml_info_t *info = mge_xml2nut + i; diff --git a/drivers/microdowell.c b/drivers/microdowell.c index 846764e84b..9b2637ea81 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -28,7 +28,9 @@ #include "main.h" #include "serial.h" +#ifndef WIN32 #include +#endif #include "timehead.h" #include "nut_stdint.h" @@ -42,7 +44,7 @@ #define MAX_SHUTDOWN_DELAY_LEN 5 #define DRIVER_NAME "MICRODOWELL UPS driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/microsol-apc.c b/drivers/microsol-apc.c index 20e0cd3466..329263703f 100644 --- a/drivers/microsol-apc.c +++ b/drivers/microsol-apc.c @@ -35,7 +35,7 @@ #include "microsol-apc.h" #define DRIVER_NAME "APC Back-UPS BR series UPS driver" -#define DRIVER_VERSION "0.69" +#define DRIVER_VERSION "0.70" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/microsol-common.c b/drivers/microsol-common.c index 84ed6feb43..2ba496b36f 100644 --- a/drivers/microsol-common.c +++ b/drivers/microsol-common.c @@ -575,7 +575,7 @@ static void get_base_info(void) upsdebugx(4, "%s: requesting %d bytes from ser_get_buf_len()", __func__, PACKET_SIZE); tam = ser_get_buf_len(upsfd, packet, PACKET_SIZE, 3, 0); - upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + upsdebugx(2, "%s: received %" PRIiSIZE " bytes from ser_get_buf_len()", __func__, tam); if (tam > 0 && nut_debug_level >= 4) { upsdebug_hex(4, "received from ser_get_buf_len()", packet, (size_t)tam); } @@ -639,7 +639,7 @@ static void get_updated_info(void) upsdebugx(3, "%s: requesting %d bytes from ser_get_buf_len()", __func__, PACKET_SIZE); tam = ser_get_buf_len(upsfd, temp, PACKET_SIZE, 3, 0); - upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + upsdebugx(2, "%s: received %" PRIiSIZE " bytes from ser_get_buf_len()", __func__, tam); if (tam > 0 && nut_debug_level >= 4) upsdebug_hex(4, "received from ser_get_buf_len()", temp, (size_t)tam); diff --git a/drivers/netvision-mib.c b/drivers/netvision-mib.c index e49057f4d3..ec9ae86c0b 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" @@ -39,55 +39,19 @@ /* UPS Battery */ #define NETVISION_OID_BATTERYSTATUS ".1.3.6.1.4.1.4555.1.1.1.1.2.1.0" static info_lkp_t netvision_batt_info[] = { - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery normal */ - { 3, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery low */ - { 4, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery depleted */ - { 5, "DISCHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery discharging */ - { 6, "RB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* battery failure */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(2, ""), /* battery normal */ + info_lkp_default(3, "LB"), /* battery low */ + info_lkp_default(4, "LB"), /* battery depleted */ + info_lkp_default(5, "DISCHRG"), /* battery discharging */ + info_lkp_default(6, "RB"), /* battery failure */ + info_lkp_sentinel }; /* Battery status: upsAlarmOnBattery */ static info_lkp_t netvision_onbatt_info[] = { - { 0, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Online */ - { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* On battery */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(0, "OL"), /* Online */ + info_lkp_default(1, "OB"), /* On battery */ + info_lkp_sentinel }; #define NETVISION_OID_SECONDSONBATTERY ".1.3.6.1.4.1.4555.1.1.1.1.2.2.0" @@ -137,124 +101,90 @@ static info_lkp_t netvision_onbatt_info[] = { #define NETVISION_OID_CONTROL_SHUTDOWN_DELAY ".1.3.6.1.4.1.4555.1.1.1.1.8.2" static info_lkp_t netvision_output_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source other */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source none */ - { 3, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source normal */ - { 4, "OL BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source bypass */ - { 5, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source battery */ - { 6, "OL BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source booster */ - { 7, "OL TRIM" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source reducer */ - { 8, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source standby */ - { 9, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* output source ecomode */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* output source other */ + info_lkp_default(2, ""), /* output source none */ + info_lkp_default(3, "OL"), /* output source normal */ + info_lkp_default(4, "OL BYPASS"), /* output source bypass */ + info_lkp_default(5, "OB"), /* output source battery */ + info_lkp_default(6, "OL BOOST"), /* output source booster */ + info_lkp_default(7, "OL TRIM"), /* output source reducer */ + info_lkp_default(8, "OL"), /* output source standby */ + info_lkp_default(9, ""), /* output source ecomode */ + info_lkp_sentinel }; /* Snmp2NUT lookup table */ static snmp_info_t netvision_mib[] = { - { "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, - "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTUPSSERIALNUMBER, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTFWVERSION, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_BATTERYSTATUS, "", - SU_FLAG_OK | SU_STATUS_BATT, &netvision_batt_info[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_OUTPUT_SOURCE, "", - SU_FLAG_OK | SU_STATUS_PWR, &netvision_output_info[0] }, + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTAGENTSWVERSION, "SOCOMEC SICON UPS", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTMODEL, + "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTUPSSERIALNUMBER, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTFWVERSION, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_BATTERYSTATUS, "", + SU_FLAG_OK | SU_STATUS_BATT, &netvision_batt_info[0]), + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_OUTPUT_SOURCE, "", + SU_FLAG_OK | SU_STATUS_PWR, &netvision_output_info[0]), /* upsAlarmOnBattery */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4555.1.1.1.1.6.3.2.0", "", - SU_FLAG_OK | SU_STATUS_PWR, &netvision_onbatt_info[0] }, + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4555.1.1.1.1.6.3.2.0", "", + SU_FLAG_OK | SU_STATUS_PWR, &netvision_onbatt_info[0]), /* ups load */ - { "ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_INPUT_1, NULL }, + snmp_info_default("ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_INPUT_1, NULL), /*ups input,output voltage, output frquency phase 1 */ - { "input.phases", 0, 1.0, NETVISION_OID_INPUT_NUM_LINES, NULL, 0, NULL }, - { "input.frequency", 0, 0.1, NETVISION_OID_INPUT_FREQ, NULL, SU_FLAG_OK, NULL }, - { "input.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_1, NULL }, - { "input.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_1, NULL }, - { "input.L1-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_3, NULL }, - { "input.L1.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P2, NULL, SU_INPUT_3, NULL }, - { "input.L2.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P2, NULL, SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P3, NULL, SU_INPUT_3, NULL }, - { "input.L3.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P3, NULL, SU_INPUT_3, NULL }, - - { "output.phases", 0, 1.0, NETVISION_OID_OUTPUT_NUM_LINES, NULL, 0, NULL }, - { "output.frequency", 0, 0.1, NETVISION_OID_OUTPUT_FREQ, NULL, SU_FLAG_OK, NULL }, - { "output.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_1, NULL }, - { "output.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_1, NULL }, - { "output.load", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_1, NULL }, - { "output.L1-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_3, NULL }, - { "output.L1.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_3, NULL }, - { "output.L1.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P2, NULL, SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P2, NULL, SU_OUTPUT_3, NULL }, - { "output.L2.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P2, NULL, SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P3, NULL, SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P3, NULL, SU_OUTPUT_3, NULL }, - { "output.L3.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P3, NULL, SU_OUTPUT_3, NULL }, - - { "input.bypass.phases", 0, 1.0, NETVISION_OID_BYPASS_NUM_LINES, NULL, 0, NULL }, - { "input.bypass.frequency", 0, 0.1, NETVISION_OID_BYPASS_FREQ, NULL, SU_FLAG_OK, NULL }, - { "input.bypass.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_1, NULL }, - { "input.bypass.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_1, NULL }, - { "input.bypass.L1-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_3, NULL }, - { "input.bypass.L1.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_3, NULL }, - { "input.bypass.L2-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P2, NULL, SU_BYPASS_3, NULL }, - { "input.bypass.L2.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P2, NULL, SU_BYPASS_3, NULL }, - { "input.bypass.L3-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P3, NULL, SU_BYPASS_3, NULL }, - { "input.bypass.L3.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P3, NULL, SU_BYPASS_3, NULL }, + snmp_info_default("input.phases", 0, 1.0, NETVISION_OID_INPUT_NUM_LINES, NULL, 0, NULL), + snmp_info_default("input.frequency", 0, 0.1, NETVISION_OID_INPUT_FREQ, NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_1, NULL), + snmp_info_default("input.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_1, NULL), + snmp_info_default("input.L1-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_3, NULL), + snmp_info_default("input.L1.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_3, NULL), + snmp_info_default("input.L2-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P2, NULL, SU_INPUT_3, NULL), + snmp_info_default("input.L2.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P2, NULL, SU_INPUT_3, NULL), + snmp_info_default("input.L3-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P3, NULL, SU_INPUT_3, NULL), + snmp_info_default("input.L3.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P3, NULL, SU_INPUT_3, NULL), + + snmp_info_default("output.phases", 0, 1.0, NETVISION_OID_OUTPUT_NUM_LINES, NULL, 0, NULL), + snmp_info_default("output.frequency", 0, 0.1, NETVISION_OID_OUTPUT_FREQ, NULL, SU_FLAG_OK, NULL), + snmp_info_default("output.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_1, NULL), + snmp_info_default("output.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_1, NULL), + snmp_info_default("output.load", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_1, NULL), + snmp_info_default("output.L1-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L1.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L2-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P2, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P2, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L2.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P2, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L3-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P3, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P3, NULL, SU_OUTPUT_3, NULL), + snmp_info_default("output.L3.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P3, NULL, SU_OUTPUT_3, NULL), + + snmp_info_default("input.bypass.phases", 0, 1.0, NETVISION_OID_BYPASS_NUM_LINES, NULL, 0, NULL), + snmp_info_default("input.bypass.frequency", 0, 0.1, NETVISION_OID_BYPASS_FREQ, NULL, SU_FLAG_OK, NULL), + snmp_info_default("input.bypass.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_1, NULL), + snmp_info_default("input.bypass.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_1, NULL), + snmp_info_default("input.bypass.L1-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L1.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L2-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P2, NULL, SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L2.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P2, NULL, SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L3-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P3, NULL, SU_BYPASS_3, NULL), + snmp_info_default("input.bypass.L3.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P3, NULL, SU_BYPASS_3, NULL), /* battery info */ - { "battery.charge", 0, 1, NETVISION_OID_BATT_CHARGE, "", SU_FLAG_OK, NULL }, - { "battery.voltage", 0, 0.1, NETVISION_OID_BATT_VOLTS, "", SU_FLAG_OK, NULL }, - { "battery.runtime", 0, 60, NETVISION_OID_BATT_RUNTIME_REMAINING, "", SU_FLAG_OK, NULL }, + snmp_info_default("battery.charge", 0, 1, NETVISION_OID_BATT_CHARGE, "", SU_FLAG_OK, NULL), + snmp_info_default("battery.voltage", 0, 0.1, NETVISION_OID_BATT_VOLTS, "", SU_FLAG_OK, NULL), + snmp_info_default("battery.runtime", 0, 60, NETVISION_OID_BATT_RUNTIME_REMAINING, "", SU_FLAG_OK, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t netvision = { "netvision", NETVISION_MIB_VERSION, NULL, NETVISION_OID_UPSIDENTMODEL, netvision_mib, NETVISION_SYSOID, NULL }; diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index e741649ffe..cb10c4a038 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -42,11 +42,16 @@ #include "nut_stdint.h" #define DRIVER_NAME "network XML UPS" -#define DRIVER_VERSION "0.44" +#define DRIVER_VERSION "0.45" /** *_OBJECT query multi-part body boundary */ #define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" +#ifdef WIN32 /* FIXME ?? skip alarm handling */ +#define HAVE_NE_SET_CONNECT_TIMEOUT 1 +#define HAVE_NE_SOCK_CONNECT_TIMEOUT 1 +#endif + /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, @@ -232,7 +237,7 @@ uint32_t ups_status = 0; static int timeout = 5; int shutdown_duration = 120; static int shutdown_timer = 0; -int do_convert_deci = 0; /* Legacy MGE-XML conversion from 2000's, not needed in modern firmwares */ +static int do_convert_deci = 0; /* Legacy MGE-XML conversion from 2000's, not needed in modern firmwares */ static time_t lastheard = 0; static subdriver_t *subdriver = &mge_xml_subdriver; static ne_session *session = NULL; @@ -279,7 +284,10 @@ void upsdrv_initinfo(void) dstate_setinfo("driver.version.data", "%s", subdriver->version); if (testvar("subscribe") && (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK)) { +/* TODO: port extrafd to Windows */ +#ifndef WIN32 extrafd = ne_sock_fd(sock); +#endif time(&lastheard); } @@ -315,7 +323,7 @@ void upsdrv_updateinfo(void) /* alarm message received */ ne_xml_parser *parser = ne_xml_create(); - upsdebugx(2, "%s: ne_sock_read(%zd bytes) => %s", __func__, ret, buf); + upsdebugx(2, "%s: ne_sock_read(%" PRIiSIZE " bytes) => %s", __func__, ret, buf); ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); ne_xml_parse(parser, buf, strlen(buf)); ne_xml_destroy(parser); @@ -331,17 +339,23 @@ void upsdrv_updateinfo(void) upslogx(LOG_ERR, "NSM connection with '%s' lost", uri.host); - upsdebugx(2, "%s: ne_sock_read(%zd) => %s", __func__, ret, ne_sock_error(sock)); + upsdebugx(2, "%s: ne_sock_read(%" PRIiSIZE ") => %s", __func__, ret, ne_sock_error(sock)); ne_sock_close(sock); if (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK) { +/* TODO: port extrafd to Windows */ +#ifndef WIN32 extrafd = ne_sock_fd(sock); +#endif time(&lastheard); return; } dstate_datastale(); - extrafd = -1; +/* TODO: port extrafd to Windows */ +#ifndef WIN32 + extrafd = ERROR_FD; +#endif return; } } @@ -438,8 +452,10 @@ void upsdrv_shutdown(void) { if (NULL != resp) object_query_destroy(resp); - if (STAT_SET_HANDLED != status) - fatalx(EXIT_FAILURE, "Shutdown failed: %d", status); + if (STAT_SET_HANDLED != status) { + upslogx(LOG_ERR, "Shutdown failed: %d", status); + set_exit_flag(-1); + } } static int instcmd(const char *cmdname, const char *extra) @@ -643,7 +659,11 @@ void upsdrv_initups(void) /* if debug level is set, direct output to stderr */ if (!nut_debug_level) { +#ifndef WIN32 fp = fopen("/dev/null", "w"); +#else + fp = fopen("nul", "w"); +#endif } else { fp = stderr; } @@ -934,9 +954,8 @@ static int netxml_dispatch_request(ne_request *request, ne_xml_parser *parser) /* Supply the 'login' and 'password' when authentication is required */ static int netxml_authenticate(void *userdata, const char *realm, int attempt, char *username, char *password) { - NUT_UNUSED_VARIABLE(userdata); - char *val; + NUT_UNUSED_VARIABLE(userdata); upsdebugx(2, "%s: realm = [%s], attempt = %d", __func__, realm, attempt); @@ -1042,7 +1061,7 @@ static void netxml_status_set(void) if (STATUS_BIT(SHUTDOWNIMM)) { status_set("FSD"); /* shutdown imminent */ } - if (STATUS_BIT(CAL)) { + if (STATUS_BIT(CALIB)) { status_set("CAL"); /* calibrating */ } } @@ -1218,13 +1237,14 @@ static object_entry_t *set_object_add( const char *name, const char *value) { - char *name_cpy; - char *value_cpy; + char *name_cpy; + char *value_cpy; + object_entry_t *entry; assert(NULL != name); assert(NULL != value); - object_entry_t *entry = (object_entry_t *)calloc(1, + entry = (object_entry_t *)calloc(1, sizeof(object_entry_t)); if (NULL == entry) @@ -1299,13 +1319,15 @@ static object_query_status_t set_object_serialise_entries(ne_buffer *buff, objec static ne_buffer *set_object_serialise_raw(object_query_t *handle) { + ne_buffer *buff; + assert(NULL != handle); /* Sanity checks */ assert(SET_OBJECT_REQUEST == handle->type); /* Create buffer */ - ne_buffer *buff = ne_buffer_create(); + buff = ne_buffer_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != buff); @@ -1318,7 +1340,8 @@ static ne_buffer *set_object_serialise_raw(object_query_t *handle) { static ne_buffer *set_object_serialise_form(object_query_t *handle) { - const char *vname = NULL; + const char *vname = NULL; + ne_buffer *buff; assert(NULL != handle); @@ -1326,7 +1349,7 @@ static ne_buffer *set_object_serialise_form(object_query_t *handle) { assert(SET_OBJECT_REQUEST == handle->type); /* Create buffer */ - ne_buffer *buff = ne_buffer_create(); + buff = ne_buffer_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != buff); @@ -1564,18 +1587,20 @@ static int set_object_raw_resp_end_element( static object_query_t *set_object_deserialise_raw(ne_buffer *buff) { - int ne_status; + int ne_status; + object_query_t *handle; + ne_xml_parser *parser; assert(NULL != buff); /* Create SET_OBJECT query response */ - object_query_t *handle = object_query_create(SET_OBJECT_RESPONSE, RAW_POST); + handle = object_query_create(SET_OBJECT_RESPONSE, RAW_POST); if (NULL == handle) return NULL; /* Create XML parser */ - ne_xml_parser *parser = ne_xml_create(); + parser = ne_xml_create(); /* neon API ref. states that the function always succeeds */ assert(NULL != parser); @@ -1634,7 +1659,8 @@ static int send_http_request( assert(NULL != req); do { /* Pragmatic do ... while (0) loop allowing breaks on error */ - const ne_status *req_st; + const ne_status *req_st; + int status; /* Set Content-Type */ if (NULL != ct) @@ -1647,7 +1673,7 @@ static int send_http_request( req_body->data, req_body->used - 1); /* Send request */ - int status = ne_begin_request(req); + status = ne_begin_request(req); if (NE_OK != status) { break; diff --git a/drivers/netxml-ups.h b/drivers/netxml-ups.h index 555dc02e9f..58d6400134 100644 --- a/drivers/netxml-ups.h +++ b/drivers/netxml-ups.h @@ -68,7 +68,7 @@ typedef enum { VRANGE, /* voltage out of range */ FRANGE, /* frequency out of range */ FUSEFAULT, /* fuse fault */ - CAL /* calibrating */ + CALIB /* calibrating */ } status_bit_t; extern uint32_t ups_status; diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index c0dd658a4f..1ed5ea72ad 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -27,7 +27,7 @@ #include "nut-ipmi.h" #define DRIVER_NAME "IPMI PSU driver" -#define DRIVER_VERSION "0.31" +#define DRIVER_VERSION "0.32" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -143,12 +143,11 @@ void upsdrv_updateinfo(void) */ } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } /* diff --git a/drivers/nut-libfreeipmi.c b/drivers/nut-libfreeipmi.c index a500db22ae..9402c9e450 100644 --- a/drivers/nut-libfreeipmi.c +++ b/drivers/nut-libfreeipmi.c @@ -329,7 +329,7 @@ static float libfreeipmi_get_voltage (uint8_t voltage_code) } /* Cleanup IPMI contexts */ -static void libfreeipmi_cleanup() +static void libfreeipmi_cleanup(void) { /* cleanup */ if (fru_ctx) { @@ -847,7 +847,7 @@ Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading */ -int nut_ipmi_monitoring_init() +int nut_ipmi_monitoring_init(void) { int errnum; diff --git a/drivers/nut_libusb.h b/drivers/nut_libusb.h index 20473d3fca..376e653037 100644 --- a/drivers/nut_libusb.h +++ b/drivers/nut_libusb.h @@ -11,7 +11,7 @@ * * The logic of this file is ripped from mge-shut driver (also from * Arnaud Quette), which is a "HID over serial link" UPS driver for - * Network UPS Tools + * Network UPS Tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +37,7 @@ * and for libusb headers and 0.1/1.0 mapping */ /* Used in drivers/libusb*.c sources: */ +#define LIBUSB_DEFAULT_CONF_INDEX 0 #define LIBUSB_DEFAULT_INTERFACE 0 #define LIBUSB_DEFAULT_DESC_INDEX 0 #define LIBUSB_DEFAULT_HID_EP_IN 1 @@ -52,13 +53,13 @@ typedef struct usb_communication_subdriver_s { const char *name; /* name of this subdriver */ const char *version; /* version of this subdriver */ - int (*open)(usb_dev_handle **sdevp, /* try to open the next available */ + int (*open_dev)(usb_dev_handle **sdevp, /* try to open the next available */ USBDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen)); - void (*close)(usb_dev_handle *sdev); + void (*close_dev)(usb_dev_handle *sdev); int (*get_report)(usb_dev_handle *sdev, usb_ctrl_repindex ReportId, usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize); @@ -73,6 +74,15 @@ typedef struct usb_communication_subdriver_s { usb_ctrl_charbuf buf, usb_ctrl_charbufsize bufsize, usb_ctrl_timeout_msec timeout); + /* Nearly all devices use a single configuration descriptor, index 0. + * But, it is possible for a device have more than one, check bNumConfigration + * on the device descriptor for the total. + * + * In USB, the descriptor heirarchy is + * device -> configuration(s) -> interface(s) -> endpoint(s) + */ + usb_ctrl_cfgindex usb_config_index; /* index of the device config we use. Almost always 0; see comments above */ + /* Used for Powervar UPS or similar cases to make sure * we use the right interface in the Composite device. * In a few cases our libusb*.c sets the value for specific diff --git a/drivers/nutdrv_atcl_usb.c b/drivers/nutdrv_atcl_usb.c index 4d51a4b164..c8d1162e2e 100644 --- a/drivers/nutdrv_atcl_usb.c +++ b/drivers/nutdrv_atcl_usb.c @@ -28,7 +28,7 @@ /* driver version */ #define DRIVER_NAME "'ATCL FOR UPS' USB driver" -#define DRIVER_VERSION "1.16" +#define DRIVER_VERSION "1.17" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -82,7 +82,8 @@ static int device_match_func(USBDevice_t *device, void *privdata) return 1; } } - upsdebugx(1, "To keep trying (in case your device does not have a vendor string), use vendor=NULL"); + upsdebugx(0, "To keep trying (in case your device does not have a vendor string), use vendor=NULL. " + "Have you tried the nutdrv_qx driver?"); return 0; } @@ -97,15 +98,16 @@ static int device_match_func(USBDevice_t *device, void *privdata) upsdebugx(3, "Matched device with vendor='%s'.", requested_vendor); return 1; } else { - upsdebugx(2, "idVendor=%04x and idProduct=%04x, " - "but provided vendor '%s' does not match device: '%s'.", + upsdebugx(0, "idVendor=%04x and idProduct=%04x, " + "but provided vendor '%s' does not match device: '%s'. " + "Have you tried the nutdrv_qx driver?", device->VendorID, device->ProductID, requested_vendor, device->Vendor); return 0; } } /* TODO: automatic way of suggesting other drivers? */ - upsdebugx(2, "idVendor=%04x and idProduct=%04x, " + upsdebugx(0, "idVendor=%04x and idProduct=%04x, " "but device vendor string '%s' does not match expected string '%s'. " "Have you tried the nutdrv_qx driver?", device->VendorID, device->ProductID, device->Vendor, USB_VENDOR_STRING); @@ -256,6 +258,17 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev { int ret = 0; uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; +#if WITH_LIBUSB_1_0 + libusb_device **devlist; + ssize_t devcount = 0; + libusb_device_handle *handle; + struct libusb_device_descriptor dev_desc; + uint8_t bus_num; + /* TODO: consider device_addr */ + int i; +#else /* => WITH_LIBUSB_0_1 */ + struct usb_bus *bus; +#endif /* libusb base init */ #if WITH_LIBUSB_1_0 @@ -277,13 +290,6 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev #endif #if WITH_LIBUSB_1_0 - libusb_device **devlist; - ssize_t devcount = 0; - libusb_device_handle *handle; - struct libusb_device_descriptor dev_desc; - uint8_t bus; - int i; - devcount = libusb_get_device_list(NULL, &devlist); if (devcount <= 0) fatal_with_errno(EXIT_FAILURE, "No USB device found"); @@ -296,7 +302,6 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev ret = libusb_open(dev, &handle); *handlep = handle; #else /* => WITH_LIBUSB_0_1 */ - struct usb_bus *bus; for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; @@ -339,13 +344,13 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev #if WITH_LIBUSB_1_0 device->VendorID = dev_desc.idVendor; device->ProductID = dev_desc.idProduct; - bus = libusb_get_bus_number(dev); + bus_num = libusb_get_bus_number(dev); device->Bus = (char *)malloc(4); if (device->Bus == NULL) { libusb_free_device_list(devlist, 1); fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - sprintf(device->Bus, "%03d", bus); + sprintf(device->Bus, "%03d", bus_num); iManufacturer = dev_desc.iManufacturer; iProduct = dev_desc.iProduct; iSerialNumber = dev_desc.iSerialNumber; @@ -527,9 +532,17 @@ void upsdrv_initups(void) for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { - if ((i < 3) && (sleep(5) == 0)) { - usb_comm_fail("Can't open USB device, retrying ..."); - continue; + if (i < 3) { +#ifdef WIN32 + sleep(5); +#else + if (sleep(5) == 0) { +#endif + usb_comm_fail("Can't open USB device, retrying ..."); + continue; +#ifndef WIN32 + } +#endif } fatalx(EXIT_FAILURE, @@ -671,5 +684,8 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "vendor", "USB vendor string (or NULL if none)"); + /* NOTE: This driver uses a very custom device matching method, + * so does not involve nut_usb_addvars() method like others do. + */ + addvar(VAR_VALUE, "vendor", "USB vendor string (or NULL if none)"); } diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index b354fef705..e346cdb235 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -38,9 +38,8 @@ * */ -#define DRIVER_VERSION "0.32" - #include "config.h" +#include #include "main.h" #include "attribute.h" #include "nut_float.h" @@ -59,6 +58,8 @@ #define DRIVER_NAME "Generic Q* Serial driver" #endif /* QX_USB */ +#define DRIVER_VERSION "0.36" + #ifdef QX_SERIAL #include "serial.h" #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ @@ -176,7 +177,7 @@ static status_lkp_t status_info[] = { { "CHRG", STATUS(CHRG) }, { "DISCHRG", STATUS(DISCHRG) }, { "BYPASS", STATUS(BYPASS) }, - { "CAL", STATUS(CAL) }, + { "CAL", STATUS(CALIB) }, { "OFF", STATUS(OFF) }, { "OVER", STATUS(OVER) }, { "TRIM", STATUS(TRIM) }, @@ -221,6 +222,39 @@ static struct { } load = { 0, 0.1, 1 }; static time_t battery_lastpoll = 0; +static int battery_voltage_reports_one_pack = 0, battery_voltage_reports_one_pack_considered = 0; + +/* Optionally multiply device-provided "battery.voltage" reading by + * the "battery.packs" (reading or common override setting) to end up + * with the dstate value representing the voltage of battery assembly, + * not that of a single cell/pack (different devices report different + * physically meaningful values for that reading). + * This shared method can be referenced from subdriver mapping tables. + */ +int qx_multiply_battvolt(item_t *item, char *value, const size_t valuelen) { + float s = 0; + + /* Adjusted here or not, this method was called at all + * and other code should not multiply again! */ + battery_voltage_reports_one_pack_considered = 1; + if (!battery_voltage_reports_one_pack || batt.packs < 2) { + /* (We assume by default that) this device already reports + * the sum-total voltage for the battery assembly - so just + * pass it on unmodified. + * Or we can't reasonably use a "batt.packs" anyway. + */ + snprintf(value, valuelen, "%s", item->value); + return 0; + } + + if (sscanf(item->value, "%f", &s) != 1) { + upsdebugx(2, "unparsable ss.ss %s", item->value); + return -1; + } + + snprintf(value, valuelen, "%.2f", s * batt.packs); + return 0; +} /* Fill batt.volt.act and guesstimate the battery charge * if it isn't already available. */ @@ -233,7 +267,10 @@ static int qx_battery(void) return -1; } - batt.volt.act = batt.packs * strtod(val, NULL); + batt.volt.act = strtod(val, NULL); + if (!battery_voltage_reports_one_pack_considered) { + batt.volt.act *= batt.packs; + } if (d_equal(batt.chrg.act, -1) && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { @@ -275,52 +312,94 @@ static int qx_load(void) return 0; } -/* Guesstimation: init */ +/* Init known (readings, configs) and guessed (if needed) battery related values */ static void qx_initbattery(void) { - if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) { - - const char *val; - - val = dstate_getinfo("battery.voltage.high"); - if (val) { - batt.volt.high = strtod(val, NULL); - } - - val = dstate_getinfo("battery.voltage.low"); - if (val) { - batt.volt.low = strtod(val, NULL); - } - - val = dstate_getinfo("battery.voltage.nominal"); - if (val) { - batt.volt.nom = strtod(val, NULL); - } + const char *val; + int batt_packs_known = 0; - /* If no values are available for both battery.voltage.{low,high} - * either from the UPS or provided by the user in ups.conf, - * try to guesstimate them, but announce it! */ - if ( (!d_equal(batt.volt.nom, -1)) && (d_equal(batt.volt.low, -1) || d_equal(batt.volt.high, -1))) { + val = dstate_getinfo("battery.voltage.high"); + if (val) { + batt.volt.high = strtod(val, NULL); + } - upslogx(LOG_INFO, "No values for battery high/low voltages"); + val = dstate_getinfo("battery.voltage.low"); + if (val) { + batt.volt.low = strtod(val, NULL); + } - /* Basic formula, which should cover most cases */ - batt.volt.low = 104 * batt.volt.nom / 120; - batt.volt.high = 130 * batt.volt.nom / 120; + val = dstate_getinfo("battery.voltage.nominal"); + if (val) { + batt.volt.nom = strtod(val, NULL); + } - /* Publish these data too */ - dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); - dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); + /* If no values are available for both battery.voltage.{low,high} + * either from the UPS or provided by the user in ups.conf, + * but nominal battery.voltage.nom is known, + * try to guesstimate them, but announce it! */ + if ( (!d_equal(batt.volt.nom, -1)) && (d_equal(batt.volt.low, -1) || d_equal(batt.volt.high, -1))) { - upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", - batt.volt.low, batt.volt.high); + upslogx(LOG_INFO, "No values for battery high/low voltages"); + /* Basic formula, which should cover most cases */ + batt.volt.low = 104 * batt.volt.nom / 120; + /* Per https://www.csb-battery.com.tw/english/01_product/02_detail.php?fid=17&pid=113 + * a nominally 12V battery can have "float charging voltage" + * at 13.5-13.8V and an "equalization charging voltage" (e.g. + * to desulphurize) at 14-15V. Note that per nut-names.txt, + * the "battery.voltage.high" is the practical 100% charge + * value (so equal or a bit less than actual battery.voltage + * reported by the device, when the situation is healthy); + * it is not the voltage that the battery CAN reach on the + * brink of boiling out: + */ + batt.volt.high = 130 * batt.volt.nom / 120; + + /* Publish these data too */ + dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); + dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); + + upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", + batt.volt.low, batt.volt.high); + + } + + val = dstate_getinfo("battery.packs"); + if (val && (strspn(val, "0123456789 .") == strlen(val))) { + batt.packs = strtod(val, NULL); + batt_packs_known = 1; + } + + if (testvar("battery_voltage_reports_one_pack")) { + battery_voltage_reports_one_pack = 1; + /* If we already have a battery.voltage reading from the device, + * it is not yet "adjusted" to consider the multiplication for + * packs (if known; if not - the guesswork and call below for + * qx_battery() should take care of it). Note that it is only + * a few lines above that we might have learned the user-set + * amount of battery packs and that they know the device only + * reports a single pack voltage in the protocol. + * Even if the qx_multiply_battvolt() method was called before + * and set the battery_voltage_reports_one_pack_considered flag, + * it is not too relevant until now *for maths*. However it is + * important to know that the mapping table for this subdriver + * does reference the adjustment method at all (which it would + * encounter and set the flag while querying battery.voltage + * from device and having a non-NULL reading now). + */ + if (battery_voltage_reports_one_pack_considered) { + val = dstate_getinfo("battery.voltage"); + if (val && batt_packs_known && batt.packs > 1) { + batt.volt.act = strtod(val, NULL) * batt.packs; + dstate_setinfo("battery.voltage", "%.2f", batt.volt.act); + } } + } - val = dstate_getinfo("battery.packs"); - if (val && (strspn(val, "0123456789 .") == strlen(val))) { - batt.packs = strtod(val, NULL); - } else { + /* Guesstimation: init values if not provided by device/overrides */ + if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) { + + if (!batt_packs_known) { /* qx_battery -> batt.volt.act */ if (!qx_battery() && (!d_equal(batt.volt.nom, -1))) { @@ -334,7 +413,7 @@ static void qx_initbattery(void) * therefore choose the one with the highest multiplier. */ for (i = 0; packs[i] > 0; i++) { - if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) { + if (packs[i] * batt.volt.act > 1.25 * batt.volt.nom) { continue; } @@ -346,6 +425,9 @@ static void qx_initbattery(void) } batt.packs = packs[i]; + upslogx(LOG_INFO, + "Autodetected %.0f as number of battery packs [%.0f/%.2f]", + batt.packs, batt.volt.nom, batt.volt.act); break; } @@ -471,7 +553,7 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen) size_t i; if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -530,7 +612,7 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); if (i > INT_MAX) { - upsdebugx(3, "%s: read too much (%zu)", __func__, i); + upsdebugx(3, "%s: read too much (%" PRIuSIZE ")", __func__, i); return -1; } return (int)i; @@ -544,7 +626,7 @@ static int sgs_command(const char *cmd, char *buf, size_t buflen) size_t cmdlen, i; if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -596,7 +678,8 @@ static int sgs_command(const char *cmd, char *buf, size_t buflen) (usb_ctrl_charbuf)tmp, 8, 1000); /* No error!!! */ - if (ret == ERROR_TIMEOUT) + /* if (ret == -110) */ + if (ret == LIBUSB_ERROR_TIMEOUT) break; /* Any errors here mean that we are unable to read a reply @@ -634,7 +717,7 @@ static int sgs_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); if (i > INT_MAX) { - upsdebugx(3, "%s: read too much (%zu)", __func__, i); + upsdebugx(3, "%s: read too much (%" PRIuSIZE ")", __func__, i); return -1; } return (int)i; @@ -648,7 +731,7 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) size_t i; if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -668,11 +751,11 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) * data (e.g. it times out). */ switch (ret) { - case ERROR_PIPE: /* Broken pipe */ + case LIBUSB_ERROR_PIPE: /* Broken pipe */ usb_clear_halt(udev, 0x81); break; - case ERROR_TIMEOUT: /* Connection timed out */ + case LIBUSB_ERROR_TIMEOUT: /* Connection timed out */ break; } @@ -736,7 +819,7 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); if (i > INT_MAX) { - upsdebugx(3, "%s: read too much (%zu)", __func__, i); + upsdebugx(3, "%s: read too much (%" PRIuSIZE ")", __func__, i); return -1; } return (int)i; @@ -750,7 +833,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) size_t i, len; if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -768,7 +851,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) if (ret <= 0) { upsdebugx(3, "send: %s (%d)", - (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", + (ret != LIBUSB_ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", ret); return ret; } @@ -787,7 +870,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) * to the UPS) */ if (ret <= 0) { upsdebugx(3, "read: %s (%d)", - (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", + (ret != LIBUSB_ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", ret); return ret; } @@ -831,7 +914,7 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) } if (len > INT_MAX) { - upsdebugx(3, "%s: read too much (%zu)", __func__, len); + upsdebugx(3, "%s: read too much (%" PRIuSIZE ")", __func__, len); return -1; } return (int)len; @@ -917,7 +1000,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -956,6 +1039,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) upsdebugx(1, "received %d (%d)", ret, buf[0]); if (langid_fix != -1) { + unsigned int di, si, size; /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ if (ret != buf[0]) { @@ -966,7 +1050,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ - unsigned int di, si, size = (unsigned int)buf[0]; + size = (unsigned int)buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) @@ -1041,7 +1125,7 @@ static int fabula_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -1166,7 +1250,7 @@ static int hunnox_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -1261,6 +1345,8 @@ static int hunnox_command(const char *cmd, char *buf, size_t buflen) /* if (hunnox_patch) { */ if (langid_fix != -1) { + unsigned int di, si, size; + /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ if (ret != buf[0]) { @@ -1271,7 +1357,7 @@ static int hunnox_command(const char *cmd, char *buf, size_t buflen) /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ - unsigned int di, si, size = (unsigned int)buf[0]; + size = (unsigned int)buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) break; @@ -1329,7 +1415,7 @@ static int fuji_command(const char *cmd, char *buf, size_t buflen) }; if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -1468,13 +1554,13 @@ static int phoenixtec_command(const char *cmd, char *buf, size_t buflen) size_t cmdlen = strlen(cmd); if (cmdlen > INT_MAX) { - upsdebugx(3, "%s: requested command is too long (%zu)", + upsdebugx(3, "%s: requested command is too long (%" PRIuSIZE ")", __func__, cmdlen); return 0; } if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -1529,7 +1615,7 @@ static int phoenixtec_command(const char *cmd, char *buf, size_t buflen) /* buflen constrained to INT_MAX above, so we can cast: */ return (int)(e - buf); } else { - upsdebugx(3, "read: buflen %zu too small", buflen); + upsdebugx(3, "read: buflen %" PRIuSIZE " too small", buflen); *buf = '\0'; return 0; } @@ -1555,7 +1641,7 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), " + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), " "reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); @@ -1566,6 +1652,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; @@ -1575,7 +1669,7 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) } for (retry = 0; retry < 10; retry++) { - + unsigned int di, si, size; int ret; ret = usb_get_string(udev, @@ -1592,16 +1686,15 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) /* This may serve in the future */ upsdebugx(1, "received %d (%d)", ret, buf[0]); - if (ret != buf[0]) { upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); continue; } /* Simple unicode -> ASCII inplace conversion - * FIXME: this code is at least shared with mge-shut/libshut - * Create a common function? */ - unsigned int di, si, size = (unsigned int)buf[0]; + * FIXME: this code is at least shared with mge-shut/libshut + * Create a common function? */ + size = (unsigned int)buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) @@ -1648,21 +1741,20 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) static int ablerex_command(const char *cmd, char *buf, size_t buflen) { - int iii; + int iii; int len; - int idx; + int idx; + int retry; char tmp[64]; char tmpryy[64]; upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); if (buflen > INT_MAX) { - upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)", + upsdebugx(3, "%s: requested to read too much (%" PRIuSIZE "), reducing buflen to (INT_MAX-1)", __func__, buflen); buflen = (INT_MAX - 1); } - - int retry; for (retry = 0; retry < 3; retry++) { int ret; @@ -1743,6 +1835,102 @@ static void *ablerex_subdriver_fun(USBDevice_t *device) return NULL; } +static struct { + bool_t initialized; + bool_t ok; + uint8_t in_endpoint_address; + uint8_t in_bmAttributes; + uint16_t in_wMaxPacketSize; + uint8_t out_endpoint_address; + uint8_t out_bmAttributes; + uint16_t out_wMaxPacketSize; +} armac_endpoint_cache = { .initialized = FALSE, .ok = FALSE }; + +static void load_armac_endpoint_cache(void) +{ +#if WITH_LIBUSB_1_0 + int ret; + struct libusb_device *dev; + struct libusb_config_descriptor *config_descriptor; + bool_t found_in = FALSE; + bool_t found_out = FALSE; +#endif /* WITH_LIBUSB_1_0 */ + + if (armac_endpoint_cache.initialized) { + return; + } + + armac_endpoint_cache.initialized = TRUE; + armac_endpoint_cache.ok = FALSE; + +#if WITH_LIBUSB_1_0 + dev = libusb_get_device(udev); + if (!dev) { + upsdebugx(4, "load_armac_endpoint_cache: unable to libusb_get_device"); + return; + } + + ret = libusb_get_active_config_descriptor(dev, &config_descriptor); + if (ret) { + upsdebugx(4, "load_armac_endpoint_cache: libusb_get_active_config_descriptor error=%d", ret); + libusb_free_config_descriptor(config_descriptor); + return; + } + + if (config_descriptor->bNumInterfaces != 1) { + upsdebugx(4, "load_armac_endpoint_cache: unexpected config_descriptor->bNumInterfaces=%d", config_descriptor->bNumInterfaces); + libusb_free_config_descriptor(config_descriptor); + return; + } else { + /* Here and below, the "else" is for C99-satisfying new variable scoping */ + const struct libusb_interface *interface = &config_descriptor->interface[0]; + + if (interface->num_altsetting != 1) { + upsdebugx(4, "load_armac_endpoint_cache: unexpected interface->num_altsetting=%d", interface->num_altsetting); + libusb_free_config_descriptor(config_descriptor); + return; + } else { + uint8_t i; + const struct libusb_interface_descriptor *interface_descriptor = &interface->altsetting[0]; + + if (interface_descriptor->bNumEndpoints != 2) { + upsdebugx(4, "load_armac_endpoint_cache: unexpected interface_descriptor->bNumEndpoints=%d", interface_descriptor->bNumEndpoints); + libusb_free_config_descriptor(config_descriptor); + return; + } + + for (i = 0; i < interface_descriptor->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *endpoint = &interface_descriptor->endpoint[i]; + + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + found_in = TRUE; + armac_endpoint_cache.in_endpoint_address = endpoint->bEndpointAddress; + armac_endpoint_cache.in_bmAttributes = endpoint->bmAttributes; + armac_endpoint_cache.in_wMaxPacketSize = endpoint->wMaxPacketSize; + } else { + found_out = TRUE; + armac_endpoint_cache.out_endpoint_address = endpoint->bEndpointAddress; + armac_endpoint_cache.out_bmAttributes = endpoint->bmAttributes; + armac_endpoint_cache.out_wMaxPacketSize = endpoint->wMaxPacketSize; + } + } + } + } + + if (found_in || found_out) { + armac_endpoint_cache.ok = TRUE; + + upsdebugx(4, "%s: in_endpoint_address=%02x, in_bmAttributes=%02d, out_endpoint_address=%02d, out_bmAttributes=%02d", + __func__, armac_endpoint_cache.in_endpoint_address, armac_endpoint_cache.in_bmAttributes, + armac_endpoint_cache.out_endpoint_address, armac_endpoint_cache.out_bmAttributes); + } + + libusb_free_config_descriptor(config_descriptor); +#else /* WITH_LIBUSB_1_0 */ + upsdebugx(4, "%s: SKIP: not implemented for libusb-0.1 or serial connections", __func__); +#endif /* !WITH_LIBUSB_1_0 */ +} + /* Armac communication subdriver * * This reproduces a communication protocol used by an old PowerManagerII @@ -1750,12 +1938,16 @@ static void *ablerex_subdriver_fun(USBDevice_t *device) * Richcomm Technologies, Inc. Dec 27 2005 ver 1.1." Maybe other Richcomm UPSes * would work with this - better than with the richcomm_usb driver. */ +#define ARMAC_READ_SIZE_FOR_CONTROL 6 +#define ARMAC_READ_SIZE_FOR_INTERRUPT 64 static int armac_command(const char *cmd, char *buf, size_t buflen) { - char tmpbuf[6]; - int ret = 0; - size_t i, bufpos; + char tmpbuf[ARMAC_READ_SIZE_FOR_INTERRUPT]; + int ret = 0; + size_t i, bufpos; const size_t cmdlen = strlen(cmd); + bool_t use_interrupt = FALSE; + int read_size = ARMAC_READ_SIZE_FOR_CONTROL; /* UPS ignores (doesn't echo back) unsupported commands which makes * the initialization long. List commands tested to be unsupported: @@ -1769,6 +1961,10 @@ static int armac_command(const char *cmd, char *buf, size_t buflen) NULL }; + if (!armac_endpoint_cache.initialized) { + load_armac_endpoint_cache(); + } + for (i = 0; unsupported[i] != NULL; i++) { if (strcmp(cmd, unsupported[i]) == 0) { upsdebugx(2, @@ -1779,18 +1975,51 @@ static int armac_command(const char *cmd, char *buf, size_t buflen) } upsdebugx(4, "armac command %.*s", (int)strcspn(cmd, "\r"), cmd); - /* Send command to the UPS in 3-byte chunks. Most fit 1 chunk, except for eg. - * parameterized tests. */ - for (i = 0; i < cmdlen;) { - const size_t bytes_to_send = (cmdlen <= (i + 3)) ? (cmdlen - i) : 3; +#if WITH_LIBUSB_1_0 + /* Be conservative and do not break old Armac UPSes */ + use_interrupt = armac_endpoint_cache.ok + && armac_endpoint_cache.in_endpoint_address == 0x82 + && armac_endpoint_cache.in_bmAttributes & LIBUSB_TRANSFER_TYPE_INTERRUPT + && armac_endpoint_cache.out_endpoint_address == 0x02 + && armac_endpoint_cache.out_bmAttributes & LIBUSB_TRANSFER_TYPE_INTERRUPT + && armac_endpoint_cache.in_wMaxPacketSize == 64; +#endif /* WITH_LIBUSB_1_0 */ + + if (use_interrupt && cmdlen + 1 < armac_endpoint_cache.in_wMaxPacketSize) { memset(tmpbuf, 0, sizeof(tmpbuf)); - tmpbuf[0] = 0xa0 + bytes_to_send; - memcpy(tmpbuf + 1, cmd + i, bytes_to_send); - ret = usb_control_msg(udev, - USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, 0x200, 0, - (usb_ctrl_charbuf)tmpbuf, 4, 5000); - i += bytes_to_send; + tmpbuf[0] = 0xa0 + cmdlen; + memcpy(tmpbuf + 1, cmd, cmdlen); + + ret = usb_interrupt_write(udev, + armac_endpoint_cache.out_endpoint_address, + (usb_ctrl_charbuf)tmpbuf, cmdlen + 1, 5000); + + read_size = ARMAC_READ_SIZE_FOR_INTERRUPT; + } else { + /* Cleanup buffer before sending a new command */ + for (i = 0; i < 10; i++) { + ret = usb_interrupt_read(udev, 0x81, + (usb_ctrl_charbuf)tmpbuf, ARMAC_READ_SIZE_FOR_CONTROL, 100); + if (ret != ARMAC_READ_SIZE_FOR_CONTROL) { + /* Timeout - buffer is clean. */ + break; + } + upsdebugx(4, "armac cleanup ret i=%" PRIuSIZE " ret=%d ctrl=%02hhx", i, ret, tmpbuf[0]); + } + + /* Send command to the UPS in 3-byte chunks. Most fit 1 chunk, except for eg. + * parameterized tests. */ + for (i = 0; i < cmdlen;) { + const size_t bytes_to_send = (cmdlen <= (i + 3)) ? (cmdlen - i) : 3; + memset(tmpbuf, 0, sizeof(tmpbuf)); + tmpbuf[0] = 0xa0 + bytes_to_send; + memcpy(tmpbuf + 1, cmd + i, bytes_to_send); + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, 0x200, 0, + (usb_ctrl_charbuf)tmpbuf, 4, 5000); + i += bytes_to_send; + } } if (ret <= 0) { @@ -1801,26 +2030,30 @@ static int armac_command(const char *cmd, char *buf, size_t buflen) return ret; } + /* Wait for response to buffer */ + usleep(2000); memset(buf, 0, buflen); bufpos = 0; - while (bufpos + 6 < buflen) { + while (bufpos + read_size + 1 < buflen) { size_t bytes_available; /* Read data in 6-byte chunks */ - ret = usb_interrupt_read(udev, - 0x81, - (usb_ctrl_charbuf)tmpbuf, 6, 1000); + ret = usb_interrupt_read(udev, use_interrupt ? armac_endpoint_cache.in_endpoint_address : 0x81, + (usb_ctrl_charbuf)tmpbuf, read_size, 1000); /* Any errors here mean that we are unable to read a reply * (which will happen after successfully writing a command * to the UPS) */ - if (ret != 6) { + if (ret != read_size) { + /* NOTE: If end condition is invalid for particular UPS we might make one + * request more and get this error. If bufpos > (say) 10 this could be ignored + * and the reply correctly read. */ upsdebugx(1, "interrupt read error: %s (%d)", ret ? nut_usb_strerror(ret) : "timeout", ret); - return ret; + return ret < 0 ? ret : (int)bufpos; } upsdebugx(4, @@ -1829,22 +2062,68 @@ static int armac_command(const char *cmd, char *buf, size_t buflen) tmpbuf[0], tmpbuf[1], tmpbuf[2], tmpbuf[3], tmpbuf[4], tmpbuf[5], tmpbuf[1], tmpbuf[2], tmpbuf[3], tmpbuf[4], tmpbuf[5]); - bytes_available = (unsigned char)tmpbuf[0] & 0x0f; + /* + * On most tested devices (including R/2000I/PSW) this was equal to the number of + * bytes returned in the buffer, but on some newer UPS (R/3000I/PF1) it was 1 more + * (1 control + 5 bytes transferred and bytes_available equal to 6 instead of 5). + * + * Current assumption is that this is number of bytes available on the UPS side + * with up to 5 (ret - 1) transferred. + */ + bytes_available = (unsigned char)tmpbuf[0] & 0x3f; if (bytes_available == 0) { /* End of transfer */ break; } - memcpy(buf + bufpos, tmpbuf + 1, bytes_available); - bufpos += bytes_available; + if (bytes_available > (unsigned)read_size - 1) { + /* Single interrupt transfer has 1 control + 5 data bytes */ + bytes_available = read_size - 1; + } + + /* Copy bytes into the final buffer while detecting end of line - \r */ + for (i = 0; i < bytes_available; i++) { + if (tmpbuf[i + 1] == 0x00 && bufpos == 0) { + /* Happens when a manually turned off UPS is connected to the USB */ + upsdebugx(3, "null byte read - is UPS off?"); + return 0; + } + + /* Vultech V2000 seems to use 0x00 within status bits. This might mean "unsupported". + * or something else completely. */ + if (tmpbuf[i + 1] == 0x00) { + if (bufpos >= 38) { + upsdebugx(3, "found null byte in status bits at %" PRIuSIZE " byte, assuming 0.", bufpos); + buf[bufpos++] = '0'; + continue; + } else { + upsdebugx(3, "found null byte in data stream - interrupting read."); + /* Break through two loops */ + goto end_of_message; + } + } + + buf[bufpos++] = tmpbuf[i + 1]; + + if (tmpbuf[i + 1] == 0x0d) { + if (i + 1 != bytes_available) { + upsdebugx(3, "trailing bytes in serial transmission found: %" PRIuSIZE " copied out of %" PRIuSIZE, + i + 1, bytes_available + ); + } + /* Break through two loops */ + goto end_of_message; + } + } if (bytes_available <= 2) { /* Slow down, let the UPS buffer more bytes */ - usleep(15000); + usleep(10000); } } +end_of_message: - if (bufpos + 6 >= buflen) { + if (bufpos + read_size >= buflen) { upsdebugx(2, "Protocol error, too much data read."); return -1; } @@ -2485,9 +2764,6 @@ int setvar(const char *varname, const char *val) } /* Try to shutdown the UPS */ -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { int retry; @@ -2502,8 +2778,11 @@ void upsdrv_shutdown(void) item = find_nut_info("ups.delay.start", 0, QX_FLAG_SKIP); /* Don't know what happened */ - if (!item) - fatalx(EXIT_FAILURE, "Unable to set start delay"); + if (!item) { + upslogx(LOG_ERR, "Unable to set start delay"); + set_exit_flag(-1); + return; + } /* Set the default value */ dstate_setinfo(item->info_type, "%s", item->dfl); @@ -2515,15 +2794,20 @@ void upsdrv_shutdown(void) val = getval(QX_VAR_ONDELAY); if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { - fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val); + upslogx(LOG_ERR, "Start delay '%s' out of range", val); + set_exit_flag(-1); + return; } /* Shutdown delay */ item = find_nut_info("ups.delay.shutdown", 0, QX_FLAG_SKIP); /* Don't know what happened */ - if (!item) - fatalx(EXIT_FAILURE, "Unable to set shutdown delay"); + if (!item) { + upslogx(LOG_ERR, "Unable to set shutdown delay"); + set_exit_flag(-1); + return; + } /* Set the default value */ dstate_setinfo(item->info_type, "%s", item->dfl); @@ -2535,7 +2819,9 @@ void upsdrv_shutdown(void) val = getval(QX_VAR_OFFDELAY); if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { - fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val); + upslogx(LOG_ERR, "Shutdown delay '%s' out of range", val); + set_exit_flag(-1); + return; } /* Stop pending shutdowns */ @@ -2574,11 +2860,14 @@ void upsdrv_shutdown(void) } - fatalx(EXIT_SUCCESS, "Shutting down in %s seconds", + upslogx(LOG_ERR, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown")); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } #ifdef QX_USB @@ -2607,19 +2896,45 @@ void upsdrv_shutdown(void) void upsdrv_help(void) { -#ifdef QX_USB - #ifndef TESTING - printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); +#ifndef TESTING size_t i; +# ifdef QX_USB + /* Subdrivers have special SOMETHING_command() handling and + * are listed in usbsubdriver[] array (just above in this + * source file). + */ + printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); for (i = 0; usbsubdriver[i].name != NULL; i++) { if (i>0) printf(", "); printf("%s", usbsubdriver[i].name); } printf("\n\n"); - #endif -#endif +# endif /* QX_USB*/ + + /* Protocols are the first token from "name" field in + * subdriver_t instances in files like nutdrv_qx_mecer.c + */ + printf("\nAcceptable values for 'protocol' via -x or ups.conf in this driver: "); + for (i = 0; subdriver_list[i] != NULL; i++) { + char subdrv_name[SMALLBUF], *p; + + /* Get rid of subdriver version */ + snprintf(subdrv_name, sizeof(subdrv_name), "%.*s", + (int)strcspn(subdriver_list[i]->name, " "), + subdriver_list[i]->name); + + /* lowercase the (ASCII) string */ + for (p = subdrv_name; *p; ++p) + *p = tolower(*p); + + if (i>0) + printf(", "); + printf("%s", subdrv_name); + } + printf("\n\n"); +#endif /* TESTING */ printf("Read The Fine Manual ('man 8 nutdrv_qx')\n"); } @@ -2659,8 +2974,14 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "idleload", "Minimum load to be used for runtime calculation"); + addvar(VAR_FLAG, "battery_voltage_reports_one_pack", + "If your device natively reports battery.voltage of a single cell/pack, " + "multiply that into voltage of the whole battery assembly. " + "You may need an override.battery.packs=N setting also."); + #ifdef QX_USB addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); @@ -2832,6 +3153,15 @@ void upsdrv_initinfo(void) /* Open the port and the like and choose the subdriver */ void upsdrv_initups(void) { +#ifdef QX_USB +# ifndef TESTING + int ret, langid; + char tbuf[255]; /* Some devices choke on size > 255 */ + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; + char *subdrv; +# endif +#endif + upsdebugx(1, "%s...", __func__); #if defined(QX_SERIAL) && defined(QX_USB) @@ -2847,6 +3177,9 @@ void upsdrv_initups(void) getval("serial") || getval("bus") || getval("langid_fix") +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + || getval("busport") +#endif ) { /* USB */ is_usb = 1; @@ -2862,6 +3195,8 @@ void upsdrv_initups(void) #ifdef QX_USB if (!is_usb) { + #else + { /* scoping */ #endif /* QX_USB */ #ifndef TESTING @@ -2929,6 +3264,8 @@ void upsdrv_initups(void) #ifdef QX_USB } else { /* is_usb */ + #else + } /* end of scoping */ #endif /* QX_USB */ #endif /* QX_SERIAL */ @@ -2936,12 +3273,10 @@ void upsdrv_initups(void) /* USB */ #ifdef QX_USB - #ifndef TESTING - int ret, langid; - char tbuf[255]; /* Some devices choke on size > 255 */ - char *regex_array[7]; + warn_if_bad_usb_port_filename(device_path); - char *subdrv = getval("subdriver"); +# ifndef TESTING + subdrv = getval("subdriver"); regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); @@ -2950,6 +3285,13 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); regex_array[6] = getval("device"); +# if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +# else + if (getval("busport")) { + upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)"); + } +# endif /* Check for language ID workaround (#1) */ if (getval("langid_fix")) { @@ -3012,7 +3354,7 @@ void upsdrv_initups(void) /* Link the matchers */ regex_matcher->next = &device_matcher; - ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); + ret = usb->open_dev(&udev, &usbdevice, regex_matcher, NULL); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. " @@ -3057,7 +3399,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!)", @@ -3065,11 +3407,11 @@ void upsdrv_initups(void) } } - #endif /* TESTING */ +# endif /* TESTING */ - #ifdef QX_SERIAL +# ifdef QX_SERIAL } /* is_usb */ - #endif /* QX_SERIAL */ +# endif /* QX_SERIAL */ #endif /* QX_USB */ @@ -3089,24 +3431,23 @@ void upsdrv_cleanup(void) #ifndef TESTING -#ifdef QX_SERIAL +# ifdef QX_SERIAL - #ifdef QX_USB +# ifdef QX_USB if (!is_usb) { - #endif /* QX_USB */ +# endif /* QX_USB */ ser_set_dtr(upsfd, 0); ser_close(upsfd, device_path); - #ifdef QX_USB +# ifdef QX_USB } else { /* is_usb */ - #endif /* QX_USB */ +# endif /* QX_USB */ -#endif /* QX_SERIAL */ - -#ifdef QX_USB +# endif /* QX_SERIAL */ - usb->close(udev); +# ifdef QX_USB + usb->close_dev(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); @@ -3114,12 +3455,15 @@ void upsdrv_cleanup(void) free(usbdevice.Serial); free(usbdevice.Bus); free(usbdevice.Device); +# if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(usbdevice.BusPort); +# endif - #ifdef QX_SERIAL +# ifdef QX_SERIAL } /* is_usb */ - #endif /* QX_SERIAL */ +# endif /* QX_SERIAL */ -#endif /* QX_USB */ +# endif /* QX_USB */ #endif /* TESTING */ @@ -3132,6 +3476,10 @@ void upsdrv_cleanup(void) * Returns < 0 on error, 0 on timeout and the number of bytes read on success. */ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) { +#ifndef TESTING + ssize_t ret = -1; +#endif + /* NOTE: Could not find in which ifdef-ed codepath, but clang complained * about unused parameters here. Reference them just in case... */ @@ -3141,8 +3489,6 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) #ifndef TESTING - ssize_t ret = -1; - # ifdef QX_USB # ifdef QX_SERIAL @@ -3151,11 +3497,15 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) # endif /* QX_SERIAL (&& QX_USB)*/ if (udev == NULL) { - ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); + dstate_setinfo("driver.state", "reconnect.trying"); + + ret = usb->open_dev(&udev, &usbdevice, reopen_matcher, NULL); if (ret < 1) { return ret; } + + dstate_setinfo("driver.state", "reconnect.updateinfo"); } ret = (*subdriver_command)(cmd, buf, buflen); @@ -3166,7 +3516,7 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) switch (ret) { - case ERROR_BUSY: /* Device or resource busy */ + case LIBUSB_ERROR_BUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); #ifndef HAVE___ATTRIBUTE__NORETURN # if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) @@ -3194,7 +3544,7 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) #endif #endif /* WITH_LIBUSB_0_1 */ - case ERROR_PIPE: /* Broken pipe */ + case LIBUSB_ERROR_PIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; @@ -3208,21 +3558,22 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) upsdebugx(1, "Device reset handled"); } goto fallthrough_case_reconnect; - case ERROR_NO_DEVICE: /* No such device */ - case ERROR_ACCESS: /* Permission denied */ - case ERROR_IO: /* I/O error */ + case LIBUSB_ERROR_NO_DEVICE: /* No such device */ + case LIBUSB_ERROR_ACCESS: /* Permission denied */ + case LIBUSB_ERROR_IO: /* I/O error */ #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -ENXIO: /* No such device or address */ #endif /* WITH_LIBUSB_0_1 */ - case ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NOT_FOUND: /* No such file or directory */ fallthrough_case_reconnect: /* Uh oh, got to reconnect! */ - usb->close(udev); + dstate_setinfo("driver.state", "reconnect.trying"); + usb->close_dev(udev); udev = NULL; break; - case ERROR_TIMEOUT: /* Connection timed out */ - case ERROR_OVERFLOW: /* Value too large for defined data type */ + case LIBUSB_ERROR_TIMEOUT: /* Connection timed out */ + case LIBUSB_ERROR_OVERFLOW: /* Value too large for defined data type */ #if EPROTO && WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -EPROTO: /* Protocol error */ #endif @@ -3244,7 +3595,7 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) ret = ser_send(upsfd, "%s", cmd); if (ret <= 0) { - upsdebugx(3, "send: %s (%zd)", + upsdebugx(3, "send: %s (%" PRIiSIZE ")", ret ? strerror(errno) : "timeout", ret); return ret; } @@ -3255,7 +3606,7 @@ static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0); if (ret <= 0) { - upsdebugx(3, "read: %s (%zd)", + upsdebugx(3, "read: %s (%" PRIiSIZE ")", ret ? strerror(errno) : "timeout", ret); return ret; } @@ -3532,6 +3883,7 @@ static bool_t qx_ups_walk(walkmode_t mode) if (mode == QX_WALKMODE_FULL_UPDATE) { batt.runt.act = -1; batt.chrg.act = -1; + battery_voltage_reports_one_pack_considered = 0; } /* Clear data from previous_item */ @@ -3731,7 +4083,7 @@ static bool_t qx_ups_walk(walkmode_t mode) ) { if (getval("runtimecal")) { - + const char *val; time_t battery_now; time(&battery_now); @@ -3754,6 +4106,49 @@ static bool_t qx_ups_walk(walkmode_t mode) } + 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 = strtod(val, NULL); + if (!battery_voltage_reports_one_pack_considered) { + batt.volt.act *= batt.packs; + } + + 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); @@ -3820,7 +4215,7 @@ static void ups_status_set(void) if (ups_status & STATUS(OFF)) { status_set("OFF"); /* UPS is off */ } - if (ups_status & STATUS(CAL)) { + if (ups_status & STATUS(CALIB)) { status_set("CAL"); /* Calibration */ } if (ups_status & STATUS(FSD)) { diff --git a/drivers/nutdrv_qx.h b/drivers/nutdrv_qx.h index 3cc3ebd408..faffe717cc 100644 --- a/drivers/nutdrv_qx.h +++ b/drivers/nutdrv_qx.h @@ -26,11 +26,18 @@ #ifndef NUTDRV_QX_H #define NUTDRV_QX_H +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + #include #include #include #include -#include "config.h" /* For testing purposes */ /*#define TESTING*/ @@ -179,6 +186,9 @@ unsigned int qx_status(void); /* Edit the current status: it takes one of the NUT status (all but OB are supported, simply set it as not OL), eventually preceded with an exclamation mark to clear it from the status (e.g. !OL). */ void update_status(const char *nutvalue); + /* Let subdrivers reference this: for devices that report "battery.voltage" of a single cell/pack, optionally multiply that into representing the whole assembly */ +int qx_multiply_battvolt(item_t *item, char *value, const size_t valuelen); + /* Data for processing status values */ #define STATUS(x) ((unsigned int)1U<value[0] * 256 + (unsigned char)item->value[1]; + int Q5_Fout, Q5_Vb, Q5_O_Cur, Q5_Err; +#ifdef ABLEREX_WITH_Q5_InvW + int Q5_InvW; +#endif +#ifdef ABLEREX_WITH_RawValue + int RawValue = ((int)(unsigned char)item->value[0]) * 256 + (unsigned char)item->value[1]; +#endif + +/* // real code below, this is for dev-testing + ablerexQ5Vb = ((int)(unsigned char)buf[7]) * 256 + (unsigned char)buf[8]; + Q5_Vbc = ((int)(unsigned char)buf[9]) * 256 + (unsigned char)buf[10]; */ - //ablerexQ5Vb = (unsigned char)buf[7] * 256 + (unsigned char)buf[8]; - //Q5_Vbc = (unsigned char)buf[9] * 256 + (unsigned char)buf[10]; upsdebugx(2, "Q51: %d %d %d %d %d %d", item->answer[0], item->answer[1], item->answer[2], item->answer[3], item->answer[4], item->answer[5]); upsdebugx(2, "Q52: %d %d %d %d %d %d", item->answer[6], item->answer[7], item->answer[8], item->answer[9], item->answer[10], item->answer[11]); upsdebugx(2, "Q53: %d %d %d %d", item->answer[12], item->answer[13], item->answer[14], item->answer[15]); - int Q5_Fout = (unsigned char)item->answer[1] * 256 + (unsigned char)item->answer[2]; - int Q5_Vb = (unsigned char)item->answer[7] * 256 + (unsigned char)item->answer[8]; + Q5_Fout = (unsigned char)item->answer[1] * 256 + (unsigned char)item->answer[2]; + Q5_Vb = (unsigned char)item->answer[7] * 256 + (unsigned char)item->answer[8]; Q5_Vbc = (unsigned char)item->answer[9] * 256 + (unsigned char)item->answer[10]; - //int Q5_InvW = (unsigned char)item->answer[11] * 256 + (unsigned char)item->answer[12]; - int Q5_Err = (unsigned char)item->answer[13] * 256 + (unsigned char)item->answer[14]; - int Q5_O_Cur = (unsigned char)item->answer[15] * 256 + (unsigned char)item->answer[16]; +#ifdef ABLEREX_WITH_Q5_InvW + Q5_InvW = (unsigned char)item->answer[11] * 256 + (unsigned char)item->answer[12]; +#endif + Q5_Err = (unsigned char)item->answer[13] * 256 + (unsigned char)item->answer[14]; + Q5_O_Cur = (unsigned char)item->answer[15] * 256 + (unsigned char)item->answer[16]; ablerexQ5Vb = Q5_Vb; upsdebugx(2, "Q5: %.1f %d %.1f", 0.1 * Q5_Fout, Q5_Err, 0.1 * Q5_O_Cur); upsdebugx(2, "Q5Vb: %d Vbc %d", Q5_Vb, Q5_Vbc); +#ifdef ABLEREX_WITH_Q5_InvW + upsdebugx(2, "Q5_InvW: %d", Q5_InvW); +#endif dstate_setinfo("output.frequency", "%.1f", 0.1 * Q5_Fout); dstate_setinfo("ups.alarm", "%d", Q5_Err); dstate_setinfo("output.current", "%.1f", 0.1 * Q5_O_Cur); snprintf(value, valuelen, "%.1f", Q5_Fout * 0.1); -/* +#ifdef ABLEREX_WITH_RawValue switch (item->from) { case 1: @@ -77,16 +87,19 @@ static int ablerex_Q5(item_t *item, char *value, const size_t valuelen) { break; default: - //Don't know what happened + /* Don't know what happened */ return -1; } -*/ +#endif return 0; } static int ablerex_battery(item_t *item, char *value, const size_t valuelen) { - double BattV = 0.0; + double BattV = 0.0; + double nomBattV = 0.0; + double battvoltact = 0.0; + BattV = strtod(item->value, NULL); upsdebugx(2, "battvoltact2: %.2f", BattV); if (!dstate_getinfo("battery.voltage.nominal")) @@ -95,12 +108,11 @@ static int ablerex_battery(item_t *item, char *value, const size_t valuelen) { return 0; } - double nomBattV = 0.0; nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); upsdebugx(2, "battvoltact1: %.2f", nomBattV); - //return 0; - - double battvoltact = 0.0; +/* + * //return 0; + */ if (ablerexQ5Vb > 0) { battvoltact = ablerexQ5Vb * nomBattV / 1200; @@ -160,8 +172,10 @@ static int ablerex_battery_charge(double BattIn) } } } else { - //double nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); - //double battV = BattIn / (nomBattV / 12); +/* + * //double nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); + * //double battV = BattIn / (nomBattV / 12); + */ for (i = 0; offlineC[i] > 0; i++) { if (BattIn >= offlineP[i]) { @@ -174,7 +188,10 @@ static int ablerex_battery_charge(double BattIn) } static int ablerex_batterycharge(item_t *item, char *value, const size_t valuelen) { - double BattV = 0.0; + double BattV = 0.0; + double nomBattV = 0.0; + int BattP; + BattV = strtod(item->value, NULL); upsdebugx(2, "battvoltc2: %.2f", BattV); if (!dstate_getinfo("battery.voltage.nominal")) @@ -183,16 +200,19 @@ static int ablerex_batterycharge(item_t *item, char *value, const size_t valuele return 0; } - double nomBattV = 0.0; nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); upsdebugx(2, "battvv1: %.2f", nomBattV); - //return 0; +/* + * //return 0; + */ if (BattV > 3.0) { BattV = BattV / (nomBattV / 12); } - int BattP = ablerex_battery_charge(BattV); - //dstate_setinfo("battery.charge", "%.0f", BattP); + BattP = ablerex_battery_charge(BattV); +/* + * //dstate_setinfo("battery.charge", "%.0f", BattP); + */ snprintf(value, valuelen, "%d", BattP); upsdebugx(2, "battcharge: %d", BattP); @@ -301,10 +321,12 @@ static int ablerex_process_status_bits(item_t *item, char *value, const size_t v status_set("RB"); } - double vout = strtod(dstate_getinfo("output.voltage"), NULL); + { /* scope */ + double vout = strtod(dstate_getinfo("output.voltage"), NULL); - if (vout < 50.0) { - status_set("OFF"); + if (vout < 50.0) { + status_set("OFF"); + } } break; @@ -364,9 +386,13 @@ static item_t ablerex_qx2nut[] = { /* Ablerex */ { "output.frequency", 0, NULL, "Q5\r", "", 22, '(', "", 1, 18, "%.1f", 0, NULL, NULL, ablerex_Q5 }, { "battery.runtime", 0, NULL, "At\r", "", 0, '(', "", 0, 0, "%d", 0, NULL, NULL, ablerex_At }, - //{ "ups.alarm", 0, NULL, "Q5\r", "", 22, '(', "", 1, 14, "%.0f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, +/* + * //{ "ups.alarm", 0, NULL, "Q5\r", "", 22, '(', "", 1, 14, "%.0f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, + */ { "ups.test.result", 0, NULL, "TR\r", "", 0, '#', "", 0, 0, "%s", 0, NULL, NULL, ablerex_TR }, - //{ "output.current", 0, NULL, "Q5\r", "", 22, '(', "", 1, 16, "%.1f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, +/* + * //{ "output.current", 0, NULL, "Q5\r", "", 22, '(', "", 1, 16, "%.1f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, + */ /* * > [I\r] diff --git a/drivers/nutdrv_qx_bestups.c b/drivers/nutdrv_qx_bestups.c index adc102f0c5..7f1cd45b44 100644 --- a/drivers/nutdrv_qx_bestups.c +++ b/drivers/nutdrv_qx_bestups.c @@ -30,7 +30,7 @@ #include "nutdrv_qx_blazer-common.h" #include "nutdrv_qx_bestups.h" -#define BESTUPS_VERSION "BestUPS 0.06" +#define BESTUPS_VERSION "BestUPS 0.07" /* Support functions */ static int bestups_claim(void); @@ -103,7 +103,7 @@ static item_t bestups_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ @@ -357,12 +357,14 @@ static int bestups_preprocess_id_answer(item_t *item, const int len) /* *SETVAR(/NONUT)* Preprocess setvars */ static int bestups_process_setvar(item_t *item, char *value, const size_t valuelen) { + double val; + if (!strlen(value)) { upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); return -1; } - double val = strtod(value, NULL); + val = strtod(value, NULL); if (!strcasecmp(item->info_type, "pins_shutdown_mode")) { @@ -394,7 +396,7 @@ static int bestups_process_setvar(item_t *item, char *value, const size_t valuel static int bestups_process_bbb_status_bit(item_t *item, char *value, const size_t valuelen) { /* Bypass/Boost/Buck bit is not reliable when a battery test, shutdown or on battery condition occurs: always ignore it in these cases */ - if (!((unsigned int)(qx_status()) & STATUS(OL)) || ((unsigned int)(qx_status()) & (STATUS(CAL) | STATUS(FSD)))) { + if (!((unsigned int)(qx_status()) & STATUS(OL)) || ((unsigned int)(qx_status()) & (STATUS(CALIB) | STATUS(FSD)))) { if (item->value[0] == '1') item->value[0] = '0'; diff --git a/drivers/nutdrv_qx_hunnox.c b/drivers/nutdrv_qx_hunnox.c index f10447904a..54c332fccf 100644 --- a/drivers/nutdrv_qx_hunnox.c +++ b/drivers/nutdrv_qx_hunnox.c @@ -26,7 +26,7 @@ #include "nutdrv_qx_hunnox.h" -#define HUNNOX_VERSION "Hunnox 0.01" +#define HUNNOX_VERSION "Hunnox 0.02" /* qx2nut lookup table */ static item_t hunnox_qx2nut[] = { @@ -43,7 +43,7 @@ static item_t hunnox_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", QX_FLAG_QUICK_POLL, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_masterguard.c b/drivers/nutdrv_qx_masterguard.c index 08168f69dd..85a0cae0f6 100644 --- a/drivers/nutdrv_qx_masterguard.c +++ b/drivers/nutdrv_qx_masterguard.c @@ -24,6 +24,7 @@ #include "nutdrv_qx_masterguard.h" #include +#include "nut_stdint.h" #define MASTERGUARD_VERSION "Masterguard 0.02" @@ -353,9 +354,9 @@ static int masterguard_output_voltages(item_t *item, char *value, const size_t v strncpy(value, item->value, valuelen); /* save before strtok mangles it */ for (w = strtok(item->value, sep); w; w = strtok(NULL, sep)) { n++; - upsdebugx(4, "output voltage #%zu: %s", n, w); + upsdebugx(4, "output voltage #%" PRIuSIZE ": %s", n, w); if ((masterguard_e_outvolts = realloc(masterguard_e_outvolts, n * sizeof(info_rw_t))) == NULL) { - upsdebugx(1, "output voltages: allocating #%zu failed", n); + upsdebugx(1, "output voltages: allocating #%" PRIuSIZE " failed", n); return -1; } strncpy(masterguard_e_outvolts[n - 1].value, w, SMALLBUF - 1); @@ -363,7 +364,7 @@ static int masterguard_output_voltages(item_t *item, char *value, const size_t v } /* need to do this seperately in case the loop is run zero times */ if ((masterguard_e_outvolts = realloc(masterguard_e_outvolts, (n + 1) * sizeof(info_rw_t))) == NULL) { - upsdebugx(1, "output voltages: allocating terminator after #%zu failed", n); + upsdebugx(1, "output voltages: allocating terminator after #%" PRIuSIZE " failed", n); return -1; } masterguard_e_outvolts[n].value[0] = '\0'; @@ -443,11 +444,11 @@ static int masterguard_fault(item_t *item, char *value, const size_t valuelen) { /* add slave address (from masterguard_my_slaveaddr) to commands that require it */ static int masterguard_add_slaveaddr(item_t *item, char *command, const size_t commandlen) { + size_t l; + NUT_UNUSED_VARIABLE(item); NUT_UNUSED_VARIABLE(commandlen); - size_t l; - l = strlen(command); if (strncmp(command + l - 4, ",XX\r", 4) != 0) { upsdebugx(1, "add slaveaddr: no ,XX\\r at end of command %s", command); @@ -464,13 +465,13 @@ static int masterguard_add_slaveaddr(item_t *item, char *command, const size_t c /* helper, not to be called directly from table */ /*!! use parameter from the value field instead of ups.delay.{shutdown,return}?? */ static int masterguard_shutdown(item_t *item, char *value, const size_t valuelen, const int stayoff) { - NUT_UNUSED_VARIABLE(item); - long offdelay; char *p; const char *val, *name; char offstr[3]; + NUT_UNUSED_VARIABLE(item); + offdelay = strtol((val = dstate_getinfo(name = "ups.delay.shutdown")), &p, 10); if (*p != '\0') goto ill; if (offdelay < 0) { @@ -510,15 +511,16 @@ static int masterguard_shutdown_stayoff(item_t *item, char *value, const size_t } static int masterguard_test_battery(item_t *item, char *value, const size_t valuelen) { - NUT_UNUSED_VARIABLE(item); - long duration; char *p; + NUT_UNUSED_VARIABLE(item); + if (value[0] == '\0') { upsdebugx(2, "battery test: no duration"); return -1; } + duration = strtol(value, &p, 10); if (*p != '\0') goto ill; if (duration == 10) { @@ -935,11 +937,11 @@ static int masterguard_claim(void) { item_t *item; /* mandatory values */ char *mandatory[] = { - "series", /* SKIP */ + "experimental.series", /* SKIP */ "device.model", /* minimal number of battery packs */ "ups.power.nominal", /* load computation */ "ups.id", /* slave address */ - "output_voltages", /* output voltages enum */ + "experimental.output_voltages", /* output voltages enum */ #if 0 "battery.packs", /* battery voltage computation */ #endif diff --git a/drivers/nutdrv_qx_mecer.c b/drivers/nutdrv_qx_mecer.c index d044f33bee..e4702944ca 100644 --- a/drivers/nutdrv_qx_mecer.c +++ b/drivers/nutdrv_qx_mecer.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_mecer.h" -#define MECER_VERSION "Mecer 0.07" +#define MECER_VERSION "Mecer 0.08" /* Support functions */ static int mecer_claim(void); @@ -60,7 +60,7 @@ static item_t mecer_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_megatec-old.c b/drivers/nutdrv_qx_megatec-old.c index 4b6fdde126..4ff8721959 100644 --- a/drivers/nutdrv_qx_megatec-old.c +++ b/drivers/nutdrv_qx_megatec-old.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_megatec-old.h" -#define MEGATEC_OLD_VERSION "Megatec/old 0.07" +#define MEGATEC_OLD_VERSION "Megatec/old 0.08" /* qx2nut lookup table */ static item_t megatec_old_qx2nut[] = { @@ -42,7 +42,7 @@ static item_t megatec_old_qx2nut[] = { { "output.voltage", 0, NULL, "D\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "D\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "D\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "D\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_megatec.c b/drivers/nutdrv_qx_megatec.c index e22979ad13..79617fd97e 100644 --- a/drivers/nutdrv_qx_megatec.c +++ b/drivers/nutdrv_qx_megatec.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_megatec.h" -#define MEGATEC_VERSION "Megatec 0.06" +#define MEGATEC_VERSION "Megatec 0.07" /* qx2nut lookup table */ static item_t megatec_qx2nut[] = { @@ -42,7 +42,7 @@ static item_t megatec_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_mustek.c b/drivers/nutdrv_qx_mustek.c index 7990b41275..3536bfd09c 100644 --- a/drivers/nutdrv_qx_mustek.c +++ b/drivers/nutdrv_qx_mustek.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_mustek.h" -#define MUSTEK_VERSION "Mustek 0.07" +#define MUSTEK_VERSION "Mustek 0.08" /* qx2nut lookup table */ static item_t mustek_qx2nut[] = { @@ -42,7 +42,7 @@ static item_t mustek_qx2nut[] = { { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_q1.c b/drivers/nutdrv_qx_q1.c index 08073bca00..3a55fbad49 100644 --- a/drivers/nutdrv_qx_q1.c +++ b/drivers/nutdrv_qx_q1.c @@ -35,7 +35,7 @@ #include "nutdrv_qx_q1.h" -#define Q1_VERSION "Q1 0.07" +#define Q1_VERSION "Q1 0.08" /* qx2nut lookup table */ static item_t q1_qx2nut[] = { @@ -52,7 +52,7 @@ static item_t q1_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_qx_voltronic-qs.c b/drivers/nutdrv_qx_voltronic-qs.c index 43df438d04..d0a3cb2f7e 100644 --- a/drivers/nutdrv_qx_voltronic-qs.c +++ b/drivers/nutdrv_qx_voltronic-qs.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_voltronic-qs.h" -#define VOLTRONIC_QS_VERSION "Voltronic-QS 0.07" +#define VOLTRONIC_QS_VERSION "Voltronic-QS 0.09" /* Support functions */ static int voltronic_qs_claim(void); @@ -76,7 +76,7 @@ static item_t voltronic_qs_qx2nut[] = { { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "output.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ @@ -191,11 +191,19 @@ static void voltronic_qs_initups(void) /* Protocol used by the UPS */ static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuelen) { - if (strcasecmp(item->value, "V")) { - upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); - return -1; + int ret = -1; + + if (!strcasecmp(item->value, "V")) { + upsdebugx(2, "%s: detected V protocol [%s]", __func__, item->value); + ret = 0; + } else if (!strcasecmp(item->value, "H")) { + upsdebugx(2, "%s: detected H protocol [%s]", __func__, item->value); + ret = 0; } + if (ret == -1) { + upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); + } else { #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic push #endif @@ -205,12 +213,13 @@ static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuele #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY #pragma GCC diagnostic ignored "-Wformat-security" #endif - snprintf(value, valuelen, item->dfl, item->value); + snprintf(value, valuelen, item->dfl, item->value); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic pop #endif + } - return 0; + return ret; } diff --git a/drivers/nutdrv_qx_voltronic.c b/drivers/nutdrv_qx_voltronic.c index 8e2f2ad7ce..23e932f5c6 100644 --- a/drivers/nutdrv_qx_voltronic.c +++ b/drivers/nutdrv_qx_voltronic.c @@ -25,7 +25,7 @@ #include "nutdrv_qx.h" #include "nutdrv_qx_voltronic.h" -#define VOLTRONIC_VERSION "Voltronic 0.06" +#define VOLTRONIC_VERSION "Voltronic 0.08" /* Support functions */ static int voltronic_claim(void); @@ -1076,7 +1076,7 @@ static item_t voltronic_qx2nut[] = { { "ups.load", 0, NULL, "QGS\r", "", 76, '(', "", 29, 31, "%.0f", 0, NULL, NULL, NULL }, /* { "unknown.1", 0, NULL, "QGS\r", "", 76, '(', "", 33, 37, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ /* { "unknown.2", 0, NULL, "QGS\r", "", 76, '(', "", 39, 43, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ - { "battery.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 45, 49, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 45, 49, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, /* { "unknown.3", 0, NULL, "QGS\r", "", 76, '(', "", 51, 55, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ { "ups.temperature", 0, NULL, "QGS\r", "", 76, '(', "", 57, 61, "%.1f", 0, NULL, NULL, NULL }, { "ups.type", 0, NULL, "QGS\r", "", 76, '(', "", 63, 64, "%s", QX_FLAG_SEMI_STATIC, NULL, NULL, voltronic_status }, @@ -1130,7 +1130,7 @@ static item_t voltronic_qx2nut[] = { * 0 1 2 */ - { "battery.voltage", 0, NULL, "QBV\r", "", 21, '(', "", 1, 5, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QBV\r", "", 21, '(', "", 1, 5, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "battery_number", ST_FLAG_RW, voltronic_r_batt_numb, "QBV\r", "", 21, '(', "", 7, 9, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_batt_numb }, /* Number of batteries that make a pack */ { "battery.packs", ST_FLAG_RW, voltronic_r_batt_packs, "QBV\r", "", 21, '(', "", 10, 11, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, NULL }, /* Number of battery packs in parallel */ { "battery.charge", 0, NULL, "QBV\r", "", 21, '(', "", 13, 15, "%.0f", 0, NULL, NULL, NULL }, diff --git a/drivers/nutdrv_qx_zinto.c b/drivers/nutdrv_qx_zinto.c index d039ccdbb8..4713ff2e28 100644 --- a/drivers/nutdrv_qx_zinto.c +++ b/drivers/nutdrv_qx_zinto.c @@ -25,7 +25,7 @@ #include "nutdrv_qx_zinto.h" -#define ZINTO_VERSION "Zinto 0.06" +#define ZINTO_VERSION "Zinto 0.07" /* qx2nut lookup table */ static item_t zinto_qx2nut[] = { @@ -42,7 +42,7 @@ static item_t zinto_qx2nut[] = { { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, - { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, qx_multiply_battvolt }, { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, /* Status bits */ { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ diff --git a/drivers/nutdrv_siemens_sitop.c b/drivers/nutdrv_siemens_sitop.c index 1c1e9efdd1..1978f04022 100644 --- a/drivers/nutdrv_siemens_sitop.c +++ b/drivers/nutdrv_siemens_sitop.c @@ -56,7 +56,7 @@ #include "nut_stdint.h" #define DRIVER_NAME "Siemens SITOP UPS500 series driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" #define RX_BUFFER_SIZE 100 @@ -98,7 +98,7 @@ static void rm_buffer_head(unsigned int n) { /* parse incoming data from the UPS. * return true if something new was received. */ -static int check_for_new_data() { +static int check_for_new_data(void) { int new_data_received = 0; int done = 0; ssize_t num_received; @@ -110,7 +110,7 @@ static int check_for_new_data() { num_received = ser_get_buf(upsfd, rx_buffer + rx_count, RX_BUFFER_SIZE - rx_count, 0, 0); if (num_received < 0) { /* comm error */ - ser_comm_fail("error %zd while reading", num_received); + ser_comm_fail("error %" PRIiSIZE " while reading", num_received); /* discard any remaining old data from the receive buffer: */ rx_count = 0; /* try to re-open the serial port: */ @@ -277,7 +277,7 @@ void upsdrv_initups(void) { */ if (poll_interval > 5) { upslogx(LOG_NOTICE, - "Option poll_interval is recommended to be lower than 5 (found: %jd)", + "Option poll_interval is recommended to be lower than 5 (found: %" PRIdMAX ")", (intmax_t)poll_interval); } diff --git a/drivers/oneac.c b/drivers/oneac.c index 916fc72928..0bb30d1f47 100644 --- a/drivers/oneac.c +++ b/drivers/oneac.c @@ -41,13 +41,14 @@ #include "main.h" #include "serial.h" #include "oneac.h" +#include "nut_stdint.h" /* Prototypes to allow setting pointer before function is defined */ int setcmd(const char* varname, const char* setvalue); int instcmd(const char *cmdname, const char *extra); #define DRIVER_NAME "Oneac EG/ON/OZ/OB UPS driver" -#define DRIVER_VERSION "0.81" +#define DRIVER_VERSION "0.82" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -89,7 +90,7 @@ static ssize_t OneacGetResponse (char* chBuff, const size_t BuffSize, int Expect break; upsdebugx (3, - "!OneacGetResponse retry (%zd, %d)...", + "!OneacGetResponse retry (%" PRIiSIZE ", %d)...", return_val, Retries); } while (--Retries > 0); diff --git a/drivers/openups-hid.c b/drivers/openups-hid.c index d8be70d4cb..0a9eda9ad0 100644 --- a/drivers/openups-hid.c +++ b/drivers/openups-hid.c @@ -121,7 +121,7 @@ static const unsigned int therm_tbl[] = (unsigned int)0x3CC }; -static const unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]); +static const unsigned int therm_tbl_size = SIZEOF_ARRAY(therm_tbl); static const char *openups_charging_fun(double value); static const char *openups_discharging_fun(double value); diff --git a/drivers/optiups.c b/drivers/optiups.c index 13eedd5010..b3fe2634f2 100644 --- a/drivers/optiups.c +++ b/drivers/optiups.c @@ -25,9 +25,10 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #define DRIVER_NAME "Opti-UPS driver" -#define DRIVER_VERSION "1.02" +#define DRIVER_VERSION "1.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -61,7 +62,7 @@ upsdrv_info_t upsdrv_info = { "It works even with a pl2303 usb-serial converter." "\n" \ "**********************************************************" "\n" -/* See http://www.networkupstools.org/protocols/optiups.html and the end of this +/* See https://www.networkupstools.org/protocols/optiups.html and the end of this * file for more information on the cable and the OPTI-UPS serial protocol used on * at least the older OPTI UPS models (420E, 820ES). */ @@ -82,7 +83,8 @@ static char _buf[256]; static int optimodel = 0; enum { OPTIMODEL_DEFAULT = 0, - OPTIMODEL_ZINTO = 1 + OPTIMODEL_ZINTO = 1, + OPTIMODEL_PS = 2 }; @@ -122,6 +124,19 @@ static ezfill_t _pollv_zinto[] = { { "BT", "ups.temperature", 0 }, }; +/* When on a 220-2400V mains supply, the NV and OV commands return 115V values. FV + * returns a value that matches the DIP switch settings for 120/240V models, so + * it can be used to scale the valus from NV and OV. + * + * I suspect this will be the case for other Opti-UPS models, but as I can only + * test with a PS-1440RM at 230V the change is only applied to PowerSeries models. + */ +static ezfill_t _pollv_ps[] = { + { "OL", "ups.load", 1.0 }, + { "FF", "input.frequency", 0.1 }, + { "BT", "ups.temperature", 0 }, +}; + /* model "IO" is parsed differently in upsdrv_initinfo() */ static ezfill_t _initv[] = { { "IM", "ups.mfr", 0 }, @@ -157,7 +172,7 @@ static inline ssize_t optireadline(void) } } else - upsdebugx(1, "READ ERROR: %zd", r ); + upsdebugx(1, "READ ERROR: %" PRIiSIZE, r); return r; } @@ -346,7 +361,14 @@ void upsdrv_initinfo(void) optiquery( "ON" ); } - optifill( _initv, sizeof(_initv)/sizeof(_initv[0]) ); + /* Autodetect an Opti-UPS PS series */ + r = optiquery( "IO" ); + if ( r > 0 && !strncasecmp(_buf, "PS-", 3) ) + { + optimodel = OPTIMODEL_PS; + } + + optifill( _initv, SIZEOF_ARRAY(_initv) ); /* Parse out model into longer string -- is this really USEFUL??? */ r = optiquery( "IO" ); @@ -461,9 +483,32 @@ void upsdrv_updateinfo(void) /* read some easy settings */ if ( optimodel == OPTIMODEL_ZINTO ) - optifill( _pollv_zinto, sizeof(_pollv_zinto)/sizeof(_pollv_zinto[0]) ); + optifill( _pollv_zinto, SIZEOF_ARRAY(_pollv_zinto) ); + else if ( optimodel == OPTIMODEL_PS ) { + short inV, outV, fV; + + optifill( _pollv_ps, SIZEOF_ARRAY(_pollv_ps) ); + + r = optiquery( "NV" ); + str_to_short ( _buf, &inV, 10 ); + r = optiquery( "OV" ); + str_to_short ( _buf, &outV, 10 ); + + r = optiquery( "FV" ); + if ( r >= 1 ) + { + str_to_short ( _buf, &fV, 10 ); + if ( fV > 180 ) + { + inV = inV * 2; + outV = outV * 2; + } + } + dstate_setinfo( "input.voltage", "%d", inV ); + dstate_setinfo( "output.voltage", "%d", outV ); + } else - optifill( _pollv, sizeof(_pollv)/sizeof(_pollv[0]) ); + optifill( _pollv, SIZEOF_ARRAY(_pollv) ); /* Battery voltage is harder */ r = optiquery( "BV" ); @@ -474,8 +519,16 @@ void upsdrv_updateinfo(void) float p, v = strtol( _buf, NULL, 10 ) / 10.0; dstate_setinfo("battery.voltage", "%.1f", v ); - /* battery voltage range: 10.4 - 13.0 VDC */ - p = ((v - 10.4) / 2.6) * 100.0; + if (v > 20) + { + /* battery voltage range: 20.8 - 26.0 VDC */ + p = ((v - 20.8) / 5.2) * 100.0; + } + else + { + /* battery voltage range: 10.4 - 13.0 VDC */ + p = ((v - 10.4) / 2.6) * 100.0; + } if ( p > 100.0 ) p = 100.0; dstate_setinfo("battery.charge", "%.1f", p ); diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c index fb4b24e3b3..733989ef68 100644 --- a/drivers/phoenixcontact_modbus.c +++ b/drivers/phoenixcontact_modbus.c @@ -24,7 +24,7 @@ #include #define DRIVER_NAME "NUT PhoenixContact Modbus driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) #define MODBUS_SLAVE_ID 192 @@ -57,12 +57,12 @@ void upsdrv_initinfo(void) void upsdrv_updateinfo(void) { + uint16_t tab_reg[64]; + errcount = 0; upsdebugx(2, "upsdrv_updateinfo"); - uint16_t tab_reg[64]; - mrir(modbus_ctx, 29697, 3, tab_reg); status_init(); @@ -131,12 +131,11 @@ void upsdrv_updateinfo(void) } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); } void upsdrv_help(void) diff --git a/drivers/pijuice.c b/drivers/pijuice.c index a1b1db1845..3e43bfdf89 100644 --- a/drivers/pijuice.c +++ b/drivers/pijuice.c @@ -20,7 +20,7 @@ #include "main.h" #include -#include +#include "nut_stdint.h" /* * Linux I2C userland is a bit of a mess until distros refresh to @@ -53,7 +53,7 @@ * situation. */ #if WITH_LINUX_I2C -#if !HAVE_DECL_I2C_SMBUS_ACCESS +# if !HAVE_DECL_I2C_SMBUS_ACCESS static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, int size, union i2c_smbus_data *data) { @@ -70,9 +70,9 @@ static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, err = -errno; return err; } -#endif +# endif -#if !HAVE_DECL_I2C_SMBUS_READ_BYTE_DATA +# if !HAVE_DECL_I2C_SMBUS_READ_BYTE_DATA static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) { union i2c_smbus_data data; @@ -84,9 +84,9 @@ static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) else return 0x0FF & data.byte; } -#endif +# endif -#if !HAVE_DECL_I2C_SMBUS_WRITE_BYTE_DATA +# if !HAVE_DECL_I2C_SMBUS_WRITE_BYTE_DATA static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) { union i2c_smbus_data data; @@ -99,9 +99,9 @@ static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value else return 0x0FF & data.byte; } -#endif +# endif -#if !HAVE_DECL_I2C_SMBUS_READ_WORD_DATA +# if !HAVE_DECL_I2C_SMBUS_READ_WORD_DATA static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) { union i2c_smbus_data data; @@ -113,9 +113,9 @@ static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) else return 0x0FFFF & data.word; } -#endif +# endif -#if !HAVE_DECL_I2C_SMBUS_WRITE_WORD_DATA +# if !HAVE_DECL_I2C_SMBUS_WRITE_WORD_DATA static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) { union i2c_smbus_data data; @@ -128,9 +128,9 @@ static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 valu else return 0x0FFFF & data.word; } -#endif +# endif -#if !HAVE_DECL_I2C_SMBUS_READ_BLOCK_DATA +# if !HAVE_DECL_I2C_SMBUS_READ_BLOCK_DATA static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) { union i2c_smbus_data data; @@ -152,8 +152,8 @@ static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 l return values; } -#endif -#endif // if WITH_LINUX_I2C +# endif +#endif /* if WITH_LINUX_I2C */ #define STATUS_CMD 0x40 #define CHARGE_LEVEL_CMD 0x41 @@ -218,7 +218,7 @@ static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 l #define NOMINAL_BATTERY_VOLTAGE 4.18 #define DRIVER_NAME "PiJuice UPS driver" -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.11" static uint8_t i2c_address = 0x14; static uint8_t shutdown_delay = 30; @@ -305,7 +305,7 @@ static inline int open_i2c_bus(char *path, uint8_t addr) return file; } -static void get_charge_level_hi_res() +static void get_charge_level_hi_res(void) { uint8_t cmd = CHARGE_LEVEL_HI_RES_CMD; uint16_t data; @@ -334,11 +334,10 @@ static void get_charge_level_hi_res() dstate_setinfo( "battery.charge", "%02.1f", battery_charge_level ); } -static void get_status() +static void get_status(void) { - uint8_t cmd = STATUS_CMD; - uint8_t data; - char status_buf[ST_MAX_VALUE_LEN]; + uint8_t cmd = STATUS_CMD, data, batteryStatus, powerInput, powerInput5vIo; + char status_buf[ST_MAX_VALUE_LEN]; upsdebugx( 3, __func__ ); @@ -346,7 +345,7 @@ static void get_status() I2C_READ_BYTE( upsfd, cmd, __func__ ) - uint8_t batteryStatus = data >> 2 & 0x03; + batteryStatus = data >> 2 & 0x03; switch( batteryStatus ) { case BATT_NORMAL: @@ -373,7 +372,7 @@ static void get_status() upsdebugx( 1, "battery.status: UNKNOWN" ); } - uint8_t powerInput = data >> 4 & 0x03; + powerInput = data >> 4 & 0x03; switch( powerInput ) { case POWER_NOT_PRESENT: @@ -392,7 +391,7 @@ static void get_status() upsdebugx( 1, "Power Input: UNKNOWN" ); } - uint8_t powerInput5vIo = data >> 6 & 0x03; + powerInput5vIo = data >> 6 & 0x03; switch( powerInput5vIo ) { case POWER_NOT_PRESENT : @@ -535,7 +534,7 @@ static void get_status() } } -static void get_battery_temperature() +static void get_battery_temperature(void) { uint8_t cmd = BATTERY_TEMPERATURE_CMD; int16_t data; @@ -548,7 +547,7 @@ static void get_battery_temperature() dstate_setinfo( "battery.temperature", "%d", data ); } -static void get_battery_voltage() +static void get_battery_voltage(void) { uint8_t cmd = BATTERY_VOLTAGE_CMD; int16_t data; @@ -561,7 +560,7 @@ static void get_battery_voltage() dstate_setinfo( "battery.voltage", "%0.3f", data / 1000.0 ); } -static void get_battery_current() +static void get_battery_current(void) { uint8_t cmd = BATTERY_CURRENT_CMD; int16_t data; @@ -583,7 +582,7 @@ static void get_battery_current() dstate_setinfo( "battery.current", "%0.3f", data / 1000.0 ); } -static void get_io_voltage() +static void get_io_voltage(void) { uint8_t cmd = IO_VOLTAGE_CMD; int16_t data; @@ -596,7 +595,7 @@ static void get_io_voltage() dstate_setinfo( "input.voltage", "%.3f", data / 1000.0 ); } -static void get_io_current() +static void get_io_current(void) { uint8_t cmd = IO_CURRENT_CMD; int16_t data; @@ -618,7 +617,7 @@ static void get_io_current() dstate_setinfo( "input.current", "%.3f", data / 1000.0 ); } -static void get_firmware_version() +static void get_firmware_version(void) { uint8_t cmd = FIRMWARE_VERSION_CMD; uint16_t data; @@ -640,7 +639,7 @@ static void get_firmware_version() dstate_setinfo( "ups.firmware", "%d.%d", major, minor ); } -static void get_battery_profile() +static void get_battery_profile(void) { uint8_t cmd = BATTERY_PROFILE_CMD; __u8 block[I2C_SMBUS_BLOCK_MAX]; @@ -653,7 +652,7 @@ static void get_battery_profile() dstate_setinfo( "battery.capacity", "%0.3f", ( block[1] << 8 | block[0] ) / 1000.0 ); } -static void get_battery_profile_ext() +static void get_battery_profile_ext(void) { uint8_t cmd = BATTERY_EXT_PROFILE_CMD; __u8 block[I2C_SMBUS_BLOCK_MAX]; @@ -678,7 +677,7 @@ static void get_battery_profile_ext() } } -static void get_power_off() +static void get_power_off(void) { uint8_t cmd = POWER_OFF_CMD; uint8_t data; @@ -697,7 +696,7 @@ static void get_power_off() } } -static void set_power_off() +static void set_power_off(void) { uint8_t cmd = POWER_OFF_CMD; @@ -729,7 +728,7 @@ static void set_power_off() I2C_WRITE_BYTE( upsfd, cmd, shutdown_delay, __func__ ) } -static void get_time() +static void get_time(void) { uint8_t cmd = RTC_TIME_CMD; __u8 block[I2C_SMBUS_BLOCK_MAX]; @@ -755,7 +754,7 @@ static void get_time() dstate_setinfo( "ups.date", "%04d-%02d-%02d", year, month, day ); } -static void get_i2c_address() +static void get_i2c_address(void) { uint8_t cmd = I2C_ADDRESS_CMD; uint8_t data; diff --git a/drivers/powercom-hid.c b/drivers/powercom-hid.c index a3973ffc05..5f838e1bc7 100644 --- a/drivers/powercom-hid.c +++ b/drivers/powercom-hid.c @@ -4,6 +4,7 @@ * 2003 - 2009 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte + * 2020 - 2022 Jim Klimov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +26,7 @@ #include "powercom-hid.h" #include "usb-common.h" -#define POWERCOM_HID_VERSION "PowerCOM HID 0.6" +#define POWERCOM_HID_VERSION "PowerCOM HID 0.7" /* FIXME: experimental flag to be put in upsdrv_info */ /* PowerCOM */ @@ -370,21 +371,25 @@ static hid_info_t powercom_hid2nut[] = { { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, - { "battery.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion }, + { "battery.mfr.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, -/* { "unmapped.ups.battery.delaybeforestartup", 0, 0, "UPS.Battery.DelayBeforeStartup", NULL, "%.0f", 0, NULL }, */ -/* { "unmapped.ups.battery.initialized", 0, 0, "UPS.Battery.Initialized", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "unmapped.ups.battery.delaybeforestartup", 0, 0, "UPS.Battery.DelayBeforeStartup", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.battery.initialized", 0, 0, "UPS.Battery.Initialized", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info }, { "ups.beeper.status", 0, 0, "UPS.AudibleAlarmControl", NULL, "%s", 0, powercom_beeper_info }, { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.date", 0, 0, "UPS.PowerSummary.ManufacturerDate", NULL, "%s", HU_FLAG_STATIC, date_conversion }, { "ups.test.result", 0, 0, "UPS.Battery.Test", NULL, "%s", 0, test_read_info }, -/* { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ -/* { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ -/* { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ -/* { "unmapped.ups.iname", 0, 0, "UPS.iName", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ -/* { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "unmapped.ups.iname", 0, 0, "UPS.iName", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* The implementation of the HID path UPS.PowerSummary.DelayBeforeStartup is unconventional: * Read: @@ -422,10 +427,12 @@ static hid_info_t powercom_hid2nut[] = { { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL }, -/* { "unmapped.ups.powercom1", 0, 0, "UPS.POWERCOM1", NULL, "%.0f", 0, NULL }, broken pipe */ -/* { "unmapped.ups.powercom2", 0, 0, "UPS.POWERCOM2", NULL, "%.0f", 0, NULL }, broken pipe */ -/* { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, */ -/* { "unmapped.ups.shutdownimminent", 0, 0, "UPS.ShutdownImminent", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "unmapped.ups.powercom1", 0, 0, "UPS.POWERCOM1", NULL, "%.0f", 0, NULL }, /* broken pipe */ + { "unmapped.ups.powercom2", 0, 0, "UPS.POWERCOM2", NULL, "%.0f", 0, NULL }, /* broken pipe */ + { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.shutdownimminent", 0, 0, "UPS.ShutdownImminent", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ /* instcmds */ { "beeper.toggle", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, @@ -442,12 +449,16 @@ static hid_info_t powercom_hid2nut[] = { { "ups.serial", 0, 0, "PowercomUPS.PowercomSerialNumber", NULL, "%s", 0, stringid_conversion }, { "ups.mfr", 0, 0, "PowercomUPS.PowercomManufacturerName", NULL, "%s", 0, stringid_conversion }, -/* { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, is always 255 */ +#if WITH_UNMAPPED_DATA_POINTS + { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, /* is always 255 */ +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "ups.mfr.date", 0, 0, "PowercomUPS.PowercomManufacturerDate", NULL, "%s", 0, date_conversion }, { "battery.temperature", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomTemperature", NULL, "%.0f", 0, NULL }, { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%.1f", 0, NULL }, { "battery.charge", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomVoltage", NULL, "%.0f", 0, NULL }, -/* { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "input.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomFrequency", NULL, "%.0f", 0, NULL }, { "input.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, { "output.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, @@ -462,7 +473,9 @@ static hid_info_t powercom_hid2nut[] = { * bit 6 Disable NO LOAD SHUTDOWN (1 = ACTIVE) * bit 7 0 */ -/* { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_upsfail_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_replacebatt_conversion }, { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_test_conversion }, @@ -477,7 +490,9 @@ static hid_info_t powercom_hid2nut[] = { * bit 6 X * bit 7 SD mode display */ -/* { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_online_conversion }, /* Low battery status may not work */ { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_lowbatt_conversion }, @@ -486,7 +501,9 @@ static hid_info_t powercom_hid2nut[] = { { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_overload_conversion }, { "output.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomFrequency", NULL, "%.0f", 0, NULL }, { "ups.test.result", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomTest", NULL, "%s", 0, test_read_info }, -/* { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, */ +#if WITH_UNMAPPED_DATA_POINTS + { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, +#endif /* if WITH_UNMAPPED_DATA_POINTS */ { "ups.delay.shutdown", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "%.0f", 0, NULL }, { "ups.delay.start", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeStartup", NULL, "%.0f", 0, NULL }, { "load.off", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, diff --git a/drivers/powercom.c b/drivers/powercom.c index 6e58a3b98c..8b22f74c84 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -85,8 +85,8 @@ #include "powercom.h" #include "math.h" -#define DRIVER_NAME "PowerCom protocol UPS driver" -#define DRIVER_VERSION "0.19" +#define DRIVER_NAME "PowerCom protocol UPS driver" +#define DRIVER_VERSION "0.21" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -289,9 +289,6 @@ static unsigned int OPTImodels[] = {0,0,0,575,0,0,0,0,0,0,0,0,0,0,0,0}; * local used functions */ -static void shutdown_halt(void) - __attribute__((noreturn)); - static void shutdown_halt(void) { ser_send_char (upsfd, (unsigned char)SHUTDOWN); @@ -299,11 +296,9 @@ static void shutdown_halt(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[0]); ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); - exit (0); -} -static void shutdown_ret(void) - __attribute__((noreturn)); + set_exit_flag(-2); /* EXIT_SUCCESS */ +} static void shutdown_ret(void) { @@ -314,7 +309,7 @@ static void shutdown_ret(void) ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (return) initiated."); - exit (0); + set_exit_flag(-2); /* EXIT_SUCCESS */ } /* registered instant commands */ @@ -407,11 +402,11 @@ static int ups_getinfo(void) types[type].num_of_bytes_from_ups, 3, 0); if (c != (ssize_t)types[type].num_of_bytes_from_ups) { - upslogx(LOG_NOTICE, "data receiving error (%zd instead of %d bytes)", c, types[type].num_of_bytes_from_ups); + upslogx(LOG_NOTICE, "data receiving error (%" PRIiSIZE " instead of %d bytes)", c, types[type].num_of_bytes_from_ups); dstate_datastale(); return 0; } else - upsdebugx(5, "Num of bytes received from UPS: %zd", c); + upsdebugx(5, "Num of bytes received from UPS: %" PRIiSIZE, c); } /* optional dump of raw data */ @@ -770,8 +765,12 @@ void upsdrv_updateinfo(void) { char val[32]; - if (!ups_getinfo()){ - return; + if (!ups_getinfo()) { + /* https://github.com/networkupstools/nut/issues/356 */ + upsdebugx(1, "%s: failed to ups_getinfo() once, retrying for slower devices", __func__); + if (!ups_getinfo()) { + return; + } } /* input.frequency */ @@ -840,9 +839,6 @@ void upsdrv_updateinfo(void) } /* shutdown UPS */ -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* power down the attached load immediately */ diff --git a/drivers/powercom.h b/drivers/powercom.h index 4ac2f6329c..d9ef384dad 100644 --- a/drivers/powercom.h +++ b/drivers/powercom.h @@ -27,7 +27,9 @@ /* C-libary includes */ #include #include +#ifndef WIN32 #include +#endif #include /* nut includes */ diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index df4e91f3e5..316950a47c 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -23,7 +23,7 @@ #include /* pm_err_t and other beasts */ #define DRIVER_NAME "Powerman PDU client driver" -#define DRIVER_VERSION "0.12" +#define DRIVER_VERSION "0.13" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -133,13 +133,11 @@ void upsdrv_initinfo(void) /* FIXME: no need for setvar (ex for outlet.n.delay.*)!? */ } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* FIXME: shutdown all outlets? */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ @@ -191,6 +189,8 @@ static int reconnect_ups(void) { pm_err_t rv; + dstate_setinfo("driver.state", "reconnect.trying"); + upsdebugx(4, "==================================================="); upsdebugx(4, "= connection lost with Powerman, try to reconnect ="); upsdebugx(4, "==================================================="); @@ -202,6 +202,7 @@ static int reconnect_ups(void) if ((rv = pm_connect(device_path, NULL, &pm, 0)) != PM_ESUCCESS) return 0; else { + dstate_setinfo("driver.state", "quiet"); upsdebugx(4, "connection restored with Powerman"); return 1; } diff --git a/drivers/powerp-bin.c b/drivers/powerp-bin.c index 89b06876d8..0c7da014a0 100644 --- a/drivers/powerp-bin.c +++ b/drivers/powerp-bin.c @@ -430,7 +430,7 @@ static ssize_t powpan_status(status_t *status) /* * WRITE D\r * READ #VVL.CTF.....\r - * 01234567890123 + * 01234567890123 */ ret = ser_send_pace(upsfd, UPSDELAY, "D\r"); @@ -545,7 +545,7 @@ static int powpan_updateinfo(void) } /* !OB && !TEST */ - if (!(status.flags[0] & 0x84)) { + if (!(status.flags[0] & 0x84) && status.o_volt) { if (status.o_volt < 0.5 * status.i_volt) { upsdebugx(2, "%s: output voltage too low", __func__); @@ -628,7 +628,7 @@ static ssize_t powpan_initups(void) upsdebug_hex(3, "read", powpan_answer, (size_t)ret); if (ret < 20) { - upsdebugx(2, "Expected 20 bytes but only got %zd", ret); + upsdebugx(2, "Expected 20 bytes but only got %" PRIiSIZE, ret); continue; } diff --git a/drivers/powerp-txt.c b/drivers/powerp-txt.c index 6f22a608d7..0390a7ca64 100644 --- a/drivers/powerp-txt.c +++ b/drivers/powerp-txt.c @@ -30,16 +30,20 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #include "powerp-txt.h" -#define POWERPANEL_TEXT_VERSION "Powerpanel-Text 0.5" +#include + +#define POWERPANEL_TEXT_VERSION "Powerpanel-Text 0.6" typedef struct { float i_volt; float o_volt; int o_load; int b_chrg; + unsigned char has_u_temp; int u_temp; float i_freq; unsigned char flags[2]; @@ -49,8 +53,6 @@ typedef struct { float o_freq; unsigned char has_runtime; int runtime; - int c_unknwn; - float q_unknwn; } status_t; static long ondelay = 1; /* minutes */ @@ -260,6 +262,7 @@ static void powpan_initinfo(void) /* * WRITE P3\r * READ #12.0,002,008.0,00\r + * #12,2x1,12,0,1,8\r (CST135XLU) */ if (powpan_command("P3\r") > 0) { @@ -277,6 +280,7 @@ static void powpan_initinfo(void) /* * WRITE P2\r * READ #1200,0720,120,47,63\r + * #1350,810,120,57,63,11.3\r (CST135XLU) */ if (powpan_command("P2\r") > 0) { @@ -300,6 +304,7 @@ static void powpan_initinfo(void) /* * WRITE P1\r * READ #120,138,088,20\r + * #120,139,100,0,300\r (CST135XLU) */ if (powpan_command("P1\r") > 0) { @@ -380,101 +385,97 @@ static void powpan_initinfo(void) static ssize_t powpan_status(status_t *status) { ssize_t ret; + ssize_t i; + ssize_t valid = 0; + int code = -1; + char value[32]; + ssize_t ofs = 0; + char seen_i_freq = 0; - ser_flush_io(upsfd); + memset(status, 0, sizeof(status_t)); /* * WRITE D\r * READ #I119.0O119.0L000B100T027F060.0S..\r * #I118.0O118.0L029B100F060.0R0218S..\r - * 01234567890123456789012345678901234 - * 0 1 2 3 + * #I118.1O118.1L13B100V27.5F60.0HF60.0R65Q1.4S\x80\x84\xc0\x88\x80W\r (CST135XLU) + * 01234567890123456789012345678901234567890123 + * 0 1 2 3 4 */ - ret = ser_send_pace(upsfd, UPSDELAY, "D\r"); - - if (ret < 0) { - upsdebug_with_errno(3, "send"); - return -1; - } - - if (ret == 0) { - upsdebug_with_errno(3, "send: timeout"); - return -1; - } - - upsdebug_hex(3, "send", "D\r", 2); - - usleep(200000); - - ret = ser_get_buf_len(upsfd, powpan_answer, 35, SER_WAIT_SEC, SER_WAIT_USEC); - - if (ret < 0) { - upsdebug_with_errno(3, "read"); - upsdebug_hex(4, " \\_", powpan_answer, 35); + ret = powpan_command("D\r"); + if (ret <= 0) return -1; - } - if (ret == 0) { - upsdebugx(3, "read: timeout"); - upsdebug_hex(4, " \\_", powpan_answer, 35); + if (powpan_answer[0] != '#') { + upsdebugx(2, "Expected start character '#', but got '%c'", powpan_answer[0]); return -1; } - upsdebug_hex(3, "read", powpan_answer, (size_t)ret); - - ret = sscanf(powpan_answer, "#I%fO%fL%dB%dT%dF%fS%2c\r", - &status->i_volt, &status->o_volt, &status->o_load, - &status->b_chrg, &status->u_temp, &status->i_freq, - status->flags); - - if (ret >= 7) { - status->has_b_volt = 0; - status->has_o_freq = 0; - status->has_runtime = 0; - } else { - - ret = sscanf(powpan_answer, "#I%fO%fL%dB%dF%fR%dS%2c\r", - &status->i_volt, &status->o_volt, &status->o_load, - &status->b_chrg, &status->i_freq, &status->runtime, - status->flags); - - if (ret >= 7) { - status->has_b_volt = 0; - status->has_o_freq = 0; - status->has_runtime = 1; - } - - } - if (ret < 7) { - ret = ser_get_buf_len(upsfd, powpan_answer+35, 23, SER_WAIT_SEC, SER_WAIT_USEC); - - if (ret < 0) { - upsdebug_with_errno(3, "read"); - upsdebug_hex(4, " \\_", powpan_answer+35, 23); - return -1; - } + for (i = 1; i <= ret; i++) { + if (i == ret || isalpha(powpan_answer[i])) { + value[ofs++] = '\0'; + valid++; + + switch (code) { + case 'I': + status->i_volt = strtof(value, NULL); + break; + case 'O': + status->o_volt = strtof(value, NULL); + break; + case 'L': + status->o_load = strtol(value, NULL, 10); + break; + case 'B': + status->b_chrg = strtol(value, NULL, 10); + break; + case 'V': + status->b_volt = strtof(value, NULL); + status->has_b_volt = 1; + break; + case 'T': + status->u_temp = strtol(value, NULL, 10); + status->has_u_temp = 1; + break; + case 'F': + status->i_freq = strtof(value, NULL); + seen_i_freq = 1; + break; + case 'H': + status->o_freq = strtof(value, NULL); + status->has_o_freq = 1; + break; + case 'R': + status->runtime = strtol(value, NULL, 10); + status->has_runtime = 1; + break; + case 'S': + memcpy(&status->flags, value, 2); + break; + default: + /* We didn't really find valid data */ + valid--; + break; + } + + code = powpan_answer[i]; + ofs = 0; + + /* + * Depending on device/firmware, the output frequency is coded as + * either an H, HF, or a second F (seen once transfered to battery) + */ + if (seen_i_freq && code == 'F') + code = 'H'; - if (ret == 0) { - upsdebugx(3, "read: timeout"); - upsdebug_hex(4, " \\_", powpan_answer+35, 23); - return -1; + continue; } - upsdebug_hex(3, "read", powpan_answer, (size_t)ret); - - ret = sscanf(powpan_answer, "#I%fO%fL%dB%dV%fT%dF%fH%fR%dC%dQ%fS%2c\r", - &status->i_volt, &status->o_volt, &status->o_load, - &status->b_chrg, &status->b_volt, &status->u_temp, - &status->i_freq, &status->o_freq, &status->runtime, - &status->c_unknwn, &status->q_unknwn, status->flags); - status->has_b_volt = 1; - status->has_o_freq = 1; - status->has_runtime = 1; - dstate_setinfo("battery.voltage.nominal", "%g", 72.0); - dstate_setinfo("output.voltage.nominal", "%g", 120.0); + value[ofs++] = powpan_answer[i]; } - if (ret < 7) { + /* if we didn't get at least 3 values consider it a failure */ + if (valid < 3) { upsdebugx(4, "Parsing status string failed"); return -1; } @@ -494,10 +495,15 @@ static int powpan_updateinfo(void) dstate_setinfo("output.voltage", "%.1f", status.o_volt); dstate_setinfo("ups.load", "%d", status.o_load); dstate_setinfo("input.frequency", "%.1f", status.i_freq); - dstate_setinfo("ups.temperature", "%d", status.u_temp); + if (status.has_u_temp) { + dstate_setinfo("ups.temperature", "%d", status.u_temp); + } dstate_setinfo("battery.charge", "%d", status.b_chrg); if (status.has_b_volt) { dstate_setinfo("battery.voltage", "%.1f", status.b_volt); + if (status.b_volt > 20.0 && status.b_volt < 28.0) { + dstate_setinfo("battery.voltage.nominal", "%g", 24.0); + } } if (status.has_o_freq) { dstate_setinfo("output.frequency", "%.1f", status.o_freq); @@ -566,6 +572,7 @@ static ssize_t powpan_initups(void) /* * WRITE P4\r * READ #BC1200 ,1.600,000000000000,CYBER POWER + * #CST135XLU,BF01403AAH2,CR7EV2002320,CyberPower Systems Inc.,,, * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ @@ -576,7 +583,7 @@ static ssize_t powpan_initups(void) } if (ret < 46) { - upsdebugx(2, "Expected 46 bytes, but only got %zd", ret); + upsdebugx(2, "Expected 46 bytes, but only got %" PRIiSIZE, ret); continue; } diff --git a/drivers/powerpanel.c b/drivers/powerpanel.c index 6c677b8ef9..9ec775784e 100644 --- a/drivers/powerpanel.c +++ b/drivers/powerpanel.c @@ -36,7 +36,7 @@ static subdriver_t *subdriver[] = { }; #define DRIVER_NAME "CyberPower text/binary protocol UPS driver" -#define DRIVER_VERSION "0.28" +#define DRIVER_VERSION "0.29" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/powervar-hid.c b/drivers/powervar-hid.c old mode 100755 new mode 100644 index a37978aa48..4821ca88c2 --- a/drivers/powervar-hid.c +++ b/drivers/powervar-hid.c @@ -119,14 +119,16 @@ static int powervar_claim(HIDDevice_t *hd) case POSSIBLY_SUPPORTED: /* by default, reject, unless the productid option is given */ if (getval("productid")) { - usb->hid_rep_index = 1; + if (!getval("usb_hid_rep_index")) + usb->hid_rep_index = 1; return 1; } possibly_supported("Powervar", hd); return 0; case SUPPORTED: - usb->hid_rep_index = 1; + if (!getval("usb_hid_rep_index")) + usb->hid_rep_index = 1; return 1; case NOT_SUPPORTED: diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c deleted file mode 100644 index aa52db3d8a..0000000000 --- a/drivers/powerware-mib.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* powerware-mib.c - data to monitor Powerware UPS with NUT - * (using MIBs described in stdupsv1.mib and Xups.mib) - * - * Copyright (C) - * 2005-2006 Olli Savia - * 2005-2006 Niels Baggesen - * 2015-2021 Eaton (author: Arnaud Quette ) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "powerware-mib.h" -#if WITH_SNMP_LKP_FUN -/* FIXME: shared helper code, need to be put in common */ -#include "eaton-pdu-marlin-helpers.h" -#endif - -#define PW_MIB_VERSION "1.01" - -/* TODO: more sysOID and MIBs support: - * - * Powerware UPS (Ingrasys X-SLOT and BD-SLOT): ".1.3.6.1.4.1.534.1" - * Powerware PXGX cards: ".1.3.6.1.4.1.534.2.12" - * PXGX 2000 cards (UPS): Get xupsIdentModel (".1.3.6.1.4.1.534.1.1.2.0") - * PXGX 1000 cards (PDU/RPP/RPM): Get pduNumPanels ".1.3.6.1.4.1.534.6.6.4.1.1.1.4.0" - */ - -/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) - * Eaton Gigabit Network Card (Genepi) */ -#define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" -/* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ -#define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" - -/* SNMP OIDs set */ -#define PW_OID_MFR_NAME "1.3.6.1.4.1.534.1.1.1.0" /* XUPS-MIB::xupsIdentManufacturer.0 */ -#define PW_OID_MODEL_NAME "1.3.6.1.4.1.534.1.1.2.0" /* XUPS-MIB::xupsIdentModel.0 */ -#define PW_OID_FIRMREV "1.3.6.1.4.1.534.1.1.3.0" /* XUPS-MIB::xupsIdentSoftwareVersion.0 */ - -#define PW_OID_BATT_RUNTIME "1.3.6.1.4.1.534.1.2.1.0" /* XUPS-MIB::xupsBatTimeRemaining.0 */ -#define PW_OID_BATT_VOLTAGE "1.3.6.1.4.1.534.1.2.2.0" /* XUPS-MIB::xupsBatVoltage.0 */ -#define PW_OID_BATT_CURRENT "1.3.6.1.4.1.534.1.2.3.0" /* XUPS-MIB::xupsBatCurrent.0 */ -#define PW_OID_BATT_CHARGE "1.3.6.1.4.1.534.1.2.4.0" /* XUPS-MIB::xupsBatCapacity.0 */ -#define PW_OID_BATT_STATUS "1.3.6.1.4.1.534.1.2.5.0" /* XUPS-MIB::xupsBatteryAbmStatus.0 */ - -#define PW_OID_IN_FREQUENCY "1.3.6.1.4.1.534.1.3.1.0" /* XUPS-MIB::xupsInputFrequency.0 */ -#define PW_OID_IN_LINE_BADS "1.3.6.1.4.1.534.1.3.2.0" /* XUPS-MIB::xupsInputLineBads.0 */ -#define PW_OID_IN_LINES "1.3.6.1.4.1.534.1.3.3.0" /* XUPS-MIB::xupsInputNumPhases.0 */ -#define PW_OID_IN_VOLTAGE "1.3.6.1.4.1.534.1.3.4.1.2" /* XUPS-MIB::xupsInputVoltage */ -#define PW_OID_IN_CURRENT "1.3.6.1.4.1.534.1.3.4.1.3" /* XUPS-MIB::xupsInputCurrent */ -#define PW_OID_IN_POWER "1.3.6.1.4.1.534.1.3.4.1.4" /* XUPS-MIB::xupsInputWatts */ - -#define PW_OID_OUT_LOAD "1.3.6.1.4.1.534.1.4.1.0" /* XUPS-MIB::xupsOutputLoad.0 */ -#define PW_OID_OUT_FREQUENCY "1.3.6.1.4.1.534.1.4.2.0" /* XUPS-MIB::xupsOutputFrequency.0 */ -#define PW_OID_OUT_LINES "1.3.6.1.4.1.534.1.4.3.0" /* XUPS-MIB::xupsOutputNumPhases.0 */ -#define PW_OID_OUT_VOLTAGE "1.3.6.1.4.1.534.1.4.4.1.2" /* XUPS-MIB::xupsOutputVoltage */ -#define PW_OID_OUT_CURRENT "1.3.6.1.4.1.534.1.4.4.1.3" /* XUPS-MIB::xupsOutputCurrent */ -#define PW_OID_OUT_POWER "1.3.6.1.4.1.534.1.4.4.1.4" /* XUPS-MIB::xupsOutputWatts */ -#define PW_OID_POWER_STATUS "1.3.6.1.4.1.534.1.4.5.0" /* XUPS-MIB::xupsOutputSource.0 */ - -#define PW_OID_BY_FREQUENCY "1.3.6.1.4.1.534.1.5.1.0" /* XUPS-MIB::xupsBypassFrequency.0 */ -#define PW_OID_BY_LINES "1.3.6.1.4.1.534.1.5.2.0" /* XUPS-MIB::xupsBypassNumPhases.0 */ -#define PW_OID_BY_VOLTAGE "1.3.6.1.4.1.534.1.5.3.1.2" /* XUPS-MIB::xupsBypassVoltage */ - -#define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ - -#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ -#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ -#define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ -#define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ -#define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ - -#define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ -#define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ -#define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ -#define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ - -#define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1.0" /* XUPS-MIB::xupsAlarms */ -#define PW_OID_ALARM_OB "1.3.6.1.4.1.534.1.7.3" /* XUPS-MIB::xupsOnBattery */ -#define PW_OID_ALARM_LB "1.3.6.1.4.1.534.1.7.4" /* XUPS-MIB::xupsLowBattery */ - -#define IETF_OID_AGENTREV "1.3.6.1.2.1.33.1.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion.0 */ -#define IETF_OID_IDENT "1.3.6.1.2.1.33.1.1.5.0" /* UPS-MIB::upsIdentName.0 */ -#define IETF_OID_CONF_OUT_VA "1.3.6.1.2.1.33.1.9.5.0" /* UPS-MIB::upsConfigOutputVA.0 */ -#define IETF_OID_CONF_RUNTIME_LOW "1.3.6.1.2.1.33.1.9.7.0" /* UPS-MIB::upsConfigLowBattTime.0 */ -#define IETF_OID_LOAD_LEVEL "1.3.6.1.2.1.33.1.4.4.1.5" /* UPS-MIB::upsOutputPercentLoad */ -#define IETF_OID_AUTO_RESTART "1.3.6.1.2.1.33.1.8.5.0" /* UPS-MIB::upsAutoRestart */ - -/* Delay before powering off in seconds */ -#define DEFAULT_OFFDELAY 30 -/* Delay before powering on in seconds */ -#define DEFAULT_ONDELAY 20 -/* Default shutdown.return delay in seconds */ -#define DEFAULT_SHUTDOWNDELAY 0 - -static info_lkp_t pw_alarm_ob[] = { - { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_alarm_lb[] = { - { 1, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_pwr_info[] = { - { 1, "" /* other */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "OFF" /* none */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "OL" /* normal */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "BYPASS" /* bypass */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "OB" /* battery */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "OL BOOST" /* booster */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "OL TRIM" /* reducer */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "OL" /* parallel capacity */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "OL" /* parallel redundancy */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "OL" /* high efficiency */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - /* Extended status values */ - { 240, "OB" /* battery (0xF0) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 100, "BYPASS" /* maintenanceBypass (0x64) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 96, "BYPASS" /* Bypass (0x60) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 81, "OL" /* high efficiency (0x51) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 80, "OL" /* normal (0x50) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 64, "OL" /* UPS supporting load, normal degraded mode (0x40) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 16, "OFF" /* none (0x10) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -/* FIXME: mapped to ups.type, but should be output.source or ups.mode (need RFC) - * to complement the above ups.status - * along with having ups.type as described hereafter*/ -/* FIXME: should be used by ups.mode or output.source (need RFC); - * Note: this define is not set via project options; code hidden with - * commit to "snmp-ups: support newer Genepi management cards" - */ -#ifdef USE_PW_MODE_INFO -static info_lkp_t pw_mode_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "normal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "parallel capacity" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "parallel redundancy" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "high efficiency" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - /* Extended status values, - * FIXME: check for source and completion */ - { 240, "" /* battery (0xF0) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 100, "" /* maintenanceBypass (0x64) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 96, "" /* Bypass (0x60) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 81, "high efficiency" /* high efficiency (0x51) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 80, "normal" /* normal (0x50) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 64, "" /* UPS supporting load, normal degraded mode (0x40) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 16, "" /* none (0x10) */ -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#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, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* None; use the Table of Elements */ - { 0x0010, "Off-line switcher, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0020, "Line-Interactive UPS, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0021, "Line-Interactive UPS, Two Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0022, "Line-Interactive UPS, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0030, "Dual AC Input, On-Line UPS, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0031, "Dual AC Input, On-Line UPS, Two Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0032, "Dual AC Input, On-Line UPS, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0040, "On-Line UPS, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0041, "On-Line UPS, Two Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0042, "On-Line UPS, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0050, "Parallel Redundant On-Line UPS, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0051, "Parallel Redundant On-Line UPS, Two Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0052, "Parallel Redundant On-Line UPS, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0060, "Parallel for Capacity On-Line UPS, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0061, "Parallel for Capacity On-Line UPS, Two Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0062, "Parallel for Capacity On-Line UPS, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0102, "System Bypass Module, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0122, "Hot-Tie Cabinet, Three Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0200, "Outlet Controller, Single Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0x0222, "Dual AC Input Static Switch Module, 3 Phase" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -/* Legacy implementation */ -static info_lkp_t pw_battery_abm_status[] = { - { 1, "CHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "DISCHRG" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -/* - { 3, "Floating" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ -/* - { 4, "Resting" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ -/* - { 5, "Unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, -*/ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_abm_status_info[] = { - { 1, "charging" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "discharging" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "floating" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "resting" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Undefined - ABM is not activated */ - { 6, "disabled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* ABM Charger Disabled */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_batt_test_info[] = { - { 1, "Unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "Done and passed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "Done and error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "In progress" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "Not supported" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "Inhibited" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "Scheduled" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_yes_no_info[] = { - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_outlet_status_info[] = { - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* pendingOff, transitional status */ - { 4, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* pendingOn, transitional status */ - /* { 5, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, unknown */ - /* { 6, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, reserved */ - { 7, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Failed in Closed position */ - { 8, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Failed in Open position */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_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" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* openWithNotice */ - { 4, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* closedWithNotice */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -#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" -#if WITH_SNMP_LKP_FUN - , eaton_sensor_temperature_unit_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_sensor_temperature_read_info[] = { - { 0, "dummy" -#if WITH_SNMP_LKP_FUN - , su_temperature_read_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -#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" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "celsius" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "fahrenheit" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -#endif // WITH_SNMP_LKP_FUN - -static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { - { 0, "normal-opened" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "normal-closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_ambient_drycontacts_state_info[] = { - { 0, "inactive" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "active" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -static info_lkp_t pw_emp002_ambient_presence_info[] = { - { 0, "unknown" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationOK */ - { 3, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* communicationLost */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ -static info_lkp_t pw_threshold_status_info[] = { - { 0, "good" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "warning-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "critical-low" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "warning-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "critical-high" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ -static info_lkp_t pw_threshold_temperature_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high temperature warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high temperature critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; -static info_lkp_t pw_threshold_humidity_alarms_info[] = { - { 0, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* No threshold triggered */ - { 1, "low humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning low threshold triggered */ - { 2, "low humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical low threshold triggered */ - { 3, "high humidity warning!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Warning high threshold triggered */ - { 4, "high humidity critical!" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* Critical high threshold triggered */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } -}; - -/* Snmp2NUT lookup table */ - -static snmp_info_t pw_mib[] = { - /* FIXME: miss device page! */ - /* UPS page */ - /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MFR_NAME, "", - SU_FLAG_STATIC, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MODEL_NAME, "", - SU_FLAG_STATIC, NULL }, - /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_FIRMREV, "", - 0, NULL }, - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", - 0, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", - SU_FLAG_STATIC, NULL }, - { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", - 0, NULL }, - /* FIXME: should be removed in favor of output.power */ - { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", - 0, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* xupsOutputWatts.1.0; Value (Integer): 300 */ - { "ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", - 0, NULL }, - - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", - SU_STATUS_PWR, &pw_pwr_info[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", - SU_STATUS_BATT, &pw_alarm_ob[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_LB, "", - SU_STATUS_BATT, &pw_alarm_lb[0] }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", - SU_STATUS_BATT, &pw_battery_abm_status[0] }, -#ifdef USE_PW_MODE_INFO - /* FIXME: should be ups.mode or output.source (need RFC) */ - /* Note: this define is not set via project options; code hidden with - * commit to "snmp-ups: support newer Genepi management cards" */ - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", - SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, -#endif /* USE_PW_MODE_INFO */ - /* xupsTopologyType.0; Value (Integer): 32 */ - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0] }, - /* FIXME: should be removed in favor of their output. equivalent! */ - { "ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", - 0, NULL }, - /* FIXME: should be removed in favor of output.power.nominal */ - { "ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", - 0, NULL }, - /* XUPS-MIB::xupsEnvAmbientTemp.0 */ - { "ups.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.1.0", "", 0, NULL }, - /* FIXME: These 2 data needs RFC! */ - /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ - { "ups.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.2.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ - { "ups.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.3.0", "", 0, NULL }, - /* XUPS-MIB::xupsTestBatteryStatus */ - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.8.2.0", "", 0, &pw_batt_test_info[0] }, - /* UPS-MIB::upsAutoRestart */ - { "ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, &pw_yes_no_info[0] }, - /* XUPS-MIB::xupsBatteryAbmStatus.0 */ - { "battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.2.5.0", "", SU_STATUS_BATT, &pw_abm_status_info[0] }, - - /* Battery page */ - { "battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "", - 0, NULL }, - { "battery.runtime", 0, 1.0, PW_OID_BATT_RUNTIME, "", - 0, NULL }, - { "battery.voltage", 0, 1.0, PW_OID_BATT_VOLTAGE, "", - 0, NULL }, - { "battery.current", 0, 0.1, PW_OID_BATT_CURRENT, "", - 0, NULL }, - { "battery.runtime.low", 0, 60.0, IETF_OID_CONF_RUNTIME_LOW, "", - 0, NULL }, - { "battery.date", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.1.2.6.0", NULL, SU_FLAG_OK, &su_convert_to_iso_date_info[FUNMAP_USDATE_TO_ISODATE] }, - - /* Output page */ - { "output.phases", 0, 1.0, PW_OID_OUT_LINES, "", 0, NULL }, - /* XUPS-MIB::xupsOutputFrequency.0 */ - { "output.frequency", 0, 0.1, "1.3.6.1.4.1.534.1.4.2.0", "", 0, NULL }, - /* XUPS-MIB::xupsConfigOutputFreq.0 */ - { "output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL }, - /* XUPS-MIB::xupsOutputVoltage.1 */ - { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* xupsOutputVoltage.1.0; Value (Integer): 230 */ - { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL }, - /* XUPS-MIB::xupsConfigOutputVoltage.0 */ - { "output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL }, - /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ - { "output.voltage.low", 0, 1.0, ".1.3.6.1.4.1.534.1.10.6.0", "", 0, NULL }, - /* XUPS-MIB::xupsConfigHighOutputVoltageLimit.0 */ - { "output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL }, - { "output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", - SU_OUTPUT_1, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* xupsOutputCurrent.1.0; Value (Integer): 0 */ - { "output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", - SU_OUTPUT_1, NULL }, - { "output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", - SU_OUTPUT_1, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ - { "output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", - 0, NULL }, - /* Duplicate of "ups.realpower.nominal" - * FIXME: map either ups or output, but not both (or have an auto-remap) */ - { "output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", - 0, NULL }, - { "output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", - SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", - SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".3", "", - SU_OUTPUT_3, NULL }, - { "output.L1.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", - SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 1.0, PW_OID_OUT_CURRENT ".2", "", - SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 1.0, PW_OID_OUT_CURRENT ".3", "", - SU_OUTPUT_3, NULL }, - { "output.L1.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", - SU_OUTPUT_3, NULL }, - { "output.L2.realpower", 0, 1.0, PW_OID_OUT_POWER ".2", "", - SU_OUTPUT_3, NULL }, - { "output.L3.realpower", 0, 1.0, PW_OID_OUT_POWER ".3", "", - SU_OUTPUT_3, NULL }, - /* FIXME: should better be output.Lx.load */ - { "output.L1.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".1", "", - SU_OUTPUT_3, NULL }, - { "output.L2.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".2", "", - SU_OUTPUT_3, NULL }, - { "output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", - SU_OUTPUT_3, NULL }, - { "output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", - 0, NULL }, - - /* Input page */ - { "input.phases", 0, 1.0, PW_OID_IN_LINES, "", - 0, NULL }, - { "input.frequency", 0, 0.1, PW_OID_IN_FREQUENCY, "", - 0, NULL }, - { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", - SU_INPUT_1, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ - { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", - SU_INPUT_1, NULL }, - - /* XUPS-MIB::xupsConfigInputVoltage.0 */ - { "input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL }, - { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", - SU_INPUT_1, NULL }, - { "input.L1-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".1", "", - SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".2", "", - SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".3", "", - SU_INPUT_3, NULL }, - { "input.L1.current", 0, 1.0, PW_OID_IN_CURRENT ".1", "", - SU_INPUT_3, NULL }, - { "input.L2.current", 0, 1.0, PW_OID_IN_CURRENT ".2", "", - SU_INPUT_3, NULL }, - { "input.L3.current", 0, 1.0, PW_OID_IN_CURRENT ".3", "", - SU_INPUT_3, NULL }, - { "input.L1.realpower", 0, 1.0, PW_OID_IN_POWER ".1", "", - SU_INPUT_3, NULL }, - { "input.L2.realpower", 0, 1.0, PW_OID_IN_POWER ".2", "", - SU_INPUT_3, NULL }, - { "input.L3.realpower", 0, 1.0, PW_OID_IN_POWER ".3", "", - SU_INPUT_3, NULL }, - { "input.quality", 0, 1.0, PW_OID_IN_LINE_BADS, "", - 0, NULL }, - - /* FIXME: this segfaults! do we assume the same number of bypass phases as input phases? - { "input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", 0, NULL }, */ - { "input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL }, - { "input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", - SU_INPUT_1, NULL }, - /* Duplicate of the above entry, but pointing at the first index */ - /* xupsBypassVoltage.1.0; Value (Integer): 244 */ - { "input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", - SU_INPUT_1, NULL }, - { "input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", - SU_INPUT_3, NULL }, - { "input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", - SU_INPUT_3, NULL }, - { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", - SU_INPUT_3, NULL }, - - /* Outlet page */ - /* Master outlet id always equal to 0 */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL }, - /* XUPS-MIB:: xupsSwitchable.0 */ - { "outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &pw_yes_no_info[0] }, - /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL }, - /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, - /* This MIB does not provide outlets switchability info. So map to a nearby - OID, for data activation, and map all values to "yes" */ - { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, - /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ - { "outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &pw_outlet_status_info[0] }, - - /* Ambient collection */ - /* EMP001 (legacy) mapping */ - /* XUPS-MIB::xupsEnvRemoteTemp.0 */ - { "ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ - { "ambient.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.9.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvRemoteTempUpperLimit.0 */ - { "ambient.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.10.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvRemoteHumidity.0 */ - { "ambient.humidity", 0, 1.0, "1.3.6.1.4.1.534.1.6.6.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvRemoteHumidityLowerLimit.0 */ - { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, - /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ - { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, - /* XUPS-MIB::xupsContactDescr.n */ - { "ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL }, - { "ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL }, - /* XUPS-MIB::xupsContactState.n */ - { "ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, - { "ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, - - /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ - /* Warning: indexes start at '1' not '0'! */ - /* sensorCount.0 */ - { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, - /* CommunicationStatus.n */ - { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", - NULL, SU_AMBIENT_TEMPLATE, &pw_emp002_ambient_presence_info[0] }, - /* sensorName.n: OctetString EMPDT1H1C2 @1 */ - { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorManufacturer.n */ - { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorModel.n */ - { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorSerialNumber.n */ - { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorUuid.n */ - { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorAddress.n */ - { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* sensorFirmwareVersion.n */ - { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, - /* temperatureUnit.1 - * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, - /* temperatureValue.n.1 */ - { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, -#if WITH_SNMP_LKP_FUN - &pw_sensor_temperature_read_info[0] -#else - NULL -#endif - }, - { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_temperature_alarms_info[0] }, - /* FIXME: ambient.n.temperature.{minimum,maximum} */ - /* temperatureThresholdLowCritical.n.1 */ - { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* temperatureThresholdLowWarning.n.1 */ - { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* temperatureThresholdHighWarning.n.1 */ - { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* temperatureThresholdHighCritical.n.1 */ - { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* humidityValue.n.1 */ - { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, - { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_humidity_alarms_info[0] }, - /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ - /* humidityThresholdLowCritical.n.1 */ - { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* humidityThresholdLowWarning.n.1 */ - { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* humidityThresholdHighWarning.n.1 */ - { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* humidityThresholdHighCritical.n.1 */ - { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - /* digitalInputName.n.{1,2} */ - { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, - { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, - /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, - { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, - /* XUPS-MIB::xupsContactState.n */ - { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, - { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, - - /* instant commands */ - { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* Shed load and restart when line power back on; cannot be canceled */ - { "shutdown.return", 0, DEFAULT_SHUTDOWNDELAY, PW_OID_CONT_LOAD_SHED_AND_RESTART, "", - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ - { "shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* XUPS-MIB::xupsControlOutputOffDelay */ - /* load off after 1 sec, shortest possible delay; 0 cancels */ - { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* XUPS-MIB::xupsControlOutputOnDelay */ - /* load on after 1 sec, shortest possible delay; 0 cancels */ - { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, - SU_TYPE_CMD | SU_FLAG_OK, NULL }, - - /* Delays handling: - * 0-n :Time in seconds until the command is issued - * -1:Cancel a pending Off/On command */ - /* XUPS-MIB::xupsRecepOffDelaySecs.n */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", - "0", SU_TYPE_CMD | SU_OUTLET, NULL }, - /* XUPS-MIB::xupsRecepOnDelaySecs.n */ - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", - "0", SU_TYPE_CMD | SU_OUTLET, NULL }, - /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", - NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, - /* XUPS-MIB::xupsRecepOnDelaySecs.n */ - { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", - NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, - - { "ups.alarms", 0, 1.0, PW_OID_ALARMS, "", - 0, NULL }, - - /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } -} ; - -static alarms_info_t pw_alarms[] = { - /* xupsLowBattery */ - { PW_OID_ALARM_LB, "LB", NULL }, - /* xupsOutputOverload */ - { ".1.3.6.1.4.1.534.1.7.7", "OVER", "Output overload!" }, - /* xupsInternalFailure */ - { ".1.3.6.1.4.1.534.1.7.8", NULL, "Internal failure!" }, - /* xupsBatteryDischarged */ - { ".1.3.6.1.4.1.534.1.7.9", NULL, "Battery discharged!" }, - /* xupsInverterFailure */ - { ".1.3.6.1.4.1.534.1.7.10", NULL, "Inverter failure!" }, - /* xupsOnBypass - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.11", "BYPASS", "On bypass!" }, - /* xupsBypassNotAvailable - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.12", NULL, "Bypass not available!" }, - /* xupsOutputOff - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.13", "OFF", "Output off!" }, - /* xupsInputFailure - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.14", NULL, "Input failure!" }, - /* xupsBuildingAlarm - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.15", NULL, "Building alarm!" }, - /* xupsShutdownImminent */ - { ".1.3.6.1.4.1.534.1.7.16", NULL, "Shutdown imminent!" }, - /* xupsOnInverter - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.17", NULL, "On inverter!" }, - /* xupsBreakerOpen - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.20", NULL, "Breaker open!" }, - /* xupsAlarmBatteryBad */ - { ".1.3.6.1.4.1.534.1.7.23", "RB", "Battery bad!" }, - /* xupsOutputOffAsRequested - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.24", "OFF", "Output off as requested!" }, - /* xupsDiagnosticTestFailed - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.25", NULL, "Diagnostic test failure!" }, - /* xupsCommunicationsLost */ - { ".1.3.6.1.4.1.534.1.7.26", NULL, "Communication with UPS lost!" }, - /* xupsUpsShutdownPending */ - { ".1.3.6.1.4.1.534.1.7.27", NULL, "Shutdown pending!" }, - /* xupsAmbientTempBad */ - { ".1.3.6.1.4.1.534.1.7.29", NULL, "Bad ambient temperature!" }, - /* xupsLossOfRedundancy */ - { ".1.3.6.1.4.1.534.1.7.30", NULL, "Redundancy lost!" }, - /* xupsAlarmTempBad */ - { ".1.3.6.1.4.1.534.1.7.31", NULL, "Bad temperature!" }, - /* xupsAlarmChargerFailed */ - { ".1.3.6.1.4.1.534.1.7.32", NULL, "Charger failure!" }, - /* xupsAlarmFanFailure */ - { ".1.3.6.1.4.1.534.1.7.33", NULL, "Fan failure!" }, - /* xupsAlarmFuseFailure */ - { ".1.3.6.1.4.1.534.1.7.34", NULL, "Fuse failure!" }, - /* xupsPowerSwitchBad */ - { ".1.3.6.1.4.1.534.1.7.35", NULL, "Powerswitch failure!" }, - /* xupsModuleFailure */ - { ".1.3.6.1.4.1.534.1.7.36", NULL, "Parallel or composite module failure!" }, - /* xupsOnAlternatePowerSource - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.37", NULL, "Using alternative power source!" }, - /* xupsAltPowerNotAvailable - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.38", NULL, "Alternative power source unavailable!" }, - /* xupsRemoteTempBad */ - { ".1.3.6.1.4.1.534.1.7.40", NULL, "Bad remote temperature!" }, - /* xupsRemoteHumidityBad */ - { ".1.3.6.1.4.1.534.1.7.41", NULL, "Bad remote humidity!" }, - /* xupsAlarmOutputBad */ - { ".1.3.6.1.4.1.534.1.7.42", NULL, "Bad output condition!" }, - /* xupsAlarmAwaitingPower - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? */ - { ".1.3.6.1.4.1.534.1.7.43", NULL, "Awaiting power!" }, - /* xupsOnMaintenanceBypass - * FIXME: informational (not an alarm), - * to RFC'ed for device.event? - * FIXME: NUT currently doesn't distinguish between Maintenance and - * Automatic Bypass (both published as "ups.alarm: BYPASS) - * Should we make the distinction? */ - { ".1.3.6.1.4.1.534.1.7.44", "BYPASS", "On maintenance bypass!" }, - - - /* end of structure. */ - { NULL, NULL, NULL } -} ; - - -mib2nut_info_t powerware = { "pw", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, POWERWARE_SYSOID , pw_alarms }; -mib2nut_info_t pxgx_ups = { "pxgx_ups", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, EATON_PXGX_SYSOID , pw_alarms }; diff --git a/drivers/raritan-pdu-mib.c b/drivers/raritan-pdu-mib.c index 210619b20b..49000e1365 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.80" /* Raritan MIB * this one uses the same MIB as Eaton Revelation, @@ -39,85 +39,71 @@ #define DO_CYCLE "2" static info_lkp_t raritan_pdu_outlet_status_info[] = { - { -1, "error" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "cycling" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* transitional status */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "error"), + info_lkp_default(0, "off"), + info_lkp_default(1, "on"), + info_lkp_default(2, "cycling"), /* transitional status */ + info_lkp_sentinel }; /* Snmp2NUT lookup table for Raritan MIB */ static snmp_info_t raritan_mib[] = { + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.6.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.6.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.13.0", - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.1.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.temperature", 0, 1, ".1.3.6.1.4.1.13742.1.3.1.5.0", NULL, 0, NULL }, + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.13.0", + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("ups.temperature", 0, 1, ".1.3.6.1.4.1.13742.1.3.1.5.0", NULL, 0, NULL), /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, ".1.3.6.1.4.1.13742.1.2.1.0", "0", 0, NULL }, - { "outlet.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.1" ".0", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.2" ".0", NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.3" ".0", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.4" ".0", NULL, 0, NULL }, + snmp_info_default("outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.13742.1.2.1.0", "0", 0, NULL), + snmp_info_default("outlet.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.1" ".0", NULL, 0, NULL), + snmp_info_default("outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.2" ".0", NULL, 0, NULL), + snmp_info_default("outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.3" ".0", NULL, 0, NULL), + snmp_info_default("outlet.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.4" ".0", NULL, 0, NULL), /* outlet template definition * Caution: the index of the data start at 0, while the name is +1 * ie outlet.1 => .0 */ - { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL }, - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.2.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_FLAG_OK | SU_OUTLET, &raritan_pdu_outlet_status_info[0] }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.4.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.current.maximum", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.5.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.7.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.voltage", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.6.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.1.2.2.1.9.%i", NULL, SU_OUTLET, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.8.%i", NULL, SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.2.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_FLAG_OK | SU_OUTLET, &raritan_pdu_outlet_status_info[0]), + snmp_info_default("outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.4.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.current.maximum", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.5.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.7.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.voltage", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.6.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.1.2.2.1.9.%i", NULL, SU_OUTLET, NULL), + snmp_info_default("outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.8.%i", NULL, SU_OUTLET, NULL), /* FIXME: * - delay for startup/shutdown sequence @@ -130,15 +116,16 @@ static snmp_info_t raritan_mib[] = { /* instant commands. */ /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ /* no counterpart found! - { "outlet.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_OFF, SU_TYPE_CMD, NULL }, - { "outlet.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_ON, SU_TYPE_CMD, NULL }, - { "outlet.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_CYCLE, SU_TYPE_CMD, NULL }, */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_OFF, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_ON, SU_TYPE_CMD, NULL), + snmp_info_default("outlet.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_CYCLE, SU_TYPE_CMD, NULL), + */ + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t raritan = { "raritan", RARITAN_MIB_VERSION, NULL, RARITAN_OID_MODEL_NAME, raritan_mib, RARITAN_SYSOID, NULL }; diff --git a/drivers/raritan-px2-mib.c b/drivers/raritan-px2-mib.c index af8966255a..5d9a39ecc5 100644 --- a/drivers/raritan-px2-mib.c +++ b/drivers/raritan-px2-mib.c @@ -6,6 +6,10 @@ * * Based on initial work and data from Opengear * + * NOTE: Many readings allow for PDU ID which is hard-coded to ".1" in + * the mapping tables below at this time. This should be extended to NUT + * support for "daisy-chain" concept which appeared later than this driver. + * * 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 @@ -23,7 +27,7 @@ #include "raritan-px2-mib.h" -#define RARITAN_PX2_MIB_VERSION "0.3" +#define RARITAN_PX2_MIB_VERSION "0.41" #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" @@ -31,663 +35,567 @@ /* info elements */ /* FIXME: triage between status and alarms, and make it compliant! */ static info_lkp_t raritanpx2_outlet_status_info[] = { - { -1, "unavailable" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "open" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "closed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "belowLowerCritical" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 3, "belowLowerWarning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 4, "normal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 5, "aboveUpperWarning" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 6, "aboveUpperCritical" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 7, "on" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 8, "off" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 9, "detected" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 10, "notDetected" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 11, "alarmed" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 12, "ok" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 13, "marginal" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 14, "fail" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 15, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 16, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 17, "standby" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 18, "one" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 19, "two" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 20, "inSync" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 21, "outOfSync" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, "NULL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "unavailable"), + info_lkp_default(0, "open"), + info_lkp_default(1, "closed"), + info_lkp_default(2, "belowLowerCritical"), + info_lkp_default(3, "belowLowerWarning"), + info_lkp_default(4, "normal"), + info_lkp_default(5, "aboveUpperWarning"), + info_lkp_default(6, "aboveUpperCritical"), + info_lkp_default(7, "on"), + info_lkp_default(8, "off"), + info_lkp_default(9, "detected"), + info_lkp_default(10, "notDetected"), + info_lkp_default(11, "alarmed"), + info_lkp_default(12, "ok"), + info_lkp_default(13, "marginal"), + info_lkp_default(14, "fail"), + info_lkp_default(15, "yes"), + info_lkp_default(16, "no"), + info_lkp_default(17, "standby"), + info_lkp_default(18, "one"), + info_lkp_default(19, "two"), + info_lkp_default(20, "inSync"), + info_lkp_default(21, "outOfSync"), + info_lkp_default(22, "i1OpenFault"), + info_lkp_default(23, "i1ShortFault"), + info_lkp_default(24, "i2OpenFault"), + info_lkp_default(25, "i2ShortFault"), + info_lkp_default(26, "fault"), + info_lkp_default(27, "warning"), + info_lkp_default(28, "critical"), + info_lkp_default(29, "selfTest"), + info_lkp_default(30, "nonRedundant"), + info_lkp_sentinel }; static info_lkp_t raritanpx2_outlet_switchability_info[] = { - { -1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 1, "yes" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 2, "no" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(-1, "yes"), + info_lkp_default(1, "yes"), + info_lkp_default(2, "no"), + info_lkp_sentinel }; /* PDU2-MIB Snmp2NUT lookup table */ static snmp_info_t raritan_px2_mib[] = { + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("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 }, + snmp_info_default("device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.2.1", + "Raritan", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* pduModel.1 = STRING: PX2-5475 */ - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", - "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", + "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* pduSerialNumber.1 = STRING: QFC3950619 */ - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.4.1", - NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + snmp_info_default("device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.4.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* pxMACAddress.1 = STRING: 0:d:5d:b:49:0 */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.11.1", - NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.11.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* boardVersion.1.mainController.1 = STRING: 0x01 */ /* FIXME: not compliant! to be RFC'ed */ - { "device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* FIXME: move to device collection! */ /* Wrong OID! - * { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL },*/ + * snmp_info_default("ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL),*/ /* boardFirmwareVersion.1.mainController.1 = STRING: 2.4.3.5-40298 */ - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* pduName.1 = STRING: my PX */ - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.13.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", - "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.13.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL), + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", + "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* Input data: * Units are given in inletSensorUnits.1.1.%i * Value should be scaled by inletSensorDecimalDigits.1.1.%i * For example, if the value is 1 and inletSensorDecimalDigits is 2, then actual value is 0.01. */ /* measurementsInletSensorValue.1.1.rmsCurrent = Gauge32: 10 (A) */ - { "input.load", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.load", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* measurementsInletSensorValue.1.1.rmsVoltage = Gauge32: 119 (V) */ - { "input.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.4", NULL, SU_FLAG_OK, NULL), /* measurementsInletSensorValue.1.1.activePower = Gauge32: 10 (W) */ - { "input.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.5", NULL, SU_FLAG_OK, NULL), /* measurementsInletSensorValue.1.1.apparentPower = Gauge32: 122 (VA) */ - { "input.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.6", NULL, SU_FLAG_OK, NULL), /* measurementsInletSensorValue.1.1.powerFactor = Gauge32: 8 (none) */ /* FIXME: need RFC! */ - { "input.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("input.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.7", NULL, SU_FLAG_OK, NULL), /* measurementsInletSensorValue.1.1.activeEnergy = Gauge32: 193359 (wattHour) */ - /* { "unmapped.measurementsInletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, */ + /* snmp_info_default("unmapped.measurementsInletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL), */ /* inletPlug.1.1 = INTEGER: plugIEC320C20(6) */ /* FIXME: need RFC (input.type | [input.]inlet.type...) and standardization - * { "unmapped.inletPlug", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.4.1.1", NULL, SU_FLAG_OK, NULL },*/ + * snmp_info_default("unmapped.inletPlug", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.4.1.1", NULL, SU_FLAG_OK, NULL),*/ /* outletCount.1 = INTEGER: 24 */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.4.1", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.4.1", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL), /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ /* outletName.1.%i = STRING: */ - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.3.1.%i", NULL, SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.3.1.%i", NULL, SU_OUTLET, NULL), /* outletSwitchingState.1.%i = INTEGER: on(7) */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.4.1.2.1.3.1.%i", NULL, SU_OUTLET, &raritanpx2_outlet_status_info[0] }, + snmp_info_default("outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.4.1.2.1.3.1.%i", NULL, SU_OUTLET, &raritanpx2_outlet_status_info[0]), /* outletLabel.1.%i = STRING: 1 */ - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.2.1.%i", "%i", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.id", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.2.1.%i", "%i", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, NULL), /* outletReceptacle.1.1 = INTEGER: receptacleNEMA520R(37) */ /* FIXME: need RFC and standardization - * { "outlet.%i.type", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.4.1.%i", NULL, SU_OUTLET | SU_FLAG_OK, NULL }, */ + * snmp_info_default("outlet.%i.type", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.4.1.%i", NULL, SU_OUTLET | SU_FLAG_OK, NULL), */ /* RMS Current (divide by 10). e.g. 5 == 0.5A */ /* measurementsOutletSensorValue.1.%i.rmsCurrent = Gauge32: 10 */ - { "outlet.%i.current", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.1", NULL, SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.current", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.1", NULL, SU_OUTLET | SU_FLAG_OK, NULL), /* measurementsOutletSensorValue.1.%i.rmsVoltage = Gauge32: 119 */ - { "outlet.%i.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.4", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.4", "%i", SU_OUTLET | SU_FLAG_OK, NULL), /* measurementsOutletSensorValue.1.%i.activePower = Gauge32: 10 */ - { "outlet.%i.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.5", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.5", "%i", SU_OUTLET | SU_FLAG_OK, NULL), /* measurementsOutletSensorValue.1.%i.apparentPower = Gauge32: 122 */ - { "outlet.%i.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.6", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.6", "%i", SU_OUTLET | SU_FLAG_OK, NULL), /* measurementsOutletSensorValue.1.%i.powerFactor = Gauge32: 8 */ - { "outlet.%i.powerfactor", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.7", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + snmp_info_default("outlet.%i.powerfactor", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.7", "%i", SU_OUTLET | SU_FLAG_OK, NULL), /* measurementsOutletSensorValue.1.1.activeEnergy = Gauge32: 89890 */ /* FIXME: - * { "unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, */ + * snmp_info_default("unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL), */ /* measurementsOutletSensorValue.1.1.onOff = Gauge32: 0 */ /* FIXME: - * { "unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.14", NULL, SU_FLAG_OK, NULL }, */ + * snmp_info_default("unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.14", NULL, SU_FLAG_OK, NULL), */ /* outletSwitchable.1.%i = INTEGER: true(1) */ - { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.28.1.%i", "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, &raritanpx2_outlet_switchability_info[0] }, + snmp_info_default("outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.28.1.%i", "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, &raritanpx2_outlet_switchability_info[0]), /* instant commands. */ /* switchingOperation.1.1 = INTEGER: on(1) */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "2", SU_TYPE_CMD | SU_OUTLET, NULL }, + snmp_info_default("outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL), + snmp_info_default("outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "2", SU_TYPE_CMD | SU_OUTLET, NULL), -#ifdef DEBUG +#if WITH_UNMAPPED_DATA_POINTS || (defined DEBUG) /* pduCount.0 = INTEGER: 1 */ /* FIXME: part of daisychain support, RFC device.count */ - { "device.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.1.0", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("device.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.1.0", NULL, SU_FLAG_OK, NULL), +#if WITH_UNMAPPED_DATA_POINTS /* pduRatedVoltage.1 = STRING: 100-120V */ - { "unmapped.pduRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.5.1", NULL, SU_FLAG_OK, NULL), /* pduRatedCurrent.1 = STRING: 16A */ - { "unmapped.pduRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.6.1", NULL, SU_FLAG_OK, NULL), /* pduRatedFrequency.1 = STRING: 50/60Hz */ - { "unmapped.pduRatedFrequency", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduRatedFrequency", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.7.1", NULL, SU_FLAG_OK, NULL), /* pduRatedVA.1 = STRING: 1.6-1.9kVA */ - { "unmapped.pduRatedVA", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.8.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduRatedVA", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.8.1", NULL, SU_FLAG_OK, NULL), /* pduImage.1 = STRING: */ - { "unmapped.pduImage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.9.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduImage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.9.1", NULL, SU_FLAG_OK, NULL), /* inletCount.1 = INTEGER: 1 */ - { "unmapped.inletCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.2.1", NULL, SU_FLAG_OK, NULL), /* overCurrentProtectorCount.1 = INTEGER: 0 */ - { "unmapped.overCurrentProtectorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.overCurrentProtectorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.3.1", NULL, SU_FLAG_OK, NULL), /* inletControllerCount.1 = INTEGER: 0 */ - { "unmapped.inletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.5.1", NULL, SU_FLAG_OK, NULL), /* outletControllerCount.1 = INTEGER: 6 */ - { "unmapped.outletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.6.1", NULL, SU_FLAG_OK, NULL), /* externalSensorCount.1 = INTEGER: 16 */ - { "unmapped.externalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.externalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.7.1", NULL, SU_FLAG_OK, NULL), /* pxIPAddress.1 = IpAddress: 192.168.20.188 */ - { "unmapped.pxIPAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.8.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pxIPAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.8.1", NULL, SU_FLAG_OK, NULL), /* netmask.1 = IpAddress: 255.255.255.0 */ - { "unmapped.netmask", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.9.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.netmask", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.9.1", NULL, SU_FLAG_OK, NULL), /* gateway.1 = IpAddress: 192.168.20.254 */ - { "unmapped.gateway", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.10.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.gateway", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.10.1", NULL, SU_FLAG_OK, NULL), /* utcOffset.1 = STRING: -5:00 */ - { "unmapped.utcOffset", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.12.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.utcOffset", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.12.1", NULL, SU_FLAG_OK, NULL), /* externalSensorsZCoordinateUnits.1 = INTEGER: rackUnits(0) */ - { "unmapped.externalSensorsZCoordinateUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.34.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.externalSensorsZCoordinateUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.34.1", NULL, SU_FLAG_OK, NULL), /* unitDeviceCapabilities.1 = BITS: 00 00 00 00 00 00 */ - { "unmapped.unitDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.35.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.unitDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.35.1", NULL, SU_FLAG_OK, NULL), /* outletSequencingDelay.1 = Gauge32: 200 */ - { "unmapped.outletSequencingDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.36.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSequencingDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.36.1", NULL, SU_FLAG_OK, NULL), /* globalOutletPowerCyclingPowerOffPeriod.1 = Gauge32: 10 */ - { "unmapped.globalOutletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.37.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.globalOutletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.37.1", NULL, SU_FLAG_OK, NULL), /* globalOutletStateOnStartup.1 = INTEGER: lastKnownState(2) */ - { "unmapped.globalOutletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.38.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.globalOutletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.38.1", NULL, SU_FLAG_OK, NULL), /* outletPowerupSequence.1 = STRING: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 */ - { "unmapped.outletPowerupSequence", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.39.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletPowerupSequence", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.39.1", NULL, SU_FLAG_OK, NULL), /* pduPowerCyclingPowerOffPeriod.1 = Gauge32: 3 */ - { "unmapped.pduPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.40.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.40.1", NULL, SU_FLAG_OK, NULL), /* pduDaisychainMemberType.1 = INTEGER: standalone(0) */ - { "unmapped.pduDaisychainMemberType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.41.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pduDaisychainMemberType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.41.1", NULL, SU_FLAG_OK, NULL), /* managedExternalSensorCount.1 = INTEGER: 0 */ - { "unmapped.managedExternalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.42.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.managedExternalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.42.1", NULL, SU_FLAG_OK, NULL), /* pxInetAddressType.1 = INTEGER: ipv4(1) */ - { "unmapped.pxInetAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.50.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pxInetAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.50.1", NULL, SU_FLAG_OK, NULL), /* pxInetIPAddress.1 = Hex-STRING: C0 A8 14 BC */ - { "unmapped.pxInetIPAddress", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.51.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pxInetIPAddress", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.51.1", NULL, SU_FLAG_OK, NULL), /* pxInetNetmask.1 = Hex-STRING: FF FF FF 00 */ - { "unmapped.pxInetNetmask", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.52.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pxInetNetmask", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.52.1", NULL, SU_FLAG_OK, NULL), /* pxInetGateway.1 = Hex-STRING: C0 A8 14 FE */ - { "unmapped.pxInetGateway", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.53.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.pxInetGateway", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.53.1", NULL, SU_FLAG_OK, NULL), /* loadShedding.1 = INTEGER: false(2) */ - { "unmapped.loadShedding", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.55.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.loadShedding", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.55.1", NULL, SU_FLAG_OK, NULL), /* serverCount.1 = INTEGER: 8 */ - { "unmapped.serverCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.56.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.serverCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.56.1", NULL, SU_FLAG_OK, NULL), /* inrushGuardDelay.1 = Gauge32: 200 */ - { "unmapped.inrushGuardDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.57.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inrushGuardDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.57.1", NULL, SU_FLAG_OK, NULL), /* cascadedDeviceConnected.1 = INTEGER: false(2) */ - { "unmapped.cascadedDeviceConnected", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.58.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.cascadedDeviceConnected", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.58.1", NULL, SU_FLAG_OK, NULL), /* synchronizeWithNTPServer.1 = INTEGER: false(2) */ - { "unmapped.synchronizeWithNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.59.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.synchronizeWithNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.59.1", NULL, SU_FLAG_OK, NULL), /* useDHCPProvidedNTPServer.1 = INTEGER: true(1) */ - { "unmapped.useDHCPProvidedNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.60.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.useDHCPProvidedNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.60.1", NULL, SU_FLAG_OK, NULL), /* firstNTPServerAddressType.1 = INTEGER: unknown(0) */ - { "unmapped.firstNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.61.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.firstNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.61.1", NULL, SU_FLAG_OK, NULL), /* firstNTPServerAddress.1 = "" */ - { "unmapped.firstNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.62.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.firstNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.62.1", NULL, SU_FLAG_OK, NULL), /* secondNTPServerAddressType.1 = INTEGER: unknown(0) */ - { "unmapped.secondNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.63.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.secondNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.63.1", NULL, SU_FLAG_OK, NULL), /* secondNTPServerAddress.1 = "" */ - { "unmapped.secondNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.64.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.secondNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.64.1", NULL, SU_FLAG_OK, NULL), /* wireCount.1 = INTEGER: 0 */ - { "unmapped.wireCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.65.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.wireCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.65.1", NULL, SU_FLAG_OK, NULL), /* transferSwitchCount.1 = INTEGER: 0 */ - { "unmapped.transferSwitchCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.66.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.transferSwitchCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.66.1", NULL, SU_FLAG_OK, NULL), /* boardVersion.1.outletController.{1-6} = STRING: 60 */ - { "unmapped.boardVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.boardVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* boardFirmwareVersion.1.outletController.{1-6} = STRING: 1F */ - { "unmapped.boardFirmwareVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.boardFirmwareVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.3.1", NULL, SU_FLAG_OK, NULL), /* boardFirmwareTimeStamp.1.mainController.1 = Gauge32: 0 */ - { "unmapped.boardFirmwareTimeStamp", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.boardFirmwareTimeStamp", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL), /* dataLogging.1 = INTEGER: true(1) */ - { "unmapped.dataLogging", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dataLogging", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* measurementPeriod.1 = INTEGER: 1 */ - { "unmapped.measurementPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.measurementPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.2.1", NULL, SU_FLAG_OK, NULL), /* measurementsPerLogEntry.1 = INTEGER: 60 */ - { "unmapped.measurementsPerLogEntry", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.measurementsPerLogEntry", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.3.1", NULL, SU_FLAG_OK, NULL), /* logSize.1 = INTEGER: 120 */ - { "unmapped.logSize", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.logSize", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.4.1", NULL, SU_FLAG_OK, NULL), /* dataLoggingEnableForAllSensors.1 = INTEGER: false(2) */ - { "unmapped.dataLoggingEnableForAllSensors", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.5.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.dataLoggingEnableForAllSensors", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.5.1", NULL, SU_FLAG_OK, NULL), /* inletLabel.1.1 = STRING: I1 */ - { "unmapped.inletLabel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletLabel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.2.1.1", NULL, SU_FLAG_OK, NULL), /* inletName.1.1 = STRING: */ - { "unmapped.inletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.3.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.3.1.1", NULL, SU_FLAG_OK, NULL), /* inletPoleCount.1.1 = INTEGER: 2 */ - { "unmapped.inletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.5.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.5.1.1", NULL, SU_FLAG_OK, NULL), /* inletRatedVoltage.1.1 = STRING: 100-120V */ - { "unmapped.inletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.6.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.6.1.1", NULL, SU_FLAG_OK, NULL), /* inletRatedCurrent.1.1 = STRING: 16A */ - { "unmapped.inletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.7.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.7.1.1", NULL, SU_FLAG_OK, NULL), /* inletDeviceCapabilities.1.1 = BITS: 9F 00 00 00 00 00 rmsCurrent(0) rmsVoltage(3) activePower(4) apparentPower(5) powerFactor(6) activeEnergy(7) */ - { "unmapped.inletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.10.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.10.1.1", NULL, SU_FLAG_OK, NULL), /* inletPoleCapabilities.1.1 = BITS: 00 00 00 00 00 00 */ - { "unmapped.inletPoleCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.11.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletPoleCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.11.1.1", NULL, SU_FLAG_OK, NULL), /* inletPlugDescriptor.1.1 = STRING: IEC 60320 C20 */ - { "unmapped.inletPlugDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.12.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletPlugDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.12.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.rmsCurrent = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.rmsVoltage = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.activePower = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.apparentPower = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.powerFactor = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorLogAvailable.1.1.activeEnergy = INTEGER: true(1) */ - { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.rmsCurrent = INTEGER: amp(2) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.rmsVoltage = INTEGER: volt(1) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.activePower = INTEGER: watt(3) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.apparentPower = INTEGER: voltamp(4) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.powerFactor = INTEGER: none(-1) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorUnits.1.1.activeEnergy = INTEGER: wattHour(5) */ - { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.rmsCurrent = Gauge32: 1 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.rmsVoltage = Gauge32: 0 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.powerFactor = Gauge32: 2 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorDecimalDigits.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.rmsCurrent = Gauge32: 100 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.rmsVoltage = Gauge32: 100 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.activePower = Gauge32: 300 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.apparentPower = Gauge32: 200 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.powerFactor = Gauge32: 500 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorAccuracy.1.1.activeEnergy = Gauge32: 100 */ - { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.rmsCurrent = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.rmsVoltage = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.activePower = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.apparentPower = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.powerFactor = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorResolution.1.1.activeEnergy = Gauge32: 1 */ - { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.rmsCurrent = Gauge32: 120 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.rmsVoltage = Gauge32: 5 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.activePower = Gauge32: 120 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.apparentPower = Gauge32: 120 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.powerFactor = Gauge32: 50 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorTolerance.1.1.activeEnergy = Gauge32: 120 */ - { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.rmsCurrent = Gauge32: 7680 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.rmsVoltage = Gauge32: 264 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.activePower = Gauge32: 202752 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.apparentPower = Gauge32: 202752 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.powerFactor = Gauge32: 100 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorMaximum.1.1.activeEnergy = Gauge32: 4294967295 */ - { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.rmsCurrent = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.rmsVoltage = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorMinimum.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.rmsCurrent = Gauge32: 10 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.rmsVoltage = Gauge32: 2 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorHysteresis.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.rmsCurrent = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.rmsVoltage = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorStateChangeDelay.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.8", NULL, SU_FLAG_OK, NULL), -/* Inlet thresholds */ + /* Inlet thresholds */ /* inletSensorLowerCriticalThreshold.1.1.rmsCurrent = Gauge32: 0 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerCriticalThreshold.1.1.rmsVoltage = Gauge32: 94 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerCriticalThreshold.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerCriticalThreshold.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerCriticalThreshold.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerCriticalThreshold.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.rmsCurrent = Gauge32: 0 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.rmsVoltage = Gauge32: 97 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorLowerWarningThreshold.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.rmsCurrent = Gauge32: 128 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.rmsVoltage = Gauge32: 127 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperCriticalThreshold.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.rmsCurrent = Gauge32: 104 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.rmsVoltage = Gauge32: 124 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.activePower = Gauge32: 0 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.apparentPower = Gauge32: 0 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.powerFactor = Gauge32: 0 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorUpperWarningThreshold.1.1.activeEnergy = Gauge32: 0 */ - { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.8", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.rmsCurrent = BITS: 30 upperWarning(2) upperCritical(3) */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.1", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.rmsVoltage = BITS: F0 lowerCritical(0) lowerWarning(1) upperWarning(2) upperCritical(3) */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.4", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.activePower = BITS: 00 */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.5", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.apparentPower = BITS: 00 */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.6", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.powerFactor = BITS: 00 */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.7", NULL, SU_FLAG_OK, NULL), /* inletSensorEnabledThresholds.1.1.activeEnergy = BITS: 00 */ - { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.8", NULL, SU_FLAG_OK, NULL), /* outletPoleCount.1.{1-24} = INTEGER: 2 */ - { "unmapped.outletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.5.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.5.1.1", NULL, SU_FLAG_OK, NULL), /* outletRatedVoltage.1.{1-24} = STRING: 100-120V */ - { "unmapped.outletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.6.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.6.1.1", NULL, SU_FLAG_OK, NULL), /* outletRatedCurrent.1.{1-24} = STRING: 16A */ - { "unmapped.outletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.7.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.7.1.1", NULL, SU_FLAG_OK, NULL), /* outletDeviceCapabilities.1.{1-24} = BITS: 9F 04 00 00 00 00 rmsCurrent(0) rmsVoltage(3) activePower(4) apparentPower(5) powerFactor(6) activeEnergy(7) onOff(13) */ - { "unmapped.outletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.10.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.10.1.1", NULL, SU_FLAG_OK, NULL), /* outletPowerCyclingPowerOffPeriod.1.{1-24} = Gauge32: 10 */ - { "unmapped.outletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.12.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.12.1.1", NULL, SU_FLAG_OK, NULL), /* outletStateOnStartup.1.{1-24} = INTEGER: globalOutletStateOnStartup(3) */ - { "unmapped.outletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.13.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.13.1.1", NULL, SU_FLAG_OK, NULL), /* outletUseGlobalPowerCyclingPowerOffPeriod.1.{1-24} = INTEGER: true(1) */ - { "unmapped.outletUseGlobalPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.14.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletUseGlobalPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.14.1.1", NULL, SU_FLAG_OK, NULL), /* outletReceptacleDescriptor.1.{1-24} = STRING: NEMA 5-20R */ - { "unmapped.outletReceptacleDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.29.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletReceptacleDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.29.1.1", NULL, SU_FLAG_OK, NULL), /* outletNonCritical.1.{1-24} = INTEGER: false(2) */ - { "unmapped.outletNonCritical", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.30.1.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletNonCritical", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.30.1.1", NULL, SU_FLAG_OK, NULL), /* outletSequenceDelay.1.{1-24} = Gauge32: 0 */ - { "unmapped.outletSequenceDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.32.1.%i", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSequenceDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.32.1.%i", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.rmsCurrent = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.1", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.rmsVoltage = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.4", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.activePower = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.5", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.apparentPower = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.6", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.powerFactor = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.7", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.activeEnergy = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.8", NULL, SU_FLAG_OK, NULL), /* outletSensorLogAvailable.1.{1-24}.onOff = INTEGER: true(1) */ - { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.14", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.rmsCurrent = INTEGER: amp(2) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.1", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.rmsVoltage = INTEGER: volt(1) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.4", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.activePower = INTEGER: watt(3) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.5", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.apparentPower = INTEGER: voltamp(4) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.6", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.powerFactor = INTEGER: none(-1) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.7", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.activeEnergy = INTEGER: wattHour(5) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.8", NULL, SU_FLAG_OK, NULL), /* outletSensorUnits.1.{1-24}.onOff = INTEGER: none(-1) */ - { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.14", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.rmsCurrent = Gauge32: 1 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.1", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.rmsVoltage = Gauge32: 0 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.4", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.activePower = Gauge32: 0 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.5", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.apparentPower = Gauge32: 0 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.6", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.powerFactor = Gauge32: 2 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.7", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.activeEnergy = Gauge32: 0 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.8", NULL, SU_FLAG_OK, NULL), /* outletSensorDecimalDigits.1.{1-24}.onOff = Gauge32: 0 */ - { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.14", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.rmsCurrent = Gauge32: 100 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.1", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.rmsVoltage = Gauge32: 100 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.4", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.activePower = Gauge32: 300 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.5", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.apparentPower = Gauge32: 200 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.6", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.powerFactor = Gauge32: 100 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.7", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.activeEnergy = Gauge32: 100 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.8", NULL, SU_FLAG_OK, NULL), /* outletSensorAccuracy.1.{1-24}.onOff = Gauge32: 0 */ - { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.14", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.rmsCurrent = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.1", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.1", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.rmsVoltage = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.4", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.4", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.activePower = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.5", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.5", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.apparentPower = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.6", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.6", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.powerFactor = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.7", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.7", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.activeEnergy = Gauge32: 1 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.8", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.8", NULL, SU_FLAG_OK, NULL), /* outletSensorResolution.1.{1-24}.onOff = Gauge32: 0 */ - { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.14", NULL, SU_FLAG_OK, NULL }, + snmp_info_default("unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.14", NULL, SU_FLAG_OK, NULL), /* end of interesting data * the rest is 18MB of verbose log and satellite data */ /* Note: All reliabilityXXX data were removed */ -#endif /* DEBUG */ +#endif /* WITH_UNMAPPED_DATA_POINTS */ +#endif /* DEBUG || WITH_UNMAPPED_DATA_POINTS */ /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t raritan_px2 = { "raritan-px2", RARITAN_PX2_MIB_VERSION, NULL, RARITAN_PX2_OID_MODEL_NAME, raritan_px2_mib, RARITAN_PX2_MIB_SYSOID, NULL }; diff --git a/drivers/rhino.c b/drivers/rhino.c index c99770d9a6..6ceab04158 100644 --- a/drivers/rhino.c +++ b/drivers/rhino.c @@ -34,10 +34,11 @@ #include "main.h" #include "serial.h" #include "nut_float.h" +#include "nut_stdint.h" #include "timehead.h" -#define DRIVER_NAME "Microsol Rhino UPS driver" -#define DRIVER_VERSION "0.52" +#define DRIVER_NAME "Microsol Rhino UPS driver" +#define DRIVER_VERSION "0.53" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -394,7 +395,7 @@ CommReceive(const unsigned char *bufptr, ssize_t size) if( size == 37 ) Waiting = 0; - printf("CommReceive size = %zd waiting = %d\n", size, Waiting ); + printf("CommReceive size = %" PRIiSIZE " waiting = %d\n", size, Waiting ); switch( Waiting ) { diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index f052225c74..053144654b 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -24,12 +24,13 @@ */ #include "main.h" +#include "nut_libusb.h" #include "usb-common.h" #include "nut_stdint.h" /* driver version */ #define DRIVER_NAME "Richcomm dry-contact to USB driver" -#define DRIVER_VERSION "0.10" +#define DRIVER_VERSION "0.12" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -241,6 +242,9 @@ static void usb_comm_good(void) */ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) { +#if WITH_LIBUSB_1_0 + int ret = 0; +#endif NUT_UNUSED_VARIABLE(device); if (usb_set_configuration(handle, 1) < 0) { @@ -248,6 +252,9 @@ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) return -1; } +#ifdef WIN + usb_set_configuration(handle, 0); +#endif if (usb_claim_interface(handle, 0) < 0) { upsdebugx(5, "Can't claim USB interface"); return -1; @@ -259,8 +266,6 @@ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) return -1; } #elif WITH_LIBUSB_1_0 - int ret = 0; - if ((ret = libusb_set_interface_alt_setting(handle, 0, 0)) < 0) { upsdebugx(5, "Can't set USB alternate interface: %s", nut_usb_strerror(ret)); return -1; @@ -303,6 +308,17 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev { int ret = 0; uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; +#if WITH_LIBUSB_1_0 + libusb_device **devlist; + ssize_t devcount = 0; + libusb_device_handle *handle; + struct libusb_device_descriptor dev_desc; + uint8_t bus_num; + /* TODO: consider device_addr */ + int i; +#else /* => WITH_LIBUSB_0_1 */ + struct usb_bus *bus; +#endif /* libusb base init */ #if WITH_LIBUSB_1_0 @@ -324,13 +340,6 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev #endif #if WITH_LIBUSB_1_0 - libusb_device **devlist; - ssize_t devcount = 0; - libusb_device_handle *handle; - struct libusb_device_descriptor dev_desc; - uint8_t bus; - int i; - devcount = libusb_get_device_list(NULL, &devlist); if (devcount <= 0) fatal_with_errno(EXIT_FAILURE, "No USB device found"); @@ -343,7 +352,6 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev ret = libusb_open(dev, &handle); *handlep = handle; #else /* => WITH_LIBUSB_0_1 */ - struct usb_bus *bus; for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; @@ -386,13 +394,13 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev #if WITH_LIBUSB_1_0 device->VendorID = dev_desc.idVendor; device->ProductID = dev_desc.idProduct; - bus = libusb_get_bus_number(dev); + bus_num = libusb_get_bus_number(dev); device->Bus = (char *)malloc(4); if (device->Bus == NULL) { libusb_free_device_list(devlist, 1); fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - sprintf(device->Bus, "%03d", bus); + sprintf(device->Bus, "%03d", bus_num); iManufacturer = dev_desc.iManufacturer; iProduct = dev_desc.iProduct; iSerialNumber = dev_desc.iSerialNumber; @@ -571,9 +579,17 @@ 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++) { +#ifndef WIN32 if ((i < 32) && (sleep(5) == 0)) { +#else +/*FIXME*/ + sleep(5); + if ((i < 32)) { +#endif usb_comm_fail("Can't open USB device, retrying ..."); continue; } @@ -625,11 +641,13 @@ void upsdrv_updateinfo(void) int ret, online, battery_normal; if (!udev) { + dstate_setinfo("driver.state", "reconnect.trying"); ret = usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback); if (ret < 0) { return; } + dstate_setinfo("driver.state", "reconnect.updateinfo"); } ret = query_ups(reply); @@ -638,6 +656,7 @@ void upsdrv_updateinfo(void) usb_comm_fail("Query to UPS failed"); dstate_datastale(); + dstate_setinfo("driver.state", "reconnect.trying"); usb_device_close(udev); udev = NULL; @@ -722,4 +741,8 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + /* TODO: Uncomment while addressing https://github.com/networkupstools/nut/issues/1768 + nut_usb_addvars(); + */ } diff --git a/drivers/riello.c b/drivers/riello.c index 69bfe8db1d..28c992bfde 100644 --- a/drivers/riello.c +++ b/drivers/riello.c @@ -4,8 +4,8 @@ * Documents describing the protocol implemented by this driver can be * found online at: * - * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf - * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf + * https://www.networkupstools.org/protocols/riello/PSGPSER-0104.pdf + * https://www.networkupstools.org/protocols/riello/PSSENTR-0100.pdf * * Copyright (C) 2012 - Elio Parisi * @@ -26,8 +26,10 @@ * Reference of the derivative work: blazer driver */ +#include "config.h" /* must be the first header */ +#include "common.h" /* for upsdebugx() etc */ + #include -#include #include "main.h" #include "riello.h" @@ -68,14 +70,12 @@ uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t che size--; CRC_Word = 0x554D; while(size--) { - pom = (CRC_Word ^ *buff) & 0x00ff; - pom = (pom ^ (pom << 4)) & 0x00ff; - /* Thanks to &0xff above, pom is at most 255 -- - * so shifted by 8 bits is still uint16_t range + pom = (CRC_Word ^ *buff) & 0x00ff; + pom = (pom ^ (pom << 4)) & 0x00ff; + /* Thanks to &0x00ff above, pom is at most 255 -- + * so shifted by 8 bits is still uint16_t range: */ - pom = (uint16_t)(pom << 8); - pom ^= (pom << 3); - pom ^= (pom >> 4); + pom = (pom << 8) ^ (pom << 3) ^ (pom >> 4); CRC_Word = (CRC_Word >> 8) ^ pom; buff++; } @@ -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) @@ -915,7 +915,7 @@ void riello_parse_sentr(uint8_t* buffer, TRielloData* data) data->StatusCode[2] |= 0x01; } -void riello_init_serial() +void riello_init_serial(void) { wait_packet = 1; buf_ptr_length = 0; diff --git a/drivers/riello.h b/drivers/riello.h index 2f3c6a7b54..e513f638b2 100644 --- a/drivers/riello.h +++ b/drivers/riello.h @@ -4,8 +4,8 @@ * Documents describing the protocol implemented by this driver can be * found online at: * - * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf - * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf + * https://www.networkupstools.org/protocols/riello/PSGPSER-0104.pdf + * https://www.networkupstools.org/protocols/riello/PSSENTR-0100.pdf * * Copyright (C) 2012 - Elio Parisi * @@ -29,7 +29,7 @@ #ifndef NUT_RIELLO_H_SEEN #define NUT_RIELLO_H_SEEN 1 -#include +#include "nut_stdint.h" #define CTRL_RETRIES 50 #define CTRL_TIMEOUT 100 diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c index 8c2714fdef..6748fca83a 100644 --- a/drivers/riello_ser.c +++ b/drivers/riello_ser.c @@ -2,8 +2,8 @@ * riello_ser.c: support for Riello serial protocol based UPSes * * A document describing the protocol implemented by this driver can be - * found online at "http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf" - * and "http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf". + * found online at "https://www.networkupstools.org/protocols/riello/PSGPSER-0104.pdf" + * and "https://www.networkupstools.org/protocols/riello/PSSENTR-0100.pdf". * * Copyright (C) 2012 - Elio Parisi * @@ -25,25 +25,30 @@ */ #include "config.h" /* must be the first header */ +#include "common.h" /* for upsdebugx() etc */ #include -#include + +#ifdef WIN32 +# include "wincompat.h" +#endif #include "main.h" #include "serial.h" #include "timehead.h" + /* -// The serial driver has no need for HID structures/code currently -// (maybe there is/was a plan for sharing something between siblings). -// Note that HID is tied to libusb or libshut definitions at the moment. + * // The serial driver has no need for HID structures/code currently + * // (maybe there is/was a plan for sharing something between siblings). + * // Note that HID is tied to libusb or libshut definitions at the moment. #include "hidparser.h" #include "hidtypes.h" -*/ -#include "common.h" /* for upsdebugx() etc */ + */ + #include "riello.h" #define DRIVER_NAME "Riello serial driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.09" #define DEFAULT_OFFDELAY 5 #define DEFAULT_BOOTDELAY 5 @@ -82,13 +87,17 @@ static TRielloData DevData; * * return -1 on error, -2 on timeout, nb_bytes_readen on success * + * See also select_read() in common.c (TODO: standardize codebase) + * *********************************************************************/ static ssize_t char_read (char *bytes, size_t size, int read_timeout) { - struct timeval serial_timeout; - fd_set readfs; ssize_t readen = 0; + +#ifndef WIN32 int rc = 0; + struct timeval serial_timeout; + fd_set readfs; FD_ZERO (&readfs); FD_SET (upsfd, &readfs); @@ -101,7 +110,30 @@ static ssize_t char_read (char *bytes, size_t size, int read_timeout) return -2; /* timeout */ if (FD_ISSET (upsfd, &readfs)) { - ssize_t now = read (upsfd, bytes, size - (size_t)readen); +#else + DWORD timeout; + COMMTIMEOUTS TOut; + + timeout = read_timeout; /* recast */ + + GetCommTimeouts(upsfd, &TOut); + TOut.ReadIntervalTimeout = MAXDWORD; + TOut.ReadTotalTimeoutMultiplier = 0; + TOut.ReadTotalTimeoutConstant = timeout; + SetCommTimeouts(upsfd, &TOut); +#endif + + ssize_t now; +#ifndef WIN32 + now = read (upsfd, bytes, size - (size_t)readen); +#else + /* FIXME? for some reason this compiles, but the first + * arg to the method should be serial_handler_t* - not + * a HANDLE as upsfd is (in main.c)... then again, many + * other drivers seem to use it just fine... + */ + now = w32_serial_read(upsfd, bytes, size - (size_t)readen, timeout); +#endif if (now < 0) { return -1; @@ -109,10 +141,12 @@ static ssize_t char_read (char *bytes, size_t size, int read_timeout) else { readen += now; } +#ifndef WIN32 } else { return -1; } +#endif return readen; } @@ -175,7 +209,7 @@ static void riello_serialcomm(uint8_t* arg_bufIn, uint8_t typedev) } } -static int get_ups_nominal() +static int get_ups_nominal(void) { uint8_t length; @@ -208,7 +242,7 @@ static int get_ups_nominal() return 0; } -static int get_ups_status() +static int get_ups_status(void) { uint8_t numread, length; @@ -248,7 +282,7 @@ static int get_ups_status() return 0; } -static int get_ups_extended() +static int get_ups_extended(void) { uint8_t length; @@ -282,7 +316,7 @@ static int get_ups_extended() } /* Not static, exposed via header. Not used though, currently... */ -int get_ups_statuscode() +int get_ups_statuscode(void) { uint8_t length; @@ -315,7 +349,7 @@ int get_ups_statuscode() return 0; } -static int get_ups_sentr() +static int get_ups_sentr(void) { uint8_t length; @@ -647,7 +681,7 @@ static int riello_instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_UNKNOWN; } -static int start_ups_comm() +static int start_ups_comm(void) { uint8_t length; @@ -934,9 +968,6 @@ void upsdrv_updateinfo(void) */ } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -966,10 +997,13 @@ void upsdrv_shutdown(void) continue; } - fatalx(EXIT_SUCCESS, "Shutting down"); + upslogx(LOG_ERR, "Shutting down"); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index d6f27e2be2..e965fd7830 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -4,7 +4,7 @@ * A document describing the protocol implemented by this driver can be * found online at: * - * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf + * https://www.networkupstools.org/protocols/riello/PSGPSER-0104.pdf * * Copyright (C) 2012 - Elio Parisi * Copyright (C) 2016 Eaton @@ -28,15 +28,13 @@ #include "config.h" /* must be the first header */ -#include - #include "main.h" #include "nut_libusb.h" #include "usb-common.h" #include "riello.h" #define DRIVER_NAME "Riello USB driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.11" #define DEFAULT_OFFDELAY 5 /*!< seconds (max 0xFF) */ #define DEFAULT_BOOTDELAY 5 /*!< seconds (max 0xFF) */ @@ -83,7 +81,7 @@ static void ussleep(useconds_t usec) usleep(usec); } -static int cypress_setfeatures() +static int cypress_setfeatures(void) { int ret; @@ -95,9 +93,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 +195,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); @@ -350,13 +348,17 @@ static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t int ret; if (udev == NULL) { - ret = usb->open(&udev, &usbdevice, reopen_matcher, &driver_callback); + dstate_setinfo("driver.state", "reconnect.trying"); + + ret = usb->open_dev(&udev, &usbdevice, reopen_matcher, &driver_callback); upsdebugx (3, "riello_command err udev NULL : %d ", ret); if (ret < 0) return ret; + dstate_setinfo("driver.state", "reconnect.updateinfo"); upsdrv_initinfo(); /* reconnect usb cable */ + dstate_setinfo("driver.state", "quiet"); } ret = (*subdriver_command)(cmd, buf, length, buflen); @@ -369,7 +371,7 @@ static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t switch (ret) { - case ERROR_BUSY: /* Device or resource busy */ + case LIBUSB_ERROR_BUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); #ifndef HAVE___ATTRIBUTE__NORETURN exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ @@ -383,7 +385,7 @@ static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t # endif #endif - case ERROR_PIPE: /* Broken pipe */ + case LIBUSB_ERROR_PIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; @@ -397,28 +399,34 @@ static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t upsdebugx(1, "Device reset handled"); } goto fallthrough_case_reconnect; - case ERROR_NO_DEVICE: /* No such device */ - case ERROR_ACCESS: /* Permission denied */ - case ERROR_IO: /* I/O error */ + case LIBUSB_ERROR_NO_DEVICE: /* No such device */ + case LIBUSB_ERROR_ACCESS: /* Permission denied */ + case LIBUSB_ERROR_IO: /* I/O error */ #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -ENXIO: /* No such device or address */ #endif - case ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NOT_FOUND: /* No such file or directory */ fallthrough_case_reconnect: /* Uh oh, got to reconnect! */ - usb->close(udev); + dstate_setinfo("driver.state", "reconnect.trying"); + usb->close_dev(udev); udev = NULL; break; - case ERROR_TIMEOUT: /* Connection timed out */ + case LIBUSB_ERROR_TIMEOUT: /* Connection timed out */ upsdebugx (3, "riello_command err: Resource temporarily unavailable"); break; - case ERROR_OVERFLOW: /* Value too large for defined data type */ -#if EPROTO && WITH_LIBUSB_0_1 +#ifndef WIN32 +/* libusb win32 does not know EPROTO and EOVERFLOW, + * it only returns EIO for any IO errors */ + case LIBUSB_ERROR_OVERFLOW: /* Value too large for defined data type */ +# if EPROTO && WITH_LIBUSB_0_1 case -EPROTO: /* Protocol error */ -#endif +# endif break; +#endif + default: break; } @@ -426,7 +434,7 @@ static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t return ret; } -static int get_ups_nominal() +static int get_ups_nominal(void) { uint8_t length; @@ -459,7 +467,7 @@ static int get_ups_nominal() return 0; } -static int get_ups_status() +static int get_ups_status(void) { uint8_t numread, length; int recv; @@ -498,7 +506,7 @@ static int get_ups_status() return 0; } -static int get_ups_extended() +static int get_ups_extended(void) { uint8_t length; int recv; @@ -531,7 +539,7 @@ static int get_ups_extended() } /* Not static, exposed via header. Not used though, currently... */ -int get_ups_statuscode() +int get_ups_statuscode(void) { uint8_t length; int recv; @@ -787,7 +795,7 @@ static int riello_instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_UNKNOWN; } -static int start_ups_comm() +static int start_ups_comm(void) { uint16_t length; int recv; @@ -828,7 +836,8 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { - + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + nut_usb_addvars(); } void upsdrv_initups(void) @@ -842,10 +851,12 @@ void upsdrv_initups(void) }; int ret; - char *regex_array[7]; + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; 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"); @@ -853,6 +864,13 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); regex_array[6] = getval("device"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +#else + if (getval("busport")) { + upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)"); + } +#endif /* pick up the subdriver name if set explicitly */ if (subdrv) { @@ -892,7 +910,7 @@ void upsdrv_initups(void) /* link the matchers */ regex_matcher->next = &device_matcher; - ret = usb->open(&udev, &usbdevice, regex_matcher, &driver_callback); + ret = usb->open_dev(&udev, &usbdevice, regex_matcher, &driver_callback); if (ret < 0) { fatalx(EXIT_FAILURE, "No supported devices found. Please check your device availability with 'lsusb'\n" @@ -996,9 +1014,6 @@ void upsdrv_initinfo(void) upsh.instcmd = riello_instcmd; } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -1027,10 +1042,13 @@ void upsdrv_shutdown(void) continue; } - fatalx(EXIT_SUCCESS, "Shutting down"); + upslogx(LOG_ERR, "Shutting down"); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } void upsdrv_updateinfo(void) @@ -1214,7 +1232,7 @@ void upsdrv_updateinfo(void) void upsdrv_cleanup(void) { - usb->close(udev); + usb->close_dev(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(usbdevice.Vendor); @@ -1222,4 +1240,7 @@ void upsdrv_cleanup(void) free(usbdevice.Serial); free(usbdevice.Bus); free(usbdevice.Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(usbdevice.BusPort); +#endif } diff --git a/drivers/safenet.c b/drivers/safenet.c index 2715db85b6..68365463fa 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -41,7 +41,7 @@ #include "safenet.h" #define DRIVER_NAME "Generic SafeNet UPS driver" -#define DRIVER_VERSION "1.7" +#define DRIVER_VERSION "1.80" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -434,7 +434,9 @@ void upsdrv_shutdown(void) continue; } - fatalx(EXIT_FAILURE, "SafeNet protocol compatible UPS not found on %s", device_path); + upslogx(LOG_ERR, "SafeNet protocol compatible UPS not found on %s", device_path); + set_exit_flag(-1); + return; } /* diff --git a/drivers/salicru-hid.c b/drivers/salicru-hid.c index c93f52a845..717fc841ce 100644 --- a/drivers/salicru-hid.c +++ b/drivers/salicru-hid.c @@ -6,6 +6,7 @@ * 2008 - 2009 Arjen de Korte * 2013 Charles Lepple * 2021 Francois Lacroix + * 2022 Abel Gomez * * Note: this subdriver was initially generated as a "stub" by the * gen-usbhid-subdriver script. It must be customized. @@ -32,7 +33,7 @@ #include "main.h" /* for getval() */ #include "usb-common.h" -#define SALICRU_HID_VERSION "Salicru HID 0.3" +#define SALICRU_HID_VERSION "Salicru HID 0.4" /* FIXME: experimental flag to be put in upsdrv_info */ /* Salicru */ @@ -40,6 +41,10 @@ /* USB IDs device table */ static usb_device_id_t salicru_usb_device_table[] = { + /* Salicru SPS 3000 ADV RT2 */ + /* https://www.salicru.com/sps-3000-adv-rt2.html */ + { USB_DEVICE(SALICRU_VENDORID, 0x0101), NULL }, + /* TWINPRO3/TWINRT3 (SLC-1500-TWIN PRO3) per https://github.com/networkupstools/nut/issues/1142 */ /* SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 */ { USB_DEVICE(SALICRU_VENDORID, 0x0201), NULL }, @@ -50,6 +55,10 @@ static usb_device_id_t salicru_usb_device_table[] = { /* https://www.salicru.com/sps-home.html */ { USB_DEVICE(SALICRU_VENDORID, 0x0300), NULL }, + /* Salicru SPS 850 ADV T, see https://github.com/networkupstools/nut/issues/1416 */ + /* https://www.salicru.com/sps-850-adv-t.html */ + { USB_DEVICE(SALICRU_VENDORID, 0x0302), NULL }, + /* Terminating entry */ { 0, 0, NULL } }; @@ -86,7 +95,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 +213,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..fdff4b15fc 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -23,12 +23,14 @@ #include "main.h" #include "attribute.h" +#ifndef WIN32 #include #include +#include +#endif #include #include #include -#include #include #ifdef HAVE_UU_LOCK @@ -45,8 +47,10 @@ static void ser_open_error(const char *port) static void ser_open_error(const char *port) { struct stat fs; +#ifndef WIN32 struct passwd *user; struct group *group; +#endif printf("\n"); @@ -60,6 +64,8 @@ static void ser_open_error(const char *port) fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } +/* TODO */ +#ifndef WIN32 user = getpwuid(getuid()); if (user) @@ -77,6 +83,7 @@ static void ser_open_error(const char *port) if (group) printf("Serial port group: %s (%d)\n", group->gr_name, (int) fs.st_gid); +#endif printf(" Mode of port: %04o\n\n", (int) fs.st_mode & 07777); @@ -89,12 +96,17 @@ static void ser_open_error(const char *port) fatalx(EXIT_FAILURE, "Fatal error: unusable configuration"); } -static void lock_set(int fd, const char *port) +static void lock_set(TYPE_FD_SER fd, const char *port) { int ret; - if (fd < 0) + if (INVALID_FD_SER(fd)) { +#ifndef WIN32 fatal_with_errno(EXIT_FAILURE, "lock_set: programming error: fd = %d", fd); +#else + fatal_with_errno(EXIT_FAILURE, "lock_set: programming error: struct = %p", fd); +#endif + } if (do_lock_port == 0) return; @@ -124,20 +136,23 @@ static void lock_set(int fd, const char *port) #else + NUT_UNUSED_VARIABLE(port); + ret = 0; /* Make compiler happy */ + ret = ret; upslog_with_errno(LOG_WARNING, "Warning: no locking method is available"); #endif } /* Non fatal version of ser_open */ -int ser_open_nf(const char *port) +TYPE_FD_SER ser_open_nf(const char *port) { - int fd; + TYPE_FD_SER fd; fd = open(port, O_RDWR | O_NOCTTY | O_EXCL | O_NONBLOCK); - if (fd < 0) { - return -1; + if (INVALID_FD_SER(fd)) { + return ERROR_FD_SER; } lock_set(fd, port); @@ -145,19 +160,19 @@ int ser_open_nf(const char *port) return fd; } -int ser_open(const char *port) +TYPE_FD_SER ser_open(const char *port) { - int res; + TYPE_FD_SER res; res = ser_open_nf(port); - if(res == -1) { + if (INVALID_FD_SER(res)) { ser_open_error(port); } return res; } -int ser_set_speed_nf(int fd, const char *port, speed_t speed) +int ser_set_speed_nf(TYPE_FD_SER fd, const char *port, speed_t speed) { struct termios tio; NUT_UNUSED_VARIABLE(port); @@ -186,7 +201,7 @@ int ser_set_speed_nf(int fd, const char *port, speed_t speed) return 0; } -int ser_set_speed(int fd, const char *port, speed_t speed) +int ser_set_speed(TYPE_FD_SER fd, const char *port, speed_t speed) { int res; @@ -198,7 +213,8 @@ int ser_set_speed(int fd, const char *port, speed_t speed) return 0; } -static int ser_set_control(int fd, int line, int state) +#ifndef WIN32 +static int ser_set_control(TYPE_FD_SER fd, int line, int state) { if (state) { return ioctl(fd, TIOCMBIS, &line); @@ -206,18 +222,54 @@ static int ser_set_control(int fd, int line, int state) return ioctl(fd, TIOCMBIC, &line); } } +#endif -int ser_set_dtr(int fd, int state) +int ser_set_dtr(TYPE_FD_SER fd, int state) { +#ifndef WIN32 return ser_set_control(fd, TIOCM_DTR, state); +#else + DWORD action; + + if (state == 0) { + action = CLRDTR; + } + else { + action = SETDTR; + } + + /* Success */ + if (EscapeCommFunction(fd->handle,action) != 0) { + return 0; + } + + return -1; +#endif } -int ser_set_rts(int fd, int state) +int ser_set_rts(TYPE_FD_SER fd, int state) { +#ifndef WIN32 return ser_set_control(fd, TIOCM_RTS, state); +#else + DWORD action; + + if(state == 0) { + action = CLRRTS; + } + else { + action = SETRTS; + } + /* Success */ + if( EscapeCommFunction(fd->handle,action) != 0) { + return 0; + } + return -1; +#endif } -static int ser_get_control(int fd, int line) +#ifndef WIN32 +static int ser_get_control(TYPE_FD_SER fd, int line) { int flags; @@ -225,31 +277,53 @@ static int ser_get_control(int fd, int line) return (flags & line); } +#endif -int ser_get_dsr(int fd) +int ser_get_dsr(TYPE_FD_SER fd) { +#ifndef WIN32 return ser_get_control(fd, TIOCM_DSR); +#else + int flags; + + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_DSR); +#endif } -int ser_get_cts(int fd) +int ser_get_cts(TYPE_FD_SER fd) { +#ifndef WIN32 return ser_get_control(fd, TIOCM_CTS); +#else + int flags; + + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_CTS); +#endif } -int ser_get_dcd(int fd) +int ser_get_dcd(TYPE_FD_SER fd) { +#ifndef WIN32 return ser_get_control(fd, TIOCM_CD); -} +#else + int flags; -int ser_flush_io(int fd) -{ - return tcflush(fd, TCIOFLUSH); + w32_getcomm(fd->handle, &flags); + return (flags & TIOCM_CD); +#endif } -int ser_close(int fd, const char *port) +int ser_close(TYPE_FD_SER fd, const char *port) { - if (fd < 0) + if (INVALID_FD_SER(fd)) { +#ifndef WIN32 fatal_with_errno(EXIT_FAILURE, "ser_close: programming error: fd=%d port=%s", fd, port); +#else + fatal_with_errno(EXIT_FAILURE, "ser_close: programming error: struct=%p port=%s", fd, port); +#endif + } if (close(fd) != 0) return -1; @@ -262,12 +336,12 @@ int ser_close(int fd, const char *port) return 0; } -ssize_t ser_send_char(int fd, unsigned char ch) +ssize_t ser_send_char(TYPE_FD_SER fd, unsigned char ch) { return ser_send_buf_pace(fd, 0, &ch, 1); } -static ssize_t send_formatted(int fd, const char *fmt, va_list va, useconds_t d_usec) +static ssize_t send_formatted(TYPE_FD_SER fd, const char *fmt, va_list va, useconds_t d_usec) { int ret; char buf[LARGEBUF]; @@ -294,7 +368,7 @@ static ssize_t send_formatted(int fd, const char *fmt, va_list va, useconds_t d_ } /* send the results of the format string with d_usec delay after each char */ -ssize_t ser_send_pace(int fd, useconds_t d_usec, const char *fmt, ...) +ssize_t ser_send_pace(TYPE_FD_SER fd, useconds_t d_usec, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -321,7 +395,7 @@ ssize_t ser_send_pace(int fd, useconds_t d_usec, const char *fmt, ...) } /* send the results of the format string with no delay */ -ssize_t ser_send(int fd, const char *fmt, ...) +ssize_t ser_send(TYPE_FD_SER fd, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -348,13 +422,13 @@ ssize_t ser_send(int fd, const char *fmt, ...) } /* send buflen bytes from buf with no delay */ -ssize_t ser_send_buf(int fd, const void *buf, size_t buflen) +ssize_t ser_send_buf(TYPE_FD_SER fd, const void *buf, size_t buflen) { return ser_send_buf_pace(fd, 0, buf, buflen); } /* send buflen bytes from buf with d_usec delay after each char */ -ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, const void *buf, +ssize_t ser_send_buf_pace(TYPE_FD_SER fd, useconds_t d_usec, const void *buf, size_t buflen) { ssize_t ret = 0; @@ -376,7 +450,7 @@ ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, const void *buf, return sent; } -ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec) +ssize_t ser_get_char(TYPE_FD_SER fd, void *ch, time_t d_sec, useconds_t d_usec) { /* Per standard below, we can cast here, because required ranges are * effectively the same (and signed -1 for suseconds_t), and at most long: @@ -385,7 +459,7 @@ ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec) return select_read(fd, ch, 1, d_sec, (suseconds_t)d_usec); } -ssize_t ser_get_buf(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) +ssize_t ser_get_buf(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) { memset(buf, '\0', buflen); @@ -393,7 +467,7 @@ ssize_t ser_get_buf(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d } /* keep reading until buflen bytes are received or a timeout occurs */ -ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) +ssize_t ser_get_buf_len(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) { ssize_t ret; ssize_t recv; @@ -418,7 +492,7 @@ ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, time_t d_sec, useconds /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, +ssize_t ser_get_line_alert(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler(char ch), time_t d_sec, useconds_t d_usec) { @@ -463,14 +537,14 @@ ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, } /* as above, only with no alertset handling (just a wrapper) */ -ssize_t ser_get_line(int fd, void *buf, size_t buflen, char endchar, +ssize_t ser_get_line(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, const char *ignset, time_t d_sec, useconds_t d_usec) { return ser_get_line_alert(fd, buf, buflen, endchar, ignset, "", NULL, d_sec, d_usec); } -ssize_t ser_flush_in(int fd, const char *ignset, int verbose) +ssize_t ser_flush_in(TYPE_FD_SER fd, const char *ignset, int verbose) { ssize_t ret, extra = 0; char ch; @@ -485,7 +559,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); @@ -494,6 +568,11 @@ ssize_t ser_flush_in(int fd, const char *ignset, int verbose) return extra; } +int ser_flush_io(TYPE_FD_SER fd) +{ + return tcflush(fd, TCIOFLUSH); +} + void ser_comm_fail(const char *fmt, ...) { int ret; diff --git a/drivers/serial.h b/drivers/serial.h index c2f5a2c914..cd8aa0fbe4 100644 --- a/drivers/serial.h +++ b/drivers/serial.h @@ -1,83 +1,114 @@ #ifndef SERIAL_H_SEEN #define SERIAL_H_SEEN 1 -#include "attribute.h" +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif -#include "config.h" +#include "attribute.h" -#if defined(HAVE_SYS_TERMIOS_H) +#include "common.h" /* for TYPE_FD_SER, possibly fallback suseconds_t */ +#ifndef WIN32 +# if defined(HAVE_SYS_TERMIOS_H) # include /* for speed_t */ -#else +# else # include -#endif /* HAVE_SYS_TERMIOS_H */ +# endif /* HAVE_SYS_TERMIOS_H */ +#else /* WIN32 */ +# include "wincompat.h" +#endif /* WIN32 */ #include /* for usleep() and useconds_t, latter also might be via */ #include +#if defined(HAVE_SYS_SELECT_H) #include /* for suseconds_t */ +#endif /* limit the amount of spew that goes in the syslog when we lose the UPS */ #define SER_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define SER_ERR_RATE 100 /* then only print every 100th error */ -int ser_open_nf(const char *port); -int ser_open(const char *port); +/* porting stuff for WIN32 */ +#ifdef WIN32 +/* TODO : support "open" flags */ +# define O_NONBLOCK 0 +# define O_NOCTTY 0 + +/* Builds on Windows MSYS2 environment did not recognize this macro: */ +# ifndef TIOCM_ST +# define TIOCM_ST 0x008 +# endif +#endif /* WIN32 */ -int ser_set_speed_nf(int fd, const char *port, speed_t speed); -int ser_set_speed(int fd, const char *port, speed_t speed); +TYPE_FD_SER ser_open_nf(const char *port); +TYPE_FD_SER ser_open(const char *port); + +int ser_set_speed(TYPE_FD_SER fd, const char *port, speed_t speed); +int ser_set_speed_nf(TYPE_FD_SER fd, const char *port, speed_t speed); /* set the state of modem control lines */ -int ser_set_dtr(int fd, int state); -int ser_set_rts(int fd, int state); +int ser_set_dtr(TYPE_FD_SER fd, int state); +int ser_set_rts(TYPE_FD_SER fd, int state); /* get the status of modem control lines */ -int ser_get_dsr(int fd); -int ser_get_cts(int fd); -int ser_get_dcd(int fd); - -int ser_flush_io(int fd); +int ser_get_dsr(TYPE_FD_SER fd); +int ser_get_cts(TYPE_FD_SER fd); +int ser_get_dcd(TYPE_FD_SER fd); -int ser_close(int fd, const char *port); +int ser_close(TYPE_FD_SER fd, const char *port); -ssize_t ser_send_char(int fd, unsigned char ch); +ssize_t ser_send_char(TYPE_FD_SER fd, unsigned char ch); /* send the results of the format string with d_usec delay after each char */ -ssize_t ser_send_pace(int fd, useconds_t d_usec, const char *fmt, ...) +ssize_t ser_send_pace(TYPE_FD_SER fd, useconds_t d_usec, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); /* send the results of the format string with no delay */ -ssize_t ser_send(int fd, const char *fmt, ...) +ssize_t ser_send(TYPE_FD_SER fd, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); /* send buflen bytes from buf with no delay */ -ssize_t ser_send_buf(int fd, const void *buf, size_t buflen); +ssize_t ser_send_buf(TYPE_FD_SER fd, const void *buf, size_t buflen); /* send buflen bytes from buf with d_usec delay after each char */ -ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, const void *buf, +ssize_t ser_send_buf_pace(TYPE_FD_SER fd, useconds_t d_usec, const void *buf, size_t buflen); -ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec); +ssize_t ser_get_char(TYPE_FD_SER fd, void *ch, time_t d_sec, useconds_t d_usec); -ssize_t ser_get_buf(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); +ssize_t ser_get_buf(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); /* keep reading until buflen bytes are received or a timeout occurs */ -ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); +ssize_t ser_get_buf_len(TYPE_FD_SER fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, - const char *ignset, const char *alertset, void handler (char ch), +ssize_t ser_get_line_alert(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, + const char *ignset, const char *alertset, void handler (char ch), time_t d_sec, useconds_t d_usec); /* as above, only with no alertset handling (just a wrapper) */ -ssize_t ser_get_line(int fd, void *buf, size_t buflen, char endchar, +ssize_t ser_get_line(TYPE_FD_SER fd, void *buf, size_t buflen, char endchar, const char *ignset, time_t d_sec, useconds_t d_usec); -ssize_t ser_flush_in(int fd, const char *ignset, int verbose); +ssize_t ser_flush_in(TYPE_FD_SER fd, const char *ignset, int verbose); +int ser_flush_io(TYPE_FD_SER fd); /* unified failure reporting: call these often */ void ser_comm_fail(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); void ser_comm_good(void); +#ifdef WIN32 +#define open(a,b) w32_serial_open(a,b) +#define close(a) w32_serial_close(a) +#define read(a,b,c) w32_serial_read(a,b,c,INFINITE) +#define write(a,b,c) w32_serial_write(a,b,c) +#endif + #endif /* SERIAL_H_SEEN */ diff --git a/drivers/skel.c b/drivers/skel.c index f6d4cb39f7..b5033672bb 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -22,7 +22,7 @@ /* #define IGNCHARS "" */ #define DRIVER_NAME "Skeleton UPS driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -94,9 +94,6 @@ void upsdrv_updateinfo(void) */ } -void upsdrv_shutdown(void) - __attribute__((noreturn)); - void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -105,7 +102,8 @@ void upsdrv_shutdown(void) it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ diff --git a/drivers/sms_ser.c b/drivers/sms_ser.c new file mode 100644 index 0000000000..02100634c9 --- /dev/null +++ b/drivers/sms_ser.c @@ -0,0 +1,704 @@ +/* + * sms_ser.c: code for mono protocol for SMS Brazil UPSes + * + * Copyright (C) 2023 - Alex W. Baule + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: riello driver + */ + +#include "config.h" /* must be the first header */ +#include + +#include "common.h" /* for upsdebugx() etc */ +#include "sms_ser.h" +#include "main.h" +#include "serial.h" + +#define ENDCHAR '\r' + +#define DRIVER_NAME "SMS Brazil UPS driver" +#define DRIVER_VERSION "1.00" + +#define QUERY_SIZE 7 +#define BUFFER_SIZE 18 +#define RESULT_SIZE 18 +#define HUMAN_VALUES 7 + +static uint16_t bootdelay = DEFAULT_BOOTDELAY; +static uint8_t bufOut[BUFFER_SIZE]; +static uint8_t bufIn[BUFFER_SIZE]; +static SmsData DeviceData; + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Alex W. BaulĆ© ", + DRV_BETA, + {NULL}}; + +void sms_parse_features(uint8_t *rawvalues, SmsData *results) { + char tbattery[6]; + char frequency[4]; + int i; + + memset(results->voltageRange, 0, sizeof(results->voltageRange)); + memset(results->currentRange, 0, sizeof(results->currentRange)); + memset(tbattery, 0, sizeof(tbattery)); + memset(frequency, 0, sizeof(frequency)); + + for (i = 1; i < BUFFER_SIZE - 2; i++) { + if (i <= 7) { + snprintfcat(results->voltageRange, 14, "%c", rawvalues[i]); + } else if (i <= 10) { + snprintfcat(results->currentRange, 6, "%c", rawvalues[i]); + } else if (i <= 13) { + snprintfcat(tbattery, sizeof(tbattery), "%c", rawvalues[i]); + } else { + snprintfcat(frequency, sizeof(frequency), "%c", rawvalues[i]); + } + } + + results->voltageBattery = atoi(tbattery); + results->frequency = atoi(frequency); +} + +void sms_parse_information(uint8_t *rawvalues, SmsData *results) { + /* Count from 1 to ignore first char and remove 2 from BUFFER_SIZE + * to compensate the start and ignore '\r' from end. */ + int i; + + memset(results->model, 0, sizeof(results->model)); + memset(results->version, 0, sizeof(results->version)); + + for (i = 1; i < BUFFER_SIZE - 2; i++) { + if (i <= 12) { + snprintfcat(results->model, 24, "%c", rawvalues[i]); + } else { + snprintfcat(results->version, 6, "%c", rawvalues[i]); + } + } +} + +void sms_parse_results(uint8_t *rawvalues, SmsData *results) { + char buf[BUFFER_SIZE]; + uint8_t byte, mask; + long v; + double h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[1], (unsigned char)rawvalues[2]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->lastinputVac = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[3], (unsigned char)rawvalues[4]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->inputVac = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[5], (unsigned char)rawvalues[6]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->outputVac = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[7], (unsigned char)rawvalues[8]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->outputpower = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[9], (unsigned char)rawvalues[10]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->outputHz = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[11], (unsigned char)rawvalues[12]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->batterylevel = h; + + memset(buf, 0, BUFFER_SIZE); + sprintf(buf, "0x%02x%02x", (unsigned char)rawvalues[13], (unsigned char)rawvalues[14]); + v = strtol(buf, NULL, 16); /* 16 == hex */ + h = v / 10; + results->temperatureC = h; + + byte = rawvalues[15]; + mask = 1; + + results->beepon = ((byte & (mask << 0)) != 0) ? true : false; + results->shutdown = ((byte & (mask << 1)) != 0) ? true : false; + results->test = ((byte & (mask << 2)) != 0) ? true : false; + results->upsok = ((byte & (mask << 3)) != 0) ? true : false; + results->boost = ((byte & (mask << 4)) != 0) ? true : false; + results->bypass = ((byte & (mask << 5)) != 0) ? true : false; + results->lowbattery = ((byte & (mask << 6)) != 0) ? true : false; + results->onbattery = ((byte & (mask << 7)) != 0) ? true : false; +} + +static int get_ups_nominal(void) { + uint8_t length; + ssize_t ret; + + upsdebugx(LOG_DEBUG, "get_ups_nominal"); + + length = sms_prepare_get_status(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(LOG_ERR, "Communication error while writing to port"); + return -1; + } + memset(bufIn, 0, BUFFER_SIZE); + ret = ser_get_buf_len(upsfd, &bufIn[0], BUFFER_SIZE, 3, 1000); + + if (ret < RESULT_SIZE) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return -1; + } + + upsdebugx(3, "Get nominal Ok: received byte %" PRIiSIZE, ret); + + if (bufIn[0] == '=' || bufIn[0] == '<' || bufIn[0] == '>') { + sms_parse_results(&bufIn[0], &DeviceData); + return 0; + } + + upsdebugx(3, "Invalid query response from 'Q' command"); + return -1; +} + +static int get_ups_information(void) { + uint8_t length; + ssize_t ret; + + upsdebugx(LOG_DEBUG, "get_ups_information"); + + length = sms_prepare_get_information(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(LOG_ERR, "Communication error while writing to port"); + return -1; + } + memset(bufIn, 0, BUFFER_SIZE); + ret = ser_get_buf_len(upsfd, &bufIn[0], BUFFER_SIZE, 3, 1000); + + if (ret < RESULT_SIZE) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return -1; + } + + upsdebugx(3, "Get information Ok: received byte %" PRIiSIZE, ret); + + if (bufIn[0] == ';' || bufIn[0] == ':') { + sms_parse_information(&bufIn[0], &DeviceData); + return 0; + } + + upsdebugx(3, "Invalid query response from 'I' command"); + return -1; +} + +static int get_ups_features(void) { + uint8_t length; + ssize_t ret; + + upsdebugx(LOG_DEBUG, "get_ups_features"); + + length = sms_prepare_get_features(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(LOG_ERR, "Communication error while writing to port"); + return -1; + } + memset(bufIn, 0, BUFFER_SIZE); + ret = ser_get_buf_len(upsfd, &bufIn[0], BUFFER_SIZE, 3, 1000); + + if (ret < RESULT_SIZE) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return -1; + } + + upsdebugx(LOG_DEBUG, "Get features Ok: received byte %" PRIiSIZE, ret); + + if (bufIn[0] == ';' || bufIn[0] == ':') { + sms_parse_features(&bufIn[0], &DeviceData); + return 0; + } + + upsdebugx(LOG_ERR, "Invalid query response from 'F' command"); + return -1; +} + +static int sms_instcmd(const char *cmdname, const char *extra) { + size_t length; + + if (!strcasecmp(cmdname, "test.battery.start")) { + long delay = extra ? strtol(extra, NULL, 10) : 10; + length = sms_prepare_test_battery_nsec(&bufOut[0], delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send test.battery.start"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command test.battery.start OK!"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.start.quick")) { + length = sms_prepare_test_battery_low(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send test.battery.start.quick"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command test.battery.start.quick OK!"); + + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.stop")) { + length = sms_prepare_cancel_test(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send test.battery.stop"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command test.battery.stop OK!"); + + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "beeper.toggle")) { + length = sms_prepare_set_beep(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send beeper.toggle"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command beeper.toggle OK!"); + + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "shutdown.return")) { + length = sms_prepare_shutdown_restore(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send shutdown.return"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command shutdown.return OK!"); + + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "shutdown.reboot")) { + uint16_t delay = bootdelay; + if (extra) { + long ldelay = strtol(extra, NULL, bootdelay); + if (ldelay >= 0 && (intmax_t)ldelay < (intmax_t)UINT16_MAX) { + delay = (uint16_t)ldelay; + } else { + upsdebugx(3, "tried to set up extra shutdown.reboot delay ut it was out of range, keeping default"); + } + } + length = sms_prepare_shutdown_nsec(&bufOut[0], delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send shutdown.reboot"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command shutdown.reboot OK!"); + + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "shutdown.stop")) { + length = sms_prepare_cancel_shutdown(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx(3, "failed to send shutdown.stop"); + return STAT_INSTCMD_FAILED; + } + upsdebugx(3, "command shutdown.stop OK!"); + + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "sms_instcmd: unknown command [%s]", cmdname); + return STAT_INSTCMD_UNKNOWN; +} + +static int sms_setvar(const char *varname, const char *val) { + if (!strcasecmp(varname, "ups.delay.reboot")) { + int ipv = atoi(val); + if (ipv >= 0) + bootdelay = (unsigned int)ipv; + dstate_setinfo("ups.delay.reboot", "%u", bootdelay); + return STAT_SET_HANDLED; + } + return STAT_SET_UNKNOWN; +} + +void upsdrv_initinfo(void) { + char *battery_status; + + upsdebugx(LOG_DEBUG, "upsdrv_initinfo"); + + if (get_ups_features() != 0) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return; + } + if (get_ups_information() != 0) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return; + } + + if (get_ups_nominal() == 0) { + dstate_setinfo("device.model", "%s", DeviceData.model); + dstate_setinfo("ups.firmware", "%s", DeviceData.version); + dstate_setinfo("input.voltage.nominal", "%s", DeviceData.voltageRange); + dstate_setinfo("input.current.nominal", "%s", DeviceData.currentRange); + dstate_setinfo("output.frequency.nominal", "%d", DeviceData.frequency); + dstate_setinfo("ups.beeper.status", "%s", (DeviceData.beepon == 1) ? "enabled" : "disabled"); + + dstate_setinfo("input.voltage.extended", "%.2f", DeviceData.lastinputVac); + + dstate_setinfo("input.voltage", "%.2f", DeviceData.inputVac); + dstate_setinfo("output.voltage", "%.2f", DeviceData.outputVac); + dstate_setinfo("ups.load", "%.2f", DeviceData.outputpower); + dstate_setinfo("output.frequency", "%.2f", DeviceData.outputHz); + dstate_setinfo("battery.charge", "%.2f", DeviceData.batterylevel); + + dstate_setinfo("battery.voltage.nominal", "%d", DeviceData.voltageBattery); + dstate_setinfo("battery.packs", "%d", DeviceData.voltageBattery / 12); + dstate_setinfo("battery.voltage", "%.2f", (DeviceData.voltageBattery * DeviceData.batterylevel) / 100); + dstate_setinfo("ups.temperature", "%.2f", DeviceData.temperatureC); + + if (DeviceData.onbattery && (uint8_t)DeviceData.batterylevel < 100) { + upsdebugx(LOG_DEBUG, "on battery and battery < last battery"); + battery_status = "discharging"; + } else if (!DeviceData.onbattery && (uint8_t)DeviceData.batterylevel < 100) { + upsdebugx(LOG_DEBUG, "on power and battery > last battery"); + battery_status = "charging"; + } else if (!DeviceData.onbattery && (uint8_t)DeviceData.batterylevel == 100) { + upsdebugx(LOG_DEBUG, "on power and battery == 100"); + battery_status = "resting"; + } else { + upsdebugx(LOG_DEBUG, "none, floating"); + battery_status = "floating"; + } + dstate_setinfo("battery.charger.status", "%s", battery_status); + } else { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return; + } + + dstate_addcmd("test.battery.start"); + dstate_addcmd("test.battery.start.quick"); + dstate_addcmd("test.battery.stop"); + dstate_addcmd("beeper.toggle"); + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stop"); + dstate_addcmd("shutdown.reboot"); + + upsh.instcmd = sms_instcmd; + upsh.setvar = sms_setvar; +} + +void upsdrv_updateinfo(void) { + char *battery_status; + + upsdebugx(LOG_DEBUG, "upsdrv_updateinfo"); + + if (get_ups_nominal() != 0) { + upslogx(LOG_ERR, "Short read from UPS"); + dstate_datastale(); + return; + } + dstate_setinfo("device.model", "%s", DeviceData.model); + dstate_setinfo("ups.firmware", "%s", DeviceData.version); + dstate_setinfo("battery.voltage.nominal", "%s", DeviceData.voltageRange); + dstate_setinfo("output.frequency.nominal", "%d", DeviceData.frequency); + dstate_setinfo("ups.beeper.status", "%s", (DeviceData.beepon == 1) ? "enabled" : "disabled"); + + dstate_setinfo("input.voltage.extended", "%.2f", DeviceData.lastinputVac); + + dstate_setinfo("input.voltage", "%.2f", DeviceData.inputVac); + dstate_setinfo("output.voltage", "%.2f", DeviceData.outputVac); + dstate_setinfo("ups.load", "%.2f", DeviceData.outputpower); + dstate_setinfo("output.frequency", "%.2f", DeviceData.outputHz); + dstate_setinfo("battery.charge", "%.2f", DeviceData.batterylevel); + + dstate_setinfo("battery.voltage.nominal", "%d", DeviceData.voltageBattery); + dstate_setinfo("battery.packs", "%d", DeviceData.voltageBattery / 12); + dstate_setinfo("battery.voltage", "%.2f", (DeviceData.voltageBattery * DeviceData.batterylevel) / 100); + dstate_setinfo("ups.temperature", "%.2f", DeviceData.temperatureC); + + upsdebugx(LOG_DEBUG, "battery level: %.2f", DeviceData.batterylevel); + upsdebugx(LOG_DEBUG, "bypass: %d", DeviceData.bypass); + upsdebugx(LOG_DEBUG, "onBattery: %d", DeviceData.onbattery); + + if (DeviceData.onbattery && (uint8_t)DeviceData.batterylevel < 100) { + upsdebugx(LOG_DEBUG, "on battery and battery < last battery"); + battery_status = "discharging"; + } else if (!DeviceData.onbattery && (uint8_t)DeviceData.batterylevel < 100) { + upsdebugx(LOG_DEBUG, "on power and battery > last battery"); + battery_status = "charging"; + } else if (!DeviceData.onbattery && (uint8_t)DeviceData.batterylevel == 100) { + upsdebugx(LOG_DEBUG, "on power and battery == 100"); + battery_status = "resting"; + } else { + upsdebugx(LOG_DEBUG, "none, floating"); + battery_status = "floating"; + } + dstate_setinfo("battery.charger.status", "%s", battery_status); + + status_init(); + + if (DeviceData.bypass) { + upsdebugx(LOG_DEBUG, "setting status to BYPASS"); + status_set("BYPASS"); + } else if (DeviceData.onbattery) { + upsdebugx(LOG_DEBUG, "setting status to OB"); + status_set("OB"); + } else if (DeviceData.lowbattery) { + upsdebugx(LOG_DEBUG, "setting status to LB"); + status_set("LB"); + } else if (!DeviceData.upsok) { + upsdebugx(LOG_DEBUG, "setting status to RB"); + status_set("RB"); + } else if (DeviceData.boost) { + upsdebugx(LOG_DEBUG, "setting status to BOOST"); + status_set("BOOST"); + } else if (!DeviceData.onbattery) { + /* sometimes the flag "onacpower" is not set */ + upsdebugx(LOG_DEBUG, "setting status to OL"); + status_set("OL"); + } else { + /* None of these parameters is ON, but we got some response, + * so the device is (administratively) OFF ? */ + upsdebugx(LOG_DEBUG, "setting status to OFF"); + status_set("OFF"); + } + + status_commit(); + dstate_dataok(); + + poll_interval = 5; +} + +void upsdrv_shutdown(void) { + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + int retry; + + /* maybe try to detect the UPS here, but try a shutdown even if + * it doesn't respond at first if possible */ + + /* replace with a proper shutdown function */ + + /* you may have to check the line status since the commands + * for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ + upsdebugx(2, "upsdrv Shutdown execute"); + + for (retry = 1; retry <= MAXTRIES; retry++) { + if (sms_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + if (sms_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + upslogx(LOG_ERR, "Shutting down"); + set_exit_flag(-2); /* EXIT_SUCCESS */ + return; + } + + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); +} + +void upsdrv_help(void) { +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) { + char msg[256]; + + upsdebugx(LOG_DEBUG, "upsdrv_makevartable"); + + snprintf(msg, sizeof msg, "Set reboot delay, in seconds (default=%d).", + DEFAULT_BOOTDELAY); + addvar(VAR_VALUE, "rebootdelay", msg); +} + +void upsdrv_initups(void) { + char *val; + + upsdebugx(LOG_DEBUG, "upsdrv_initups"); + + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B2400); + + if ((val = getval("rebootdelay"))) { + int ipv = atoi(val); + if (ipv >= 0) + bootdelay = (unsigned int)ipv; + } +} + +void upsdrv_cleanup(void) { + upsdebugx(LOG_DEBUG, "upsdrv_cleanup"); + /* free(dynamic_mem); */ + ser_close(upsfd, device_path); +} + +uint8_t sms_prepare_get_status(uint8_t *buffer) { + buffer[0] = 'Q'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_get_information(uint8_t *buffer) { + buffer[0] = 'I'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_get_features(uint8_t *buffer) { + buffer[0] = 'F'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_set_beep(uint8_t *buffer) { + buffer[0] = 'M'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_test_battery_low(uint8_t *buffer) { + buffer[0] = 'L'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_test_battery_nsec(uint8_t *buffer, uint16_t delay) { + buffer[0] = 'T'; + buffer[1] = (uint8_t)(delay % 256); + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_shutdown_nsec(uint8_t *buffer, uint16_t delay) { + buffer[0] = 'S'; + buffer[1] = (uint8_t)(delay % 256); + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_shutdown_restore(uint8_t *buffer) { + buffer[0] = 'R'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_cancel_test(uint8_t *buffer) { + buffer[0] = 'D'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} + +uint8_t sms_prepare_cancel_shutdown(uint8_t *buffer) { + buffer[0] = 'C'; + buffer[1] = 255; + buffer[2] = 255; + buffer[3] = 255; + buffer[4] = 255; + buffer[5] = (buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4]) * 255; + buffer[6] = ENDCHAR; + + return 7; +} diff --git a/drivers/sms_ser.h b/drivers/sms_ser.h new file mode 100644 index 0000000000..282ada7f1e --- /dev/null +++ b/drivers/sms_ser.h @@ -0,0 +1,76 @@ +/* + * sms_ser.h: defines/macros protocol for SMS Brazil UPSes + * + * Copyright (C) 2023 - Alex W. Baule + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: riello driver + */ + +#ifndef NUT_SMS_SER_H_SEEN +#define NUT_SMS_SER_H_SEEN 1 + +#include /* bool type */ + +#include "nut_stdint.h" + +#define DEFAULT_BOOTDELAY 64 /* seconds (max 0xFF) */ +#define MAXTRIES 3 + +typedef struct { + char model[25]; /* device.model */ + char version[7]; /* ups.firmware */ + char voltageRange[15]; /* garbage from sms (it's a string with some strange items) */ + char currentRange[7]; /* garbage from sms (it's 000) maybe with some cleanup on string input.voltage.nominal */ + uint8_t voltageBattery; /* battery.voltage.nominal (DeviceData.voltageBattery / 12 = battery.packs) */ + uint8_t frequency; /* output.frequency.nominal */ + + bool beepon; /* ups.beeper.status */ + bool shutdown; /* ups.status = FSD (the shutdown has started by another via) */ + bool test; /* the UPS is testing the battery, need a status ? */ + bool upsok; /* ups.status or battery.status ? (Maybe RB if is False ?) */ + bool boost; /* ups.status = BOOST */ + bool bypass; /* ups.status = BYPASS */ + bool lowbattery; /* ups.status = LB (OL + LB or OB + LB ?) */ + bool onbattery; /* ups.status = OB + battery.charger.status = discharging */ + + float lastinputVac; /* garbage ? always 000 */ + float inputVac; /* input.voltage */ + float outputVac; /* output.voltage */ + float outputpower; /* ups.load */ + float outputHz; /* output.frequency */ + float batterylevel; /* battery.charge (batterylevel < 100 ? battery.charger.status = charging if onacpower/discharging if onbattery : resting) + * battery.voltage = (voltageBattery * batterylevel) / 100) + */ + float temperatureC; /* ups.temperature */ +} SmsData; + +void sms_parse_features(uint8_t *rawvalues, SmsData *results); +void sms_parse_information(uint8_t *rawvalues, SmsData *results); +void sms_parse_results(uint8_t* rawvalues, SmsData* results); + +uint8_t sms_prepare_get_status(uint8_t* buffer); +uint8_t sms_prepare_get_information(uint8_t* buffer); +uint8_t sms_prepare_get_features(uint8_t* buffer); +uint8_t sms_prepare_set_beep(uint8_t* buffer); +uint8_t sms_prepare_test_battery_low(uint8_t* buffer); +uint8_t sms_prepare_test_battery_nsec(uint8_t* buffer, uint16_t delay); +uint8_t sms_prepare_shutdown_nsec(uint8_t* buffer, uint16_t delay); +uint8_t sms_prepare_shutdown_restore(uint8_t* buffer); +uint8_t sms_prepare_cancel_test(uint8_t* buffer); +uint8_t sms_prepare_cancel_shutdown(uint8_t* buffer); + +#endif /* NUT_SMS_SER_H_SEEN */ diff --git a/drivers/snmp-ups-helpers.c b/drivers/snmp-ups-helpers.c index fcf231fce2..71414b33c3 100644 --- a/drivers/snmp-ups-helpers.c +++ b/drivers/snmp-ups-helpers.c @@ -81,19 +81,11 @@ const char *su_usdate_to_isodate_info_fun(void *raw_date) info_lkp_t su_convert_to_iso_date_info[] = { /* array index = FUNMAP_USDATE_TO_ISODATE: */ - { 1, "dummy" -#if WITH_SNMP_LKP_FUN - , su_usdate_to_isodate_info_fun, NULL, NULL, NULL -#endif - }, - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_fun_vp2s(1, "dummy", su_usdate_to_isodate_info_fun), + info_lkp_sentinel }; - +#if !(defined WITH_SNMP_LKP_FUN_DUMMY) || !WITH_SNMP_LKP_FUN_DUMMY /* Process temperature value according to 'temperature_unit' */ const char *su_temperature_read_fun(void *raw_snmp_value) { @@ -122,3 +114,4 @@ const char *su_temperature_read_fun(void *raw_snmp_value) upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); return su_scratch_buf; } +#endif /* WITH_SNMP_LKP_FUN_DUMMY */ diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 7cf004c406..c5e6556b16 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -52,11 +52,13 @@ #include "apc-mib.h" #include "mge-mib.h" #include "netvision-mib.h" -#include "powerware-mib.h" #include "eaton-pdu-genesis2-mib.h" #include "eaton-pdu-marlin-mib.h" +#include "eaton-pdu-nlogic-mib.h" #include "eaton-pdu-pulizzi-mib.h" #include "eaton-pdu-revelation-mib.h" +#include "eaton-ups-pwnm2-mib.h" +#include "eaton-ups-pxg-mib.h" #include "raritan-pdu-mib.h" #include "raritan-px2-mib.h" #include "baytech-mib.h" @@ -71,9 +73,11 @@ #include "eaton-ats16-nm2-mib.h" #include "apc-ats-mib.h" #include "apc-pdu-mib.h" +#include "apc-epdu-mib.h" #include "eaton-ats30-mib.h" #include "emerson-avocent-pdu-mib.h" #include "hpe-pdu-mib.h" +#include "hpe-pdu3-cis-mib.h" #endif /* WITH_DMFMIB */ /* Address API change */ @@ -99,11 +103,11 @@ #endif #if WITH_DMFMIB -// Array of pointers to singular instances of mib2nut_info_t -mib2nut_info_t **mib2nut = NULL; -mibdmf_parser_t *dmp = NULL; -char *dmf_dir = NULL; -char *dmf_file = NULL; +/* Array of pointers to singular instances of mib2nut_info_t */ +static mib2nut_info_t **mib2nut = NULL; +static mibdmf_parser_t *dmp = NULL; +static char *dmf_dir = NULL; +static char *dmf_file = NULL; #else /* not WITH_DMFMIB */ # ifdef WITH_DMF_LUA @@ -118,34 +122,38 @@ char *dmf_file = NULL; * cd scripts/DMF/dmfsnmp.d/ && grep 'errstat == SNMP_ERR_NOSUCHNAME) { + upsdebugx(4, "%s: OID does not exist, skipping", __func__); + snmp_free_pdu(response); + nut_snmp_free(ret_array); + return NULL; + } + + upsdebugx(3, "status = %i, response->errstat = %li", status, response->errstat); + + /* Error throttling otherwise */ numerr++; + upsdebugx(4, "%s: numerr++ (total=%i)", __func__, numerr); if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) { upslogx(LOG_WARNING, "[%s] Warning: excessive poll " @@ -1226,12 +1300,29 @@ static struct snmp_pdu **nut_snmp_walk(const char *OID, int max_iteration) snmp_free_pdu(response); break; } else { - numerr = 0; + /* Checked the "type" field of the returned varbind if + * it is a type error exception (only applicable with + * SNMPv2 or SNMPv3 protocol, would not happen with + * SNMPv1). This allows to proceed interpreting large + * responses when one entry in the middle is rejectable. + */ + if (response->variables->type == SNMP_NOSUCHOBJECT || + response->variables->type == SNMP_NOSUCHINSTANCE || + response->variables->type == SNMP_ENDOFMIBVIEW) { + upslogx(LOG_WARNING, "[%s] Warning: type error exception (OID = %s)", + upsname?upsname:device_name, OID); + snmp_free_pdu(response); + break; + } + else { + /* no error */ + numerr = 0; + } } nb_iteration++; /* +1 is for the terminating NULL */ - struct snmp_pdu ** new_ret_array = realloc( + new_ret_array = realloc( ret_array, sizeof(struct snmp_pdu*) * ((size_t)nb_iteration+1) ); @@ -1288,21 +1379,24 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l switch (pdu->variables->type) { case ASN_OCTET_STR: case ASN_OPAQUE: - len = pdu->variables->val_len > buf_len - 1 ? - buf_len - 1 : pdu->variables->val_len; - /* Test for hexadecimal values */ - int hex = 0, x; - unsigned char *cp; - for(cp = pdu->variables->val.string, x = 0; x < (int)pdu->variables->val_len; x++, cp++) { - if (!(isprint(*cp) || isspace(*cp))) { - hex = 1; - } - } - if (hex) - snprint_hexstring(buf, buf_len, pdu->variables->val.string, pdu->variables->val_len); - else { - memcpy(buf, pdu->variables->val.string, len); - buf[len] = '\0'; + { /* scoping */ + /* Test for hexadecimal values */ + int hex = 0, x; + unsigned char *cp; + + len = pdu->variables->val_len > buf_len - 1 ? + buf_len - 1 : pdu->variables->val_len; + for(cp = pdu->variables->val.string, x = 0; x < (int)pdu->variables->val_len; x++, cp++) { + if (!(isprint((size_t)*cp) || isspace((size_t)*cp))) { + hex = 1; + } + } + if (hex) + snprint_hexstring(buf, buf_len, pdu->variables->val.string, pdu->variables->val_len); + else { + memcpy(buf, pdu->variables->val.string, len); + buf[len] = '\0'; + } } break; case ASN_INTEGER: @@ -1347,9 +1441,10 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l upsdebugx(2, "Received an OID value: %s", tmp_buf); /* Try to get the value of the pointed OID */ if (nut_snmp_get_str(tmp_buf, buf, buf_len, oid2info) == FALSE) { + char *oid_leaf; upsdebugx(3, "Failed to retrieve OID value, using fallback"); /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ - char *oid_leaf = strrchr(tmp_buf, '.'); + oid_leaf = strrchr(tmp_buf, '.'); snprintf(buf, buf_len, "%s", oid_leaf+1); upsdebugx(3, "Fallback value: %s", buf); } @@ -1468,9 +1563,10 @@ bool_t nut_snmp_get_int(const char *OID, long *pval) upsdebugx(2, "Received an OID value: %s", tmp_buf); /* Try to get the value of the pointed OID */ if (nut_snmp_get_int(tmp_buf, &value) == FALSE) { + char *oid_leaf; upsdebugx(3, "Failed to retrieve OID value, using fallback"); /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ - char *oid_leaf = strrchr(tmp_buf, '.'); + oid_leaf = strrchr(tmp_buf, '.'); value = strtol(oid_leaf+1, NULL, 0); upsdebugx(3, "Fallback value: %ld", value); } @@ -1648,7 +1744,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 +1754,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); @@ -1759,6 +1898,8 @@ void su_alarm_set(snmp_info_t *su_info_p, long value) if ((info_value = su_find_infoval(su_info_p->oid2info, &value)) != NULL && info_value[0] != 0) { + char alarm_info_value_more[SU_LARGEBUF + 32]; /* can sprintf() SU_LARGEBUF plus markup into here */ + /* Special handling for outlet & outlet groups alarms */ if ((su_info_p->flags & SU_OUTLET) || (su_info_p->flags & SU_OUTLET_GROUP)) { @@ -1779,8 +1920,8 @@ void su_alarm_set(snmp_info_t *su_info_p, long value) * start of path */ if (info_type[0] == 'L') { /* Extract phase number */ + item_number = atoi(info_type+1); - char alarm_info_value_more[SU_LARGEBUF + 32]; /* can sprintf() SU_LARGEBUF plus markup into here */ upsdebugx(2, "%s: appending phase L%i", __func__, item_number); @@ -1821,7 +1962,7 @@ snmp_info_t *su_find_info(const char *type) /* Counter match the sysOID using {device,ups}.model OID * Return TRUE if this OID can be retrieved, FALSE otherwise */ -static bool_t match_model_OID() +static bool_t match_model_OID(void) { bool_t retCode = FALSE; snmp_info_t *su_info_p, *cur_info_p; @@ -1883,7 +2024,7 @@ static bool_t match_model_OID() /* Try to find the MIB using sysOID matching. * Return a pointer to a mib2nut definition if found, NULL otherwise */ -static mib2nut_info_t *match_sysoid() +static mib2nut_info_t *match_sysoid(void) { char sysOID_buf[LARGEBUF]; oid device_sysOID[MAX_OID_LEN]; @@ -1893,91 +2034,96 @@ static mib2nut_info_t *match_sysoid() int i; /* Retrieve sysOID value of this device */ - if (nut_snmp_get_oid(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf)) == TRUE) + if (nut_snmp_get_oid(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf)) != TRUE) + { + upsdebugx(2, "Can't get sysOID value (using nut_snmp_get_oid())"); + /* Fallback for non-compliant device, that returns a string and not an OID */ + if (nut_snmp_get_str(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf), NULL) != TRUE) { + upsdebugx(2, "Can't get sysOID value (using nut_snmp_get_str())"); + return NULL; + } + } + + upsdebugx(1, "%s: device sysOID value = %s", __func__, sysOID_buf); + + /* Build OIDs for comparison */ + if (!read_objid(sysOID_buf, device_sysOID, &device_sysOID_len)) { - upsdebugx(1, "%s: device sysOID value = %s", __func__, sysOID_buf); + upsdebugx(2, "%s: can't build device_sysOID %s: %s", + __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); - /* Build OIDs for comparison */ - if (!read_objid(sysOID_buf, device_sysOID, &device_sysOID_len)) + return NULL; + } + + /* Now, iterate on mib2nut definitions */ + for (i = 0; mib2nut[i] != NULL; i++) + { + upsdebugx(1, "%s: checking MIB %s", __func__, mib2nut[i]->mib_name); + + if (mib2nut[i]->sysOID == NULL) + continue; + + /* Clear variables */ + memset(mib2nut_sysOID, 0, sizeof(mib2nut_sysOID)); + mib2nut_sysOID_len = MAX_OID_LEN; + + if (!read_objid(mib2nut[i]->sysOID, mib2nut_sysOID, &mib2nut_sysOID_len)) { - upsdebugx(2, "%s: can't build device_sysOID %s: %s", + upsdebugx(2, "%s: can't build OID %s: %s", __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); - return NULL; + /* Try to continue anyway! */ + continue; } - /* Now, iterate on mib2nut definitions */ - for (i = 0; mib2nut[i] != NULL; i++) + /* Now compare these */ + upsdebugx(1, "%s: comparing %s with %s", __func__, sysOID_buf, mib2nut[i]->sysOID); + if (!netsnmp_oid_equals(device_sysOID, device_sysOID_len, mib2nut_sysOID, mib2nut_sysOID_len)) { - upsdebugx(1, "%s: checking MIB %s", __func__, mib2nut[i]->mib_name); + upsdebugx(2, "%s: sysOID matches MIB '%s'!", __func__, mib2nut[i]->mib_name); + /* Counter verify, using {ups,device}.model */ + snmp_info = mib2nut[i]->snmp_info; - if (mib2nut[i]->sysOID == NULL) + if (snmp_info == NULL) { +#if WITH_DMFMIB + upsdebugx(0, "%s: WARNING: snmp_info is not initialized " + "for mapping table entry #%d \"%s\"" + ", did you load DMF file(s) from correct directory? " + "(used '%s')", + __func__, i, mib2nut[i]->mib_name, dmf_dir + ); +#else /* not WITH_DMFMIB */ + upsdebugx(0, "%s: WARNING: snmp_info is not initialized " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name + ); +#endif /* WITH_DMFMIB */ continue; + } + else if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name); + } - /* Clear variables */ - memset(mib2nut_sysOID, 0, sizeof(mib2nut_sysOID)); - mib2nut_sysOID_len = MAX_OID_LEN; - - if (!read_objid(mib2nut[i]->sysOID, mib2nut_sysOID, &mib2nut_sysOID_len)) + if (match_model_OID() != TRUE) { - upsdebugx(2, "%s: can't build OID %s: %s", - __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); - - /* Try to continue anyway! */ + upsdebugx(2, "%s: testOID provided and doesn't match MIB '%s'!", __func__, mib2nut[i]->mib_name); + snmp_info = NULL; continue; } + else + upsdebugx(2, "%s: testOID provided and matches MIB '%s'!", __func__, mib2nut[i]->mib_name); - /* Now compare these */ - upsdebugx(1, "%s: comparing %s with %s", __func__, sysOID_buf, mib2nut[i]->sysOID); - if (!netsnmp_oid_equals(device_sysOID, device_sysOID_len, mib2nut_sysOID, mib2nut_sysOID_len)) - { - upsdebugx(2, "%s: sysOID matches MIB '%s'!", __func__, mib2nut[i]->mib_name); - /* Counter verify, using {ups,device}.model */ - snmp_info = mib2nut[i]->snmp_info; - - if (snmp_info == NULL) { -#if WITH_DMFMIB - upsdebugx(0, "%s: WARNING: snmp_info is not initialized " - "for mapping table entry #%d \"%s\"" - ", did you load DMF file(s) from correct directory? " - "(used '%s')", - __func__, i, mib2nut[i]->mib_name, dmf_dir - ); -#else /* not WITH_DMFMIB */ - upsdebugx(0, "%s: WARNING: snmp_info is not initialized " - "for mapping table entry #%d \"%s\"", - __func__, i, mib2nut[i]->mib_name - ); -#endif - continue; - } - else if (snmp_info[0].info_type == NULL) { - upsdebugx(1, "%s: WARNING: snmp_info is empty " - "for mapping table entry #%d \"%s\"", - __func__, i, mib2nut[i]->mib_name); - } - - if (match_model_OID() != TRUE) - { - upsdebugx(2, "%s: testOID provided and doesn't match MIB '%s'!", __func__, mib2nut[i]->mib_name); - snmp_info = NULL; - continue; - } - else - upsdebugx(2, "%s: testOID provided and matches MIB '%s'!", __func__, mib2nut[i]->mib_name); - - return mib2nut[i]; - } + return mib2nut[i]; } - - /* Yell all to call for user report */ - upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'!\n" \ - "Please report it to NUT developers, with an 'upsc' output for your device.\n" \ - "Going back to the classic MIB detection method.", - sysOID_buf); } - else - upsdebugx(2, "Can't get sysOID value"); + + /* Yell all to call for user report */ + upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'!\n" \ + "Please report it to NUT developers, with an 'upsc' output for your device.\n" \ + "Going back to the classic MIB detection method.", + sysOID_buf); return NULL; } @@ -2030,9 +2176,10 @@ bool_t load_mib2nut(const char *mib) __func__, i, mib2nut[i]->mib_name); if (!mibIsAuto && strcmp(mib, mib2nut[i]->mib_name)) { /* "mib" is neither "auto" nor the name in mapping table */ - upsdebugx(2, "%s: skip the \"%s\" entry which " - "is neither \"auto\" nor a valid name in the mapping table", - __func__, mib); + upsdebugx(2, "%s: skip the \"%s\" entry from " + "the mapping table which is not \"%s\" " + "(and which in turn is not \"auto\")", + __func__, mib2nut[i]->mib_name, mib); continue; } upsdebugx(2, "%s: trying classic sysOID matching method with '%s' mib", @@ -2054,7 +2201,7 @@ bool_t load_mib2nut(const char *mib) "for mapping table entry #%d \"%s\"", __func__, i, mib2nut[i]->mib_name ); -#endif +#endif /* WITH_DMFMIB */ continue; } else if (snmp_info[0].info_type == NULL) { @@ -2111,7 +2258,9 @@ bool_t load_mib2nut(const char *mib) /* String not seen during mib2nut[] walk - * and if we had no hits, we walked it all */ - fatalx(EXIT_FAILURE, "Unknown 'mibs' value: %s", mib); + fatalx(EXIT_FAILURE, "Unknown 'mibs' value " + "which is neither \"auto\" nor a valid " + "name in the mapping table: %s", mib); } } else { fatalx(EXIT_FAILURE, "No supported device detected at [%s] (host %s)", @@ -2141,14 +2290,16 @@ long su_find_valinfo(info_lkp_t *oid2info, const char* value) return -1; } -/* String reformating function */ +/* String reformatting function */ const char *su_find_strval(info_lkp_t *oid2info, void *value) { #if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { + const char *retvalue; + upsdebugx(2, "%s: using generic lookup function (string reformatting)", __func__); - const char * retvalue = oid2info->fun_vp2s(value); + retvalue = oid2info->fun_vp2s(value); upsdebugx(2, "%s: got value '%s'", __func__, retvalue); return retvalue; } @@ -2156,7 +2307,7 @@ const char *su_find_strval(info_lkp_t *oid2info, void *value) #else NUT_UNUSED_VARIABLE(oid2info); upsdebugx(1, "%s: no mapping function for this OID string value (%s)", __func__, (char*)value); -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ return NULL; } @@ -2169,12 +2320,14 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) #if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { + const char *retvalue; + upsdebugx(2, "%s: using generic lookup function", __func__); - const char * retvalue = oid2info->fun_vp2s(raw_value); + retvalue = oid2info->fun_vp2s(raw_value); upsdebugx(2, "%s: got value '%s'", __func__, retvalue); return retvalue; } -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ /* Otherwise, use the simple values mapping */ for (info_lkp = oid2info; (info_lkp != NULL) && @@ -2276,7 +2429,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 +2438,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 +2449,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... */ @@ -2327,17 +2485,17 @@ static void free_info(snmp_info_t *su_info_p) * the MIB, based on a test using a template OID */ static int base_snmp_template_index(const snmp_info_t *su_info_p) { + int base_index = -1; + char test_OID[SU_INFOSIZE]; + snmp_info_flags_t template_type; + if (!su_info_p) return -1; - int base_index = -1; - char test_OID[SU_INFOSIZE]; - snmp_info_flags_t template_type = get_template_type(su_info_p->info_type); - - if (!su_info_p->OID) - return base_index; + template_type = get_template_type(su_info_p->info_type); - upsdebugx(3, "%s: OID template = %s", __func__, su_info_p->OID); + upsdebugx(3, "%s: OID template = %s", __func__, + (su_info_p->OID ? su_info_p->OID : "") ); /* Try to differentiate between template types which may have * different indexes ; and store it to not redo it again */ @@ -2361,6 +2519,10 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) } base_index = template_index_base; + /* If no OID defined, now we can return the good index computed */ + if (!su_info_p->OID) + return base_index; + if (template_index_base == -1) { /* not initialised yet */ @@ -2412,6 +2574,7 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) } } } + /* Only store if it's a template for outlets or outlets groups, * not for daisychain (which has different index) */ if (su_info_p->flags & SU_OUTLET) @@ -2423,6 +2586,7 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) else device_template_index_base = base_index; } + upsdebugx(3, "%s: template_index_base = %i", __func__, base_index); return base_index; } @@ -2430,7 +2594,7 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) /* Try to determine the number of items (outlets, outlet groups, ...), * using a template definition. Walk through the template until we can't * get anymore values. I.e., if we can iterate up to 8 item, return 8 */ -static int guestimate_template_count(snmp_info_t *su_info_p) +static int guesstimate_template_count(snmp_info_t *su_info_p) { int base_index = 0; char test_OID[SU_INFOSIZE]; @@ -2439,6 +2603,12 @@ static int guestimate_template_count(snmp_info_t *su_info_p) upsdebugx(1, "%s(%s)", __func__, OID_template); + /* Test if OID is indexed: safeguard for infinite loop */ + if (strchr(OID_template, '%') == NULL) { + upsdebugx(3, "Warning: non-indexed object, discarding (OID = %s)", OID_template); + return 0; + } + /* Determine if OID index starts from 0 or 1? */ #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic push @@ -2518,8 +2688,8 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ if(dstate_getinfo(template_count_var) == NULL) { /* FIXME: should we disable it? * su_info_p->flags &= ~SU_FLAG_OK; - * or rely on guestimation? */ - template_count = guestimate_template_count(su_info_p); + * or rely on guesstimation? */ + template_count = guesstimate_template_count(su_info_p); /* Publish the count estimation */ if (template_count > 0) { dstate_setinfo(template_count_var, "%i", template_count); @@ -2795,10 +2965,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 +2981,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; } @@ -2816,6 +2989,7 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) } else { if (mode == SU_WALKMODE_INIT) { /* handle unsupported vars */ + upsdebugx(4, "%s: Disabling var '%s'", __func__, su_info_p->info_type); su_info_p->flags &= ~SU_FLAG_OK; } else { if (!(su_info_p->flags & SU_FLAG_STALE)) { @@ -2838,7 +3012,7 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) * Determine the number of device(s) and if daisychain support has to be enabled * Set the values of devices_count (internal) and "device.count" (public) * Return TRUE if daisychain support is enabled, FALSE otherwise */ -bool_t daisychain_init() +bool_t daisychain_init(void) { snmp_info_t *su_info_p = NULL; @@ -2855,12 +3029,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,30 +3049,30 @@ 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 */ else { - devices_count = guestimate_template_count(su_info_p); + devices_count = guesstimate_template_count(su_info_p); upsdebugx(1, "Guesstimation: there are %ld device(s) present", devices_count); } /* Sanity check before data publication */ - if (devices_count < 1) { devices_count = 1; daisychain_enabled = FALSE; @@ -3096,30 +3274,36 @@ static int process_phase_data(const char* type, long *nb_phases, snmp_info_t *su } #if WITH_DMF_LUA -int publish_Lua_dstate(lua_State *L){ +int publish_Lua_dstate(lua_State *L); +int publish_Lua_dstate(lua_State *L) { const char *info_type = lua_tostring(L, 1); const char *value = lua_tostring(L, 2); - if((info_type) && (value)) + if ((info_type) && (value)) dstate_setinfo(info_type, "%s", value); return 0; } -int lua_C_gateway(lua_State *L){ +int lua_C_gateway(lua_State *L); +int lua_C_gateway(lua_State *L) { /* get number of arguments */ - const char *info_type = lua_tostring(L, 1); - int current_device_number = lua_tointeger(L, 2); - - char *buf = (char *) malloc((strlen(info_type)+12) * sizeof(char)); + const char *lua_info_type = lua_tostring(L, 1); + int lua_current_device_number = lua_tointeger(L, 2); + const char *value; + char *buf = (char *) malloc((strlen(lua_info_type)+12) * sizeof(char)); + if (!buf) { + upsdebugx(1, "%s: failed to allocate a buffer", __func__); + return -1; + } - if(current_device_number > 0) - sprintf(buf, "device.%d.%s", current_device_number, info_type); + if (lua_current_device_number > 0) + sprintf(buf, "device.%d.%s", lua_current_device_number, lua_info_type); else - sprintf(buf, "device.%s", info_type); + sprintf(buf, "device.%s", lua_info_type); - const char *value = dstate_getinfo(buf); + value = dstate_getinfo(buf); - if(value) + if (value) lua_pushstring(L, value); /* return the number of results */ @@ -3132,7 +3316,10 @@ int lua_C_gateway(lua_State *L){ bool_t snmp_ups_walk(int mode) { long *walked_input_phases, *walked_output_phases, *walked_bypass_phases; - static unsigned long iterations = 0; +#ifdef COUNT_ITERATIONS + /* Experimental workaround for stale info */ + static unsigned long iterations = 0; +#endif snmp_info_t *su_info_p; bool_t status = FALSE; @@ -3161,8 +3348,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(); @@ -3186,14 +3378,14 @@ bool_t snmp_ups_walk(int mode) || (strcmp("lua-5.1", su_info_p->function_language)==0) || (strcmp("lua", su_info_p->function_language)==0) ) { -#if WITH_DMF_LUA +# if WITH_DMF_LUA if (su_info_p->luaContext){ - char *result = NULL; + char *result = NULL, *funcname; lua_register(su_info_p->luaContext, "lua_C_gateway", lua_C_gateway); lua_register(su_info_p->luaContext, "publish_Lua_dstate", publish_Lua_dstate); - char *funcname = snmp_info_type_to_main_function_name(su_info_p->info_type); + funcname = snmp_info_type_to_main_function_name(su_info_p->info_type); upsdebugx(4, "DMF-LUA: Going to call Lua funcname:\n%s\n", funcname ? funcname : "" ); upsdebugx(5, "DMF-LUA: Lua code block being interpreted:\n%s\n", su_info_p->function_code ); lua_getglobal(su_info_p->luaContext, funcname); @@ -3217,10 +3409,10 @@ bool_t snmp_ups_walk(int mode) free(buf); } } /* if (su_info_p->luaContext) */ -#else +# else /* not WITH_DMF_LUA */ upsdebugx(1, "SNMP_INFO entry backed by dynamic code in '%s' was skipped because support for this language is not compiled in", su_info_p->function_language ? su_info_p->function_language : "LUA"); -#endif /* WITH_DMF_LUA */ +# endif /* WITH_DMF_LUA */ } /* if function_language resolved to "lua*" */ else { upsdebugx(1, "SNMP_INFO entry backed by dynamic code in '%s' was skipped because support for this language is not compiled in", @@ -3231,16 +3423,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 +3472,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; } @@ -3321,11 +3530,13 @@ bool_t snmp_ups_walk(int mode) continue; } +#ifdef COUNT_ITERATIONS /* check stale elements only on each PN_STALE_RETRY iteration. */ - /* if ((su_info_p->flags & SU_FLAG_STALE) && + if ((su_info_p->flags & SU_FLAG_STALE) && (iterations % SU_STALE_RETRY) != 0) continue; - */ +#endif + /* Filter 1-phase Vs 3-phase according to {input,output,bypass}.phase. * Non matching items are disabled, and flags are cleared at init * time */ @@ -3396,7 +3607,11 @@ bool_t snmp_ups_walk(int mode) device_alarm_init(); } } + +#ifdef COUNT_ITERATIONS iterations++; +#endif + return status; } @@ -3412,14 +3627,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 +3659,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 +3670,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 +3686,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 +3747,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,11 +3767,30 @@ 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); - if( value > 0 ) { - pdu_array = nut_snmp_walk(su_info_p->OID, INT_MAX); + if (value > 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) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (value > INT_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "=> truncating alarms present to INT_MAX"); + value = INT_MAX; + } + pdu_array = nut_snmp_walk(su_info_p->OID, (int)value); if(pdu_array == NULL) { upsdebugx(2, "=> Walk failed"); return FALSE; @@ -3549,6 +3837,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,11 +3874,18 @@ 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) { + const char *fmt_buf; if (quirk_symmetra_threephase) { if (!strcasecmp(su_info_p->info_type, "input.transfer.low") || !strcasecmp(su_info_p->info_type, "input.transfer.high")) { @@ -3597,13 +3895,16 @@ bool_t su_ups_get(snmp_info_t *su_info_p) snprintf(buf, sizeof(buf), "%.2f", tmp_dvalue); } } - /* Check if there is a string reformating function */ - const char *fmt_buf = NULL; + /* Check if there is a string reformatting function */ + fmt_buf = NULL; if ((fmt_buf = su_find_strval(su_info_p->oid2info, buf)) != NULL) { snprintf(buf, sizeof(buf), "%s", fmt_buf); } } } else { + upsdebugx(2, "%s: requesting nut_snmp_get_int(), " + "with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { if ((su_info_p->flags&SU_FLAG_NEGINVALID && value<0) @@ -3635,11 +3936,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; @@ -3662,7 +3970,7 @@ static int su_setOID(int mode, const char *varname, const char *val) snmp_info_t *su_info_p = NULL; bool_t status; int retval = STAT_SET_FAILED; - int cmd_offset = 0; + int cmd_offset = 0; /* FIXME: Does not seem to be actually used! */ long value = -1; /* normal (default), outlet, or outlet group variable */ snmp_info_flags_t vartype = 0; @@ -3679,21 +3987,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,16 +4059,47 @@ 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 { - snmp_info_t *tmp_info_p; + /* 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; + const char *item_number_ptr = NULL; + char *item_varname; /* Store the target outlet or group number */ - int item_number = extract_template_number_from_snmp_info_t(tmp_varname); + int item_number = extract_template_number_from_snmp_info_t(tmp_varname); /* Store the total number of outlets or outlet groups */ - int total_items = -1; + int total_items = -1; /* Check if it is outlet / outlet.group */ vartype = get_template_type(tmp_varname); @@ -3745,7 +4125,7 @@ static int su_setOID(int mode, const char *varname, const char *val) return STAT_SET_INVALID; } /* find back the item template */ - char *item_varname = (char *)xmalloc(SU_INFOSIZE); + item_varname = (char *)xmalloc(SU_INFOSIZE); snprintf(item_varname, SU_INFOSIZE, "%s.%s%s", (vartype == SU_OUTLET)?"outlet":"outlet.group", "%i", strchr(item_number_ptr++, '.')); @@ -3783,8 +4163,8 @@ static int su_setOID(int mode, const char *varname, const char *val) /* Workaround buggy Eaton Pulizzi implementation * which have different offsets index for data & commands! */ if (su_info_p->flags & SU_CMD_OFFSET) { - upsdebugx(3, "Adding command offset"); cmd_offset++; + upsdebugx(3, "Adding command offset, now: %i", cmd_offset); } } @@ -3931,7 +4311,8 @@ static int su_setOID(int mode, const char *varname, const char *val) return retval; } -/* set r/w INFO_ element to a value. */ +/* set r/w INFO_ element to a value. + * FIXME: make a common function with su_instcmd! */ int su_setvar(const char *varname, const char *val) { return su_setOID(SU_MODE_SETVAR, varname, val); @@ -4015,6 +4396,7 @@ void read_mibconf(char *mib) { char fn[SMALLBUF]; PCONF_CTX_t ctx; + int numerrors = 0; upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, mib); @@ -4029,6 +4411,7 @@ void read_mibconf(char *mib) if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); + numerrors++; continue; } @@ -4046,8 +4429,17 @@ void read_mibconf(char *mib) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); + numerrors++; upslogx(LOG_WARNING, "%s", errmsg); } } + + /* FIXME: Per legacy behavior, we silently went on. + * Maybe should abort on unusable configs? + */ + if (numerrors) { + upslogx(LOG_ERR, "Encountered %d MIB config errors, those entries were ignored", numerrors); + } + pconf_finish(&ctx); } diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index a804de0220..8ae7e92a25 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -10,6 +10,7 @@ * 2002-2006 Dmitry Frolov * J.W. Hoogervorst * Niels Baggesen + * 2020-2024 Jim Klimov * * Sponsored by Eaton * and originally by MGE UPS SYSTEMS @@ -31,7 +32,6 @@ */ /* TODO list: -- add syscontact/location (to all mib.h or centralized?) - complete shutdown - add enum values to OIDs. - optimize network flow by: @@ -53,12 +53,27 @@ #ifndef SNMP_UPS_H #define SNMP_UPS_H +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + +#include "nut_stdint.h" /* uint32_t */ + +/* Net-SNMP relies on "u_char", "u_short", "u_long" and such, + * but does not pull the system types definitions in its headers. + */ +#include + /* Note: the snmp-ups.c code is built with legacy OR DMF mapping tables, * and the build recipes explicitly disable DMF for one binary and enable * it for another. */ #ifndef WITH_DMFMIB -#define WITH_DMFMIB 0 +# define WITH_DMFMIB 0 #endif #ifdef WANT_DMF_FUNCTIONS @@ -76,6 +91,13 @@ # undef WITH_DMF_FUNCTIONS # endif # define WITH_DMF_FUNCTIONS 0 +#else /* WITH_DMFMIB */ +# ifndef WITH_DMF_LUA +# define WITH_DMF_LUA 0 +# endif +# ifndef WITH_DMF_FUNCTIONS +# define WITH_DMF_FUNCTIONS 0 +# endif #endif #if WITH_DMF_LUA @@ -119,13 +141,24 @@ #undef HAVE_DMALLOC_H #endif -/* Net-SNMP relies on "u_char", "u_short", "u_long" and such, - * but does not pull the system types definitions in its headers. - */ -#include +#ifdef WIN32 +# ifdef random +# undef random +# endif +# ifdef _WIN32_WINNT +# undef _WIN32_WINNT +# endif +#endif + #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 @@ -139,10 +172,16 @@ #define DEFAULT_SEMISTATICFREQ 10 /* in snmpwalk update cycles */ /* use explicit booleans */ -#ifndef FALSE -typedef enum ebool { FALSE, TRUE } bool_t; -#else +#if !(defined HAVE_BOOL_T) || !HAVE_BOOL_T +# ifndef FALSE +typedef enum ebool { FALSE = 0, TRUE } bool_t; +# else typedef int bool_t; +# endif +# ifdef HAVE_BOOL_T +# undef HAVE_BOOL_T +# endif +# define HAVE_BOOL_T 1 #endif /* Common SNMP data and lookup definitions */ @@ -163,13 +202,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 */ @@ -184,8 +227,9 @@ typedef struct { * Currently the few cases using a "fun_vp2s" type of lookup function * 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). + * are added for completeness but are not really handled and do not + * have real consumers in the existing NUT codebase (static mib2nut + * tables in *-mib.c files). * Related to su_find_infoval() (long* => string), su_find_valinfo() * (string => long) and su_find_strval() (char* => string) routines * defined below. @@ -194,9 +238,25 @@ 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; +#if WITH_SNMP_LKP_FUN +# define info_lkp_default(_1, _2) {_1, _2, NULL, NULL, NULL, NULL} +# define info_lkp_fun_vp2s(_1, _2, _3) {_1, _2, _3, NULL, NULL, NULL} +# define info_lkp_nuf_s2l(_1, _2, _3) {_1, _2, NULL, _3, NULL, NULL} +# define info_lkp_fun_s2l(_1, _2, _3) {_1, _2, NULL, NULL, _3, NULL} +# define info_lkp_nuf_vp2s(_1, _2, _3) {_1, _2, NULL, NULL, NULL, _3} +#else +# define info_lkp_default(_1, _2) {_1, _2} +/* Ignore the function pointer where not supported */ +# define info_lkp_fun_vp2s(_1, _2, _3) {_1, _2} +# define info_lkp_nuf_s2l(_1, _2, _3) {_1, _2} +# define info_lkp_fun_s2l(_1, _2, _3) {_1, _2} +# define info_lkp_nuf_vp2s(_1, _2, _3) {_1, _2} +#endif /* WITH_SNMP_LKP_FUN */ +#define info_lkp_sentinel info_lkp_default(0, NULL) + /* Structure containing info about one item that can be requested from UPS and set in INFO. If no interpreter functions is defined, use sprintf with given format string. If unit is not NONE, values @@ -232,10 +292,21 @@ typedef struct { char *function_code; # if WITH_DMF_LUA lua_State *luaContext; -# endif -#endif +# endif /* WITH_DMF_LUA */ +#endif /* WITH_DMF_FUNCTIONS */ } snmp_info_t; +#if WITH_DMF_FUNCTIONS +# if WITH_DMF_LUA +# define snmp_info_default(_1, _2, _3, _4, _5, _6, _7) {_1, _2, _3, _4, _5, _6, _7, NULL, NULL, NULL} +# else +# define snmp_info_default(_1, _2, _3, _4, _5, _6, _7) {_1, _2, _3, _4, _5, _6, _7, NULL, NULL} +# endif /* WITH_DMF_LUA */ +#else +# define snmp_info_default(_1, _2, _3, _4, _5, _6, _7) {_1, _2, _3, _4, _5, _6, _7} +#endif /* WITH_DMF_FUNCTIONS */ +#define snmp_info_sentinel snmp_info_default(NULL, 0, 0, NULL, NULL, 0, NULL) + /* "flags" bits 0..9 */ #define SU_FLAG_OK (1UL << 0) /* show element to upsd - * internal to snmp driver */ @@ -255,8 +326,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 +370,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..dc9e260ce0 --- /dev/null +++ b/drivers/socomec_jbus.c @@ -0,0 +1,491 @@ +/* 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.07" + +#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) +{ + uint16_t tab_reg[12]; + int r; + + upsdebugx(2, "upsdrv_initinfo"); + + 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) +{ + uint16_t tab_reg[64]; + int r; + + upsdebugx(2, "upsdrv_updateinfo"); + + 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) +{ + /* replace with a proper shutdown function */ + upslogx(LOG_ERR, "shutdown not supported"); + set_exit_flag(-1); +} + +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 0 && nut_debug_level >= 4) { upsdebug_hex(4, "received from ser_get_buf_len()", packet, (size_t)tam); } @@ -875,15 +877,15 @@ static void get_update_info(void) { /* get update package */ temp[0] = 0; /* flush temp buffer */ - upsdebugx(3, "%s: requesting %zu bytes from ser_get_buf_len()", __func__, packet_size); + upsdebugx(3, "%s: requesting %" PRIuSIZE " bytes from ser_get_buf_len()", __func__, packet_size); tam = ser_get_buf_len(upsfd, temp, packet_size, 3, 0); if (tam < 0) { - upsdebugx(0, "%s: Error (%zd) reading from ser_get_buf_len()", __func__, tam); + upsdebugx(0, "%s: Error (%" PRIiSIZE ") reading from ser_get_buf_len()", __func__, tam); fatalx(EXIT_FAILURE, NO_SOLIS); } - upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + upsdebugx(2, "%s: received %" PRIiSIZE " bytes from ser_get_buf_len()", __func__, tam); if(tam > 0 && nut_debug_level >= 4) upsdebug_hex(4, "received from ser_get_buf_len()", temp, (size_t)tam); diff --git a/drivers/tripplite-hid.c b/drivers/tripplite-hid.c index 20d51526a2..ed93b3dd4d 100644 --- a/drivers/tripplite-hid.c +++ b/drivers/tripplite-hid.c @@ -29,7 +29,7 @@ #include "tripplite-hid.h" #include "usb-common.h" -#define TRIPPLITE_HID_VERSION "TrippLite HID 0.84" +#define TRIPPLITE_HID_VERSION "TrippLite HID 0.85" /* FIXME: experimental flag to be put in upsdrv_info */ @@ -154,6 +154,8 @@ static usb_device_id_t tripplite_usb_device_table[] = { /* Delta/Minuteman Enterprise Plus E1500RM2U */ { USB_DEVICE(DELTA_VENDORID, 0xa011), battery_scale_1dot0 }, + /* Delta/Minuteman PRO1500RT2U */ + { USB_DEVICE(DELTA_VENDORID, 0xa0a0), battery_scale_1dot0 }, /* Terminating entry */ { 0, 0, NULL } @@ -299,7 +301,7 @@ static usage_tables_t tripplite_utab[] = { /* HID2NUT lookup table */ static hid_info_t tripplite_hid2nut[] = { -#ifdef USBHID_UPS_TRIPPLITE_DEBUG +#if WITH_UNMAPPED_DATA_POINTS || (defined USBHID_UPS_TRIPPLITE_DEBUG) /* unmapped variables - meaning unknown */ { "UPS.Flow.0xffff0097", 0, 0, "UPS.Flow.0xffff0097", NULL, "%.0f", 0, NULL }, @@ -340,7 +342,7 @@ static hid_info_t tripplite_hid2nut[] = { { "UPS.OutletSystem.Outlet.0xffff00ac", 0, 0, "UPS.OutletSystem.Outlet.0xffff00ac", NULL, "%.0f", 0, NULL }, { "UPS.PowerSummary.iOEMInformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, -#endif /* USBHID_UPS_TRIPPLITE_DEBUG */ +#endif /* if WITH_UNMAPPED_DATA_POINTS || USBHID_UPS_TRIPPLITE_DEBUG */ /* Device page */ { "device.part", 0, 0, "UPS.TLCustom.[1].iUPSPartNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, diff --git a/drivers/tripplite.c b/drivers/tripplite.c index 672c734631..f353c38a7e 100644 --- a/drivers/tripplite.c +++ b/drivers/tripplite.c @@ -117,7 +117,7 @@ #include #define DRIVER_NAME "Tripp-Lite SmartUPS driver" -#define DRIVER_VERSION "0.93" +#define DRIVER_VERSION "0.94" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -395,7 +395,7 @@ void upsdrv_updateinfo(void) if (len != 21) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("Data command failed: [%zd] bytes != 21 bytes.", len); + ser_comm_fail("Data command failed: [%" PRIiSIZE "] bytes != 21 bytes.", len); dstate_datastale(); } return; @@ -612,9 +612,10 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { + char *val; + upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); - char *val; if ((val = getval("offdelay"))) { int ipv = atoi(val); diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 8b18786cd1..5dbc22f0c0 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -10,6 +10,7 @@ Copyright (C) 2004 Nicholas J. Kain Copyright (C) 2005-2008, 2014 Charles Lepple Copyright (C) 2016 Eaton + Copyright (C) 2023 Eliran Sapir 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 @@ -135,8 +136,8 @@ #include #include "usb-common.h" -#define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.32" +#define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" +#define DRIVER_VERSION "0.35" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -145,7 +146,8 @@ upsdrv_info_t upsdrv_info = { "Charles Lepple \n" \ "Russell Kroll \n" \ "Rickard E. (Rik) Faith \n" \ - "Nicholas J. Kain ", + "Nicholas J. Kain \n" \ + "Eliran Sapir ", DRV_EXPERIMENTAL, { NULL } }; @@ -173,19 +175,19 @@ static int subdriver_match_func(USBDevice_t *arghd, void *privdata) switch (is_usb_device_supported(tripplite_usb_device_table, arghd)) { - case SUPPORTED: - return 1; - - case POSSIBLY_SUPPORTED: - /* by default, reject, unless the productid option is given */ - if (getval("productid")) { + case SUPPORTED: return 1; - } - return 0; - case NOT_SUPPORTED: - default: - return 0; + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + return 0; + + case NOT_SUPPORTED: + default: + return 0; } } @@ -207,58 +209,58 @@ static enum tl_model_t { /*! Are the values encoded in ASCII or binary? * TODO: Add 3004? */ -static int is_binary_protocol() +static int is_binary_protocol(void) { switch(tl_model) { - case TRIPP_LITE_SMART_3005: - return 1; - case TRIPP_LITE_SMARTPRO: - case TRIPP_LITE_SMART_0004: - case TRIPP_LITE_OMNIVS: - case TRIPP_LITE_OMNIVS_2001: - case TRIPP_LITE_UNKNOWN: + case TRIPP_LITE_SMART_3005: + return 1; + case TRIPP_LITE_SMARTPRO: + case TRIPP_LITE_SMART_0004: + case TRIPP_LITE_OMNIVS: + case TRIPP_LITE_OMNIVS_2001: + case TRIPP_LITE_UNKNOWN: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - return 0; + return 0; } } /*! Is this the "SMART" family of protocols? * TODO: Add 3004? */ -static int is_smart_protocol() +static int is_smart_protocol(void) { switch(tl_model) { - case TRIPP_LITE_SMARTPRO: - case TRIPP_LITE_SMART_0004: - case TRIPP_LITE_SMART_3005: - return 1; - case TRIPP_LITE_OMNIVS: - case TRIPP_LITE_OMNIVS_2001: - case TRIPP_LITE_UNKNOWN: + case TRIPP_LITE_SMARTPRO: + case TRIPP_LITE_SMART_0004: + case TRIPP_LITE_SMART_3005: + return 1; + case TRIPP_LITE_OMNIVS: + case TRIPP_LITE_OMNIVS_2001: + case TRIPP_LITE_UNKNOWN: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - return 0; + return 0; } } @@ -282,6 +284,8 @@ static int is_smart_protocol() #define MAX_VOLT 13.4 /*!< Max battery voltage (100%) */ #define MIN_VOLT 11.0 /*!< Min battery voltage (10%) */ +#define DEFAULT_UPSID 65535 + static USBDevice_t *hd = NULL; static USBDevice_t curDevice; static USBDeviceMatcher_t *reopen_matcher = NULL; @@ -314,6 +318,63 @@ static long battery_voltage_nominal = 12, static unsigned int offdelay = DEFAULT_OFFDELAY; /* static unsigned int bootdelay = DEFAULT_BOOTDELAY; */ +/* Function declaration for send_cmd */ +static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *reply, size_t reply_len); + +/* Driver matching by ups.id since serial number isn't exposed on some Tripplite models. + Ups.id is the same as Unit Id, and it is a user configurable value between 1-65535. + Default Unit Id is 65535. May be set with upsrw, and it persists after powerloss as well. + To match by ups id, (upsid='your ups id') must be defined inside the ups.conf +*/ +int match_by_unitid(usb_dev_handle *argudev, USBDevice_t *arghd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen); +int match_by_unitid(usb_dev_handle *argudev, USBDevice_t *arghd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) +{ + char *value = getval("upsid"); + long config_unit_id = 0; + ssize_t ret; + unsigned char u_msg[] = "U"; + unsigned char u_value[9]; + + NUT_UNUSED_VARIABLE(argudev); + NUT_UNUSED_VARIABLE(arghd); + NUT_UNUSED_VARIABLE(rdbuf); + NUT_UNUSED_VARIABLE(rdlen); + + /* If upsid is not defined in the config, return 1 (null behavior - match any device), + * otherwise read it from the device and match against what was asked in ups.conf */ + if (value == NULL) { + return 1; + } else { + config_unit_id = atol(value); + } + + /* Read ups id from the device */ + if (tl_model != TRIPP_LITE_OMNIVS && tl_model != TRIPP_LITE_SMART_0004) { + /* Unit ID might not be supported by all models: */ + ret = send_cmd(u_msg, sizeof(u_msg), u_value, sizeof(u_value) - 1); + if (ret <= 0) { + upslogx(LOG_INFO, "Unit ID not retrieved (not available on all models)"); + } else { + /* Translating from two bytes (unsigned chars), so via uint16_t */ + unit_id = (uint16_t)((uint16_t)(u_value[1]) << 8) | (uint16_t)(u_value[2]); + upsdebugx(1, "Retrieved Unit ID: %ld", unit_id); + } + } + + /* Check if the ups ids match */ + if (config_unit_id == unit_id) { + upsdebugx(1, "Retrieved Unit ID (%ld) matches the configured one (%ld)", + unit_id, config_unit_id); + return 1; + } else { + upsdebugx(1, "Retrieved Unit ID (%ld) does not match the configured one (%ld). " + "Do you have several compatible UPSes? Otherwise, please check if the ID " + "was set in the previous life of your device (can use upsrw to set another" + "value).", unit_id, config_unit_id); + return 0; + } +} + /*!@brief Try to reconnect once. * @return 1 if reconnection was successful. */ @@ -325,11 +386,13 @@ static int reconnect_ups(void) return 1; } + dstate_setinfo("driver.state", "reconnect.trying"); + upsdebugx(2, "=================================================="); upsdebugx(2, "= device has been disconnected, try to reconnect ="); upsdebugx(2, "=================================================="); - ret = comm_driver->open(&udev, &curDevice, reopen_matcher, NULL); + ret = comm_driver->open_dev(&udev, &curDevice, reopen_matcher, match_by_unitid); if (ret < 1) { upslogx(LOG_INFO, "Reconnecting to UPS failed; will retry later..."); dstate_datastale(); @@ -337,6 +400,7 @@ static int reconnect_ups(void) } hd = &curDevice; + dstate_setinfo("driver.state", "quiet"); return ret; } @@ -507,6 +571,10 @@ static void decode_v(const unsigned char *value) input_voltage_scaled = 230; break; + case 6: input_voltage_nominal = + input_voltage_scaled = 230; + break; + default: upslogx(2, "Unknown input voltage range: 0x%02x", (unsigned int)ivn); break; @@ -539,7 +607,7 @@ static void usb_comm_fail(int res, const char *msg) static int try = 0; switch(res) { - case ERROR_BUSY: + case LIBUSB_ERROR_BUSY: upslogx(LOG_WARNING, "%s: Device claimed by another process", msg); fatalx(EXIT_FAILURE, "Terminating: EBUSY"); @@ -548,6 +616,7 @@ static void usb_comm_fail(int res, const char *msg) #endif default: + dstate_setinfo("driver.state", "reconnect.trying"); upslogx(LOG_WARNING, "%s: Device detached? (error %d: %s)", msg, res, nut_usb_strerror(res)); @@ -559,7 +628,9 @@ static void usb_comm_fail(int res, const char *msg) if(hd) { upslogx(LOG_NOTICE, "Successfully reconnected"); try = 0; + dstate_setinfo("driver.state", "reconnect.updateinfo"); upsdrv_initinfo(); + dstate_setinfo("driver.state", "quiet"); } else { if(try > MAX_RECONNECT_TRIES) { fatalx(EXIT_FAILURE, "Too many unsuccessful reconnection attempts"); @@ -585,11 +656,11 @@ static void usb_comm_fail(int res, const char *msg) */ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *reply, size_t reply_len) { - NUT_UNUSED_VARIABLE(reply_len); unsigned char buffer_out[8]; unsigned char csum = 0; int ret = 0, send_try, recv_try=0, done = 0; size_t i = 0; + NUT_UNUSED_VARIABLE(reply_len); upsdebugx(3, "send_cmd(msg_len=%u, type='%c')", (unsigned)msg_len, msg[0]); @@ -618,7 +689,7 @@ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *rep (usb_ctrl_charbufsize)sizeof(buffer_out)); if(ret != sizeof(buffer_out)) { - upslogx(1, "libusb_set_report() returned %d instead of %zu", + upslogx(1, "libusb_set_report() returned %d instead of %" PRIuSIZE, ret, sizeof(buffer_out)); return ret; } @@ -743,10 +814,11 @@ static int soft_shutdown(void) static int hard_shutdown(void) { int ret; - char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; + unsigned char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; - cmd_N[2] = offdelay; - cmd_N[1] = offdelay >> 8; + /* FIXME: Assumes memory layout / endianness? */ + cmd_N[2] = (unsigned char)(offdelay & 0x00FF); + cmd_N[1] = (unsigned char)(offdelay >> 8); upsdebugx(3, "hard_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); @@ -1012,7 +1084,7 @@ void upsdrv_initinfo(void) if(tl_model != TRIPP_LITE_SMARTPRO ) { ret = send_cmd(w_msg, sizeof(w_msg), w_value, sizeof(w_value)-1); if(ret <= 0) { - if(ret == ERROR_PIPE) { + if(ret == LIBUSB_ERROR_PIPE) { fatalx(EXIT_FAILURE, "Could not reset watchdog. Please check and" "see if usbhid-ups(8) works with this UPS."); } else { @@ -1442,7 +1514,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. */ @@ -1540,6 +1612,10 @@ void upsdrv_makevartable(void) MAX_VOLT); addvar(VAR_VALUE, "battery_max", msg); + /* allow -x upsid=X */ + snprintf(msg, sizeof msg, "UPS ID (Unit ID) (default=%d)", DEFAULT_UPSID); + addvar(VAR_VALUE, "upsid", msg); + #if 0 snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).", DEFAULT_STARTDELAY); @@ -1557,10 +1633,12 @@ void upsdrv_makevartable(void) */ void upsdrv_initups(void) { - char *regex_array[7]; + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; 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"); @@ -1569,6 +1647,9 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); /* probably won't see this */ regex_array[5] = getval("bus"); regex_array[6] = getval("device"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +#endif r = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); if (r==-1) { @@ -1582,7 +1663,7 @@ void upsdrv_initups(void) /* Search for the first supported UPS matching the regular * expression */ - r = comm_driver->open(&udev, &curDevice, regex_matcher, NULL); + r = comm_driver->open_dev(&udev, &curDevice, regex_matcher, match_by_unitid); if (r < 1) { fatalx(EXIT_FAILURE, "No matching USB/HID UPS found"); } @@ -1635,7 +1716,7 @@ void upsdrv_initups(void) void upsdrv_cleanup(void) { - comm_driver->close(udev); + comm_driver->close_dev(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); free(curDevice.Vendor); @@ -1643,4 +1724,7 @@ void upsdrv_cleanup(void) free(curDevice.Serial); free(curDevice.Bus); free(curDevice.Device); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(curDevice.BusPort); +#endif } diff --git a/drivers/tripplitesu.c b/drivers/tripplitesu.c index 05e89725c3..82d98eee88 100644 --- a/drivers/tripplitesu.c +++ b/drivers/tripplitesu.c @@ -123,9 +123,10 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" -#define DRIVER_NAME "Tripp Lite SmartOnline driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_NAME "Tripp Lite SmartOnline driver" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -192,9 +193,9 @@ static struct { #define RELAY_OFF "ROF" /* set */ #define RELAY_ON "RON" /* set */ #define ATX_RESUME "RSM" /* set */ -#define SHUTDOWN_ACTION "SDA" /* set */ -#define SHUTDOWN_RESTART "SDR" /* set */ -#define SHUTDOWN_TYPE "SDT" /* poll/set */ +#define TSU_SHUTDOWN_ACTION "SDA" /* set */ +#define TSU_SHUTDOWN_RESTART "SDR" /* set */ +#define TSU_SHUTDOWN_TYPE "SDT" /* poll/set */ #define RELAY_STATUS "SOL" /* poll/set */ #define SELECT_OUTPUT_VOLTAGE "SOV" /* poll/set */ #define STATUS_ALARM "STA" /* poll */ @@ -235,7 +236,7 @@ static ssize_t do_command(char type, const char *command, const char *parameters return -1; } - upsdebugx(3, "do_command: %zd bytes sent [%s] -> OK", ret, buffer); + upsdebugx(3, "do_command: %" PRIiSIZE " bytes sent [%s] -> OK", ret, buffer); ret = ser_get_buf_len(upsfd, (unsigned char *)buffer, 4, 3, 0); if (ret < 0) { @@ -248,9 +249,10 @@ static ssize_t do_command(char type, const char *command, const char *parameters } buffer[ret] = '\0'; - upsdebugx(3, "do_command: %zd byted read [%s]", ret, buffer); + upsdebugx(3, "do_command: %" PRIiSIZE " byted read [%s]", ret, buffer); if (!strcmp(buffer, "~00D")) { + int c; ret = ser_get_buf_len(upsfd, (unsigned char *)buffer, 3, 3, 0); if (ret < 0) { @@ -263,9 +265,9 @@ static ssize_t do_command(char type, const char *command, const char *parameters } buffer[ret] = '\0'; - upsdebugx(3, "do_command: %zd bytes read [%s]", ret, buffer); + upsdebugx(3, "do_command: %" PRIiSIZE " bytes read [%s]", ret, buffer); - int c = atoi(buffer); + c = atoi(buffer); if (c < 0) { upsdebugx(3, "do_command: response not expected to be a negative count!"); return -1; @@ -297,7 +299,7 @@ static ssize_t do_command(char type, const char *command, const char *parameters } response[ret] = '\0'; - upsdebugx(3, "do_command: %zd bytes read [%s]", ret, response); + upsdebugx(3, "do_command: %" PRIiSIZE " bytes read [%s]", ret, response); /* Tripp Lite pads their string responses with spaces. I don't like that, so I remove them. This is safe to @@ -422,7 +424,7 @@ static int get_sensitivity(void) { if (do_command(POLL, VOLTAGE_SENSITIVITY, "", response) <= 0) return 0; - for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) { + for (i = 0; i < SIZEOF_ARRAY(sensitivity); i++) { if (sensitivity[i].code == atoi(response)) { dstate_setinfo("input.sensitivity", "%s", sensitivity[i].name); @@ -437,7 +439,7 @@ static void set_sensitivity(const char *val) { char parm[20]; unsigned int i; - for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) { + for (i = 0; i < SIZEOF_ARRAY(sensitivity); i++) { if (!strcasecmp(val, sensitivity[i].name)) { snprintf(parm, sizeof(parm), "%u", i); do_command(SET, VOLTAGE_SENSITIVITY, parm, NULL); @@ -486,31 +488,31 @@ static int instcmd(const char *cmdname, const char *extra) } if (!strcasecmp(cmdname, "shutdown.reboot")) { auto_reboot(1); - do_command(SET, SHUTDOWN_RESTART, "1", NULL); - do_command(SET, SHUTDOWN_ACTION, "10", NULL); + do_command(SET, TSU_SHUTDOWN_RESTART, "1", NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { auto_reboot(1); - do_command(SET, SHUTDOWN_RESTART, "1", NULL); - do_command(SET, SHUTDOWN_ACTION, "60", NULL); + do_command(SET, TSU_SHUTDOWN_RESTART, "1", NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, "60", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "shutdown.return")) { auto_reboot(1); - do_command(SET, SHUTDOWN_RESTART, "1", NULL); - do_command(SET, SHUTDOWN_ACTION, "10", NULL); + do_command(SET, TSU_SHUTDOWN_RESTART, "1", NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } #if 0 /* doesn't seem to work */ if (!strcasecmp(cmdname, "shutdown.stayoff")) { auto_reboot(0); - do_command(SET, SHUTDOWN_ACTION, "10", NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, "10", NULL); return STAT_INSTCMD_HANDLED; } #endif if (!strcasecmp(cmdname, "shutdown.stop")) { - do_command(SET, SHUTDOWN_ACTION, "0", NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, "0", NULL); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { @@ -658,8 +660,7 @@ void upsdrv_initinfo(void) } if (get_sensitivity()) { dstate_setflags("input.sensitivity", ST_FLAG_RW); - for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); - i++) + for (i = 0; i < SIZEOF_ARRAY(sensitivity); i++) dstate_addenum("input.sensitivity", "%s", sensitivity[i].name); } @@ -809,8 +810,7 @@ void upsdrv_updateinfo(void) size_t trsize; r = atoi(response); - trsize = sizeof(test_result_names) / - sizeof(test_result_names[0]); + trsize = SIZEOF_ARRAY(test_result_names); if ((r < 0) || (r >= (int) trsize)) r = 0; @@ -854,9 +854,9 @@ void upsdrv_shutdown(void) /* in case the power is on, tell it to automatically reboot. if it is off, this has no effect. */ snprintf(parm, sizeof(parm), "%d", 1); /* delay before reboot, in minutes */ - do_command(SET, SHUTDOWN_RESTART, parm, NULL); + do_command(SET, TSU_SHUTDOWN_RESTART, parm, NULL); snprintf(parm, sizeof(parm), "%d", 5); /* delay before shutdown, in seconds */ - do_command(SET, SHUTDOWN_ACTION, parm, NULL); + do_command(SET, TSU_SHUTDOWN_ACTION, parm, NULL); } void upsdrv_help(void) diff --git a/drivers/upscode2.c b/drivers/upscode2.c index 50a4764a8f..5dac03bf20 100644 --- a/drivers/upscode2.c +++ b/drivers/upscode2.c @@ -43,7 +43,7 @@ #include "nut_float.h" #define DRIVER_NAME "UPScode II UPS driver" -#define DRIVER_VERSION "0.90" +#define DRIVER_VERSION "0.91" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -517,7 +517,7 @@ void upsdrv_initups(void) fatalx(EXIT_FAILURE, "Bad output_pace parameter: %s", str); output_pace_usec = (useconds_t)temp; } - upsdebugx(1, "output_pace = %ju uSec", (uintmax_t)output_pace_usec); + upsdebugx(1, "output_pace = %" PRIuMAX " uSec", (uintmax_t)output_pace_usec); if ((str = getval("full_update_timer")) != NULL) { int temp = atoi(str); @@ -1050,7 +1050,9 @@ static ssize_t upscrecv(char *buf) } else if (res == 0) { upsdebugx(3, "upscrecv: Timeout"); } else { - upsdebugx(3, "upscrecv: %zd bytes:\t'%s'", res-1, str_rtrim(buf, ENDCHAR)); + /* Note: s should end up same as buf */ + char *s = str_rtrim(buf, ENDCHAR); + upsdebugx(3, "upscrecv: %" PRIiSIZE " bytes:\t'%s'", res-1, s); } return res; diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index a02a7c31b4..1c8b6fd52c 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -24,12 +24,19 @@ #include #include #include +#ifndef WIN32 #include +#else +#include "wincompat.h" +#endif #include "proto.h" #include "common.h" #include "upsconf.h" #include "attribute.h" +#include "nut_stdint.h" +#include "main.h" +#include "upsdrvquery.h" typedef struct { char *upsname; @@ -37,12 +44,19 @@ typedef struct { char *port; int sdorder; int maxstartdelay; + int exceeded_timeout; +#ifndef WIN32 + pid_t pid; +#else + int pid; /* for WIN32 used just as a flag that this UPS was started by this tool in this run */ +#endif void *next; } ups_t; static ups_t *upstable = NULL; +static int upscount = 0; -static int maxsdorder = 0, testmode = 0, exec_error = 0; +static int maxsdorder = 0, testmode = 0, exec_error = 0, exec_timeout = 0; /* Should we wait for driver (1) or "parallelize" drivers start (0) */ static int waitfordrivers = 1; @@ -59,15 +73,34 @@ static int retrydelay = 5; /* Directory where driver executables live */ static char *driverpath = NULL; - /* passthrough to the drivers: chroot path and new user name */ -static char *pt_root = NULL, *pt_user = NULL; - -void do_upsconf_args(char *upsname, char *var, char *val) + /* passthrough to the drivers: chroot path and new user name; signal/command */ +static char *pt_root = NULL, *pt_user = NULL, *pt_cmd = NULL; + + /* flag to pass nut_debug_level to launched drivers (as their -D... args) */ +static int nut_debug_level_passthrough = 0; +static int nut_foreground_passthrough = -1; + +/* Keep track of requested operation (function pointer) */ +static void (*command)(const ups_t *) = NULL; + +/* signal handling */ +int exit_flag = 0; +#ifndef WIN32 +static int reload_flag = 0; +static time_t last_dangerous_reload = 0; +#endif +#ifndef WIN32 +static int signal_flag = 0; +#else +static char *signal_flag = NULL; +#endif + +void do_upsconf_args(char *arg_upsname, char *var, char *val) { ups_t *tmp, *last; /* handle global declarations */ - if (!upsname) { + if (!arg_upsname) { if (!strcmp(var, "maxstartdelay")) maxstartdelay = atoi(val); @@ -82,8 +115,14 @@ void do_upsconf_args(char *upsname, char *var, char *val) if (!strcmp(var, "retrydelay")) retrydelay = atoi(val); - if (!strcmp(var, "nowait")) - waitfordrivers = 0; + if (!strcmp(var, "nowait")) { + char * s = getenv("NUT_IGNORE_NOWAIT"); + if (s && !strcmp(s, "true")) { + upsdebugx(0, "NOTE: 'nowait' setting ignored due to NUT_IGNORE_NOWAIT envvar"); + } else { + waitfordrivers = 0; + } + } /* ignore anything else - it's probably for main */ @@ -95,7 +134,7 @@ void do_upsconf_args(char *upsname, char *var, char *val) while (tmp) { last = tmp; - if (!strcmp(tmp->upsname, upsname)) { + if (!strcmp(tmp->upsname, arg_upsname)) { if (!strcmp(var, "driver")) tmp->driver = xstrdup(val); @@ -119,12 +158,14 @@ void do_upsconf_args(char *upsname, char *var, char *val) } tmp = xmalloc(sizeof(ups_t)); - tmp->upsname = xstrdup(upsname); + tmp->upsname = xstrdup(arg_upsname); tmp->driver = NULL; tmp->port = NULL; + tmp->pid = -1; tmp->next = NULL; tmp->sdorder = 0; tmp->maxstartdelay = -1; /* use global value by default */ + tmp->exceeded_timeout = 0; if (!strcmp(var, "driver")) tmp->driver = xstrdup(val); @@ -138,46 +179,402 @@ void do_upsconf_args(char *upsname, char *var, char *val) upstable = tmp; } -/* handle sending the signal */ -static void stop_driver(const ups_t *ups) +static void signal_driver_cmd(const ups_t *ups, +#ifndef WIN32 + int cmd +#else + const char *cmd +#endif +) { +#ifndef WIN32 char pidfn[SMALLBUF]; +#endif int ret; - struct stat fs; - upsdebugx(1, "Stopping UPS: %s", ups->upsname); +#ifndef WIN32 + if (cmd == SIGCMD_RELOAD_OR_ERROR) +#else + if (cmd && !strcmp(cmd, SIGCMD_RELOAD_OR_ERROR)) +#endif + { + /* not a signal, use socket protocol */ + char buf[LARGEBUF]; + struct timeval tv; - snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), - ups->driver, ups->upsname); - ret = stat(pidfn, &fs); + upsdebugx(1, "Signalling UPS [%s]: %s", + ups->upsname, "driver.reload-or-error"); - if ((ret != 0) && (ups->port != NULL)) { - upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); - snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), - ups->driver, xbasename(ups->port)); - ret = stat(pidfn, &fs); - } + if (testmode) + return; - if (ret != 0) { - upslog_with_errno(LOG_ERR, "Can't open %s either", pidfn); + /* Post the query and wait for reply */ + /* FIXME: coordinate with pollfreq? */ + tv.tv_sec = 15; + tv.tv_usec = 0; + ret = upsdrvquery_oneshot(ups->driver, ups->upsname, + "INSTCMD driver.reload-or-error\n", + buf, sizeof(buf), &tv); + if (ret < 0) { + goto socket_error; + } else { + upslogx(LOG_INFO, "Request to reload-or-error returned code %d", ret); + if (ret != STAT_INSTCMD_HANDLED) + exec_error++; + /* TODO: Propagate "ret" to caller, eventually CLI exit-code? */ + } + + return; + +socket_error: + upslog_with_errno(LOG_ERR, "Socket dialog with the other driver instance"); exec_error++; return; } +#ifndef WIN32 +/* TODO: implement WIN32 */ +/* handle generally signalling the UPS */ + /* Real signals */ +#ifndef WIN32 + upsdebugx(1, "Signalling UPS [%s]: %d (%s)", + ups->upsname, cmd, strsignal(cmd)); +#else + upsdebugx(1, "Signalling UPS [%s]: %s", + ups->upsname, cmd); +#endif + +#ifndef WIN32 + if (ups->pid == -1) { + struct stat fs; + snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), + ups->driver, ups->upsname); + ret = stat(pidfn, &fs); + + if ((ret != 0) && (ups->port != NULL)) { + upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); + snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), + ups->driver, xbasename(ups->port)); + ret = stat(pidfn, &fs); + } + + if (ret != 0) { + upslog_with_errno(LOG_ERR, "Can't open %s either", pidfn); + exec_error++; + return; + } + } else { + /* We started the driver in this run of upsdrvctl + * tool, stayed foregrounded, and now are singnalling + * the driver(s) tracked by this process. + * NOTE: Not a filename here, but using same variable + * name makes the code below simpler to maintain. + */ + snprintf(pidfn, sizeof(pidfn), "PID %" PRIdMAX, (intmax_t)ups->pid); + } +#else + snprintf(pidfn, sizeof(pidfn), "%s-%s", ups->driver, ups->upsname); +#endif + upsdebugx(2, "Sending signal to %s", pidfn); if (testmode) return; - ret = sendsignalfn(pidfn, SIGTERM); +#ifndef WIN32 + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, cmd); + } else { + ret = sendsignalpid(ups->pid, cmd); + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + upslog_with_errno(LOG_WARNING, + "Child process %s exited; signal return code is %d", + pidfn, ret); + } + } +#else + ret = sendsignal(pidfn, cmd); +#endif if (ret < 0) { - upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn); + upslog_with_errno(LOG_ERR, "Signalling %s failed: %d", pidfn, ret); exec_error++; + } +#endif /* WIN32 */ +} + +/* handle generally signalling the UPS with recently raised signal */ +static void signal_driver(const ups_t *ups) { + signal_driver_cmd(ups, signal_flag); +} + +/* handle sending the signal */ +static void stop_driver(const ups_t *ups) +{ + char pidfn[SMALLBUF]; + int ret, i; + + upsdebugx(1, "Stopping UPS: %s", ups->upsname); + +#ifndef WIN32 + if (ups->pid == -1) { + struct stat fs; + snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), + ups->driver, ups->upsname); + ret = stat(pidfn, &fs); + + if ((ret != 0) && (ups->port != NULL)) { + upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); + snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), + ups->driver, xbasename(ups->port)); + ret = stat(pidfn, &fs); + } + + if (ret != 0) { + upslog_with_errno(LOG_ERR, "Can't open %s either", pidfn); + exec_error++; + return; + } + } else { + /* We started the driver in this run of upsdrvctl + * tool, stayed foregrounded, and now are exiting. + * NOTE: Not a filename here, but using same variable + * name makes the code below simpler to maintain. + */ + snprintf(pidfn, sizeof(pidfn), "PID %" PRIdMAX, (intmax_t)ups->pid); + } +#else + snprintf(pidfn, sizeof(pidfn), "%s-%s", ups->driver, ups->upsname); +#endif + + upsdebugx(2, "Sending signal to %s", pidfn); + + if (testmode) return; + +#ifndef WIN32 + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, SIGTERM); + } else { + ret = sendsignalpid(ups->pid, SIGTERM); + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + return; + } + } +#else + ret = sendsignal(pidfn, COMMAND_STOP); +#endif + + if (ret < 0) { +#ifndef WIN32 + upsdebugx(2, "SIGTERM to %s failed, retrying with SIGKILL", pidfn); + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, SIGKILL); + } else { + ret = sendsignalpid(ups->pid, SIGKILL); + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + return; + } + } +#else + upsdebugx(2, "Stopping %s failed, retrying again", pidfn); + ret = sendsignal(pidfn, COMMAND_STOP); +#endif + if (ret < 0) { + upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn); + exec_error++; + return; + } + } + + for (i = 0; i < 5 ; i++) { +#ifndef WIN32 + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, 0); + } else { + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + return; + } + ret = sendsignalpid(ups->pid, 0); + } +#else + ret = sendsignalfn(pidfn, 0); +#endif + if (ret != 0) { + upsdebugx(2, "Sending signal to %s failed, driver is finally down or wrongly owned", pidfn); + return; + } + sleep(1); } + +#ifndef WIN32 + upslog_with_errno(LOG_ERR, "Stopping %s failed, retrying harder", pidfn); + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, SIGKILL); + } else { + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + return; + } + ret = sendsignalpid(ups->pid, SIGKILL); + } +#else + upslog_with_errno(LOG_ERR, "Stopping %s failed, retrying again", pidfn); + ret = sendsignal(pidfn, COMMAND_STOP); +#endif + if (ret == 0) { + for (i = 0; i < 5 ; i++) { +#ifndef WIN32 + if (ups->pid == -1) { + ret = sendsignalfn(pidfn, 0); + } else { + /* reap zombie if this child died */ + if (waitpid(ups->pid, NULL, WNOHANG) == ups->pid) { + return; + } + ret = sendsignalpid(ups->pid, 0); + } +#else + ret = sendsignalfn(pidfn, 0); +#endif + if (ret != 0) { + upsdebugx(2, "Sending signal to %s failed, driver is finally down or wrongly owned", pidfn); + /* While a TERMinated driver cleans up, + * a stuck and KILLed one does not, so: + */ + if (ups->pid == -1) { + unlink(pidfn); + } + return; + } + sleep(1); + } + } + + upslog_with_errno(LOG_ERR, "Stopping %s failed", pidfn); + exec_error++; +} + +void set_exit_flag(const int sig) +{ + exit_flag = sig; +} + +static void set_signal_flag(const +#ifndef WIN32 + int +#else + char * +#endif + sig +) { + /* non-const, so some casting trickery */ + signal_flag = ( +#ifndef WIN32 + int +#else + char * +#endif + )sig; } +static void reset_signal_flag(void) +{ +#ifndef WIN32 + set_signal_flag(0); +#else + set_signal_flag(NULL); +#endif +} + +#ifndef WIN32 +/* TODO: Equivalent for WIN32 - see SIGCMD_RELOAD in upd and upsmon */ +static void set_reload_flag(const +#ifndef WIN32 + int +#else + char * +#endif + sig +) { + set_signal_flag(sig); + switch (sig) { + case SIGCMD_RELOAD_OR_EXIT: /* SIGUSR1 */ + /* reload-or-exit (this driver instance may die) */ + reload_flag = 2; + break; + +# ifdef SIGCMD_RELOAD_OR_RESTART + case SIGCMD_RELOAD_OR_RESTART: /* SIGUSR2 */ + /* reload-or-restart (this driver instance may recycle itself) */ + /* FIXME: Not implemented yet */ + reload_flag = 3; + break; +# endif + + case SIGCMD_RELOAD: /* SIGHUP */ + case SIGCMD_RELOAD_OR_ERROR: /* Not even a signal, but a socket protocol action */ + default: + /* reload what we can, log what needs a restart so skipped */ + reload_flag = 1; + } + + upsdebugx(1, "%s: raising reload flag due to signal %d (%s) => reload_flag=%d", + __func__, sig, strsignal(sig), reload_flag); +} +#endif + +static void setup_signals(void) +{ +#ifndef WIN32 + struct sigaction sa; +#endif + + set_exit_flag(0); + reset_signal_flag(); + +#ifndef WIN32 + /* Keep in sync with signal handling in drivers/main.c */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = set_exit_flag; + + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + sa.sa_handler = SIG_IGN; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif + sigaction(SIGPIPE, &sa, NULL); + + /* handle reloading */ + sa.sa_handler = set_reload_flag; + sigaction(SIGCMD_RELOAD, &sa, NULL); /* SIGHUP */ + sigaction(SIGCMD_RELOAD_OR_EXIT, &sa, NULL); /* SIGUSR1 */ +# ifdef SIGCMD_RELOAD_OR_RESTART +/* FIXME: Want SIGCMD_RELOAD_OR_RESTART implemented */ + sigaction(SIGCMD_RELOAD_OR_RESTART, &sa, NULL); /* SIGUSR2 */ +# endif + +# ifdef SIGCMD_DATA_DUMP + /* handle run-time data dump (may be limited to non-backgrounding lifetimes) */ + sa.sa_handler = set_signal_flag; + sigaction(SIGCMD_DATA_DUMP, &sa, NULL); /* SIGURG or SIGWINCH something else on obscure systems */ +# endif +#endif /* WIN32 */ +} + +#ifndef WIN32 static void waitpid_timeout(const int sig) { NUT_UNUSED_VARIABLE(sig); @@ -185,6 +582,7 @@ static void waitpid_timeout(const int sig) /* do nothing */ return; } +#endif /* print out a command line at the given debug level. */ static void debugcmdline(int level, const char *msg, char *const argv[]) @@ -202,97 +600,276 @@ static void debugcmdline(int level, const char *msg, char *const argv[]) static void forkexec(char *const argv[], const ups_t *ups) { +#ifndef WIN32 int ret; - pid_t pid; - pid = fork(); + if (nut_foreground_passthrough > 0 && upscount == 1) { + upsdebugx(1, "Starting the only driver with explicitly " + "requested foregrounding mode, not forking"); + } else { + pid_t pid, waitret; - if (pid < 0) - fatal_with_errno(EXIT_FAILURE, "fork"); + pid = fork(); - if (pid != 0) { /* parent */ - int wstat; - struct sigaction sa; + if (pid < 0) + fatal_with_errno(EXIT_FAILURE, "fork"); - /* Handle "parallel" drivers startup */ - if (waitfordrivers == 0) { - upsdebugx(2, "'nowait' set, continuing..."); - return; - } + if (pid != 0) { /* parent */ + int wstat; + struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = waitpid_timeout; - sigaction(SIGALRM, &sa, NULL); + /* work around const for this one... */ + int *pupid = (int *)&(ups->pid); + int *puexectimeout = (int *)&(ups->exceeded_timeout); + *pupid = pid; + *puexectimeout = 0; - /* Use the local maxstartdelay, if available */ - if (ups->maxstartdelay != -1) { - if (ups->maxstartdelay >= 0) - alarm((unsigned int)ups->maxstartdelay); - } else { /* Otherwise, use the global (or default) value */ - if (maxstartdelay >= 0) - alarm((unsigned int)maxstartdelay); - } + /* Handle "parallel" drivers startup */ + if (waitfordrivers == 0) { + upsdebugx(2, "'nowait' set, continuing..."); + return; + } - ret = waitpid(pid, &wstat, 0); + if (nut_foreground_passthrough > 0 && upscount > 1) { + /* Let upsdrvctl fork to run its numerous children + * but without further forking on their side - so + * not waiting for them to complete start-ups. + */ + upsdebugx(1, "Starting driver with explicitly " + "requested foregrounding mode: will not " + "wait for it to fork and detach, continuing..."); + return; + } - alarm(0); + if (nut_foreground_passthrough != 0 + && nut_debug_level > 0 + && nut_debug_level_passthrough > 0 + ) { + upsdebugx(2, "Starting driver with debug but " + "without explicit backgrounding: " + "will not wait for it to fork and " + "detach, continuing..."); + return; + } - if (ret == -1) { - upslogx(LOG_WARNING, "Startup timer elapsed, continuing..."); - exec_error++; - return; - } + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = waitpid_timeout; + sigaction(SIGALRM, &sa, NULL); + + /* Use the local maxstartdelay, if available */ + if (ups->maxstartdelay != -1) { + if (ups->maxstartdelay >= 0) + alarm((unsigned int)ups->maxstartdelay); + } else { /* Otherwise, use the global (or default) value */ + if (maxstartdelay >= 0) + alarm((unsigned int)maxstartdelay); + } - if (WIFEXITED(wstat) == 0) { - upslogx(LOG_WARNING, "Driver exited abnormally"); - exec_error++; - return; - } + waitret = waitpid(pid, &wstat, 0); - if (WEXITSTATUS(wstat) != 0) { - upslogx(LOG_WARNING, "Driver failed to start" - " (exit status=%d)", WEXITSTATUS(wstat)); - exec_error++; - return; - } + alarm(0); - /* the rest only work when WIFEXITED is nonzero */ + if (waitret == -1) { + upslogx(LOG_WARNING, "Startup timer elapsed, continuing..."); + exec_timeout++; + *puexectimeout = 1; + return; + } - if (WIFSIGNALED(wstat)) { - upslog_with_errno(LOG_WARNING, "Driver died after signal %d", - WTERMSIG(wstat)); - exec_error++; - } + if (WIFEXITED(wstat) == 0) { + upslogx(LOG_WARNING, "Driver exited abnormally"); + exec_error++; + return; + } - return; + /* the rest only work when WIFEXITED is nonzero */ + + if (WEXITSTATUS(wstat) != 0) { + upslogx(LOG_WARNING, "Driver failed to start" + " (exit status=%d)", WEXITSTATUS(wstat)); + exec_error++; + return; + } + + if (WIFSIGNALED(wstat)) { + upslog_with_errno(LOG_WARNING, "Driver died after signal %d", + WTERMSIG(wstat)); + exec_error++; + } + + return; + } } - /* child */ + /* child or foreground mode (no fork) */ ret = execv(argv[0], argv); - /* shouldn't get here */ + /* shouldn't get here normally */ + upsdebugx(1, "%s: execv returned %d", __func__, ret); fatal_with_errno(EXIT_FAILURE, "execv"); +#else + BOOL ret; + DWORD res; + DWORD exit_code = 0; + char commandline[SMALLBUF]; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + int i = 1; + + memset(&StartupInfo, 0, sizeof(STARTUPINFO)); + + /* the command line is made of the driver name followed by args */ + snprintf(commandline, sizeof(commandline), "%s", ups->driver); + while (argv[i] != NULL) { + snprintfcat(commandline, sizeof(commandline), " %s", argv[i]); + i++; + } + + ret = CreateProcess( + argv[0], + commandline, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + + if (ret == 0) { + fatal_with_errno(EXIT_FAILURE, "execv"); + } + + /* Wait a bit then look at driver process. + * Unlike under Linux, Windows spawn drivers directly. If the driver is alive, all is OK. + * An optimization can probably be implemented to prevent waiting so much time when all is OK. + */ + res = WaitForSingleObject(ProcessInformation.hProcess, + (ups->maxstartdelay!=-1?ups->maxstartdelay:maxstartdelay)*1000); + + if (res != WAIT_TIMEOUT) { + GetExitCodeProcess( ProcessInformation.hProcess, &exit_code ); + upslogx(LOG_WARNING, "Driver failed to start (exit status=%d)", ret); + exec_error++; + return; + } else { + /* work around const for this one... */ + int *pupid = (int *)&(ups->pid); + int *puexectimeout = (int *)&(ups->exceeded_timeout); + *pupid = 0; /* For WIN32, just a flag (not "-1" has a meaning) */ + *puexectimeout = 1; + } + + return; +#endif } static void start_driver(const ups_t *ups) { - char *argv[8]; - char dfn[SMALLBUF]; + char *argv[10]; + char dfn[SMALLBUF], dbg[SMALLBUF]; int ret, arg = 0; - int initial_exec_error = exec_error, drv_maxretry = maxretry; + int initial_exec_error = exec_error, initial_exec_timeout = exec_timeout, drv_maxretry = maxretry; struct stat fs; upsdebugx(1, "Starting UPS: %s", ups->upsname); +#ifndef WIN32 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); +#else + snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); +#endif ret = stat(dfn, &fs); if (ret < 0) 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 %" PRIuSIZE, + 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; + } + + /* Default: -1, FG/BG depends on debugging level */ + /* send_all_drivers() also warns if got many drivers to handle + * and foreground mode - it won't loop really */ + switch (nut_foreground_passthrough) { + case 0: + argv[arg++] = (char *)"-B"; /* FIXME: cast away const */ + break; + case 1: + argv[arg++] = (char *)"-F"; /* FIXME: cast away const */ + break; + case 2: + argv[arg++] = (char *)"-FF"; /* FIXME: cast away const */ + break; + default: + if (nut_debug_level_passthrough > 0 + && nut_debug_level > 0 + ) { + upsdebugx(1, "WARNING: Requested a debugging level " + "but not explicitly a backgrounding mode - " + "driver may never try to fork away; however " + "the upsdrvctl tool will fork it and not wait."); + } + } + argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; @@ -313,6 +890,7 @@ static void start_driver(const ups_t *ups) while (drv_maxretry > 0) { int cur_exec_error = exec_error; + int cur_exec_timeout = exec_timeout; upsdebugx(2, "%i remaining attempts", drv_maxretry); debugcmdline(2, "exec: ", argv); @@ -323,9 +901,10 @@ static void start_driver(const ups_t *ups) } /* driver command succeeded */ - if (cur_exec_error == exec_error) { + if (cur_exec_error == exec_error && cur_exec_timeout == exec_timeout) { drv_maxretry = 0; exec_error = initial_exec_error; + exec_timeout = initial_exec_timeout; } else { /* otherwise, retry if still needed */ @@ -339,16 +918,52 @@ static void start_driver(const ups_t *ups) static void help(const char *progname) __attribute__((noreturn)); -static void help(const char *progname) +static void help(const char *arg_progname) { printf("Starts and stops UPS drivers via ups.conf.\n\n"); - printf("usage: %s [OPTIONS] (start | stop | shutdown) []\n\n", progname); + printf("usage: %s [OPTIONS] (start | stop | shutdown) []\n\n", arg_progname); + printf("usage: %s [OPTIONS] -c []\n\n", arg_progname); + printf("Common options:\n"); printf(" -h display this help\n"); printf(" -r drivers will chroot to \n"); 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 raise debugging level\n"); + printf(" -d pass debugging level from upsdrvctl to driver\n"); + printf(" -F driver stays foregrounded even if no debugging is enabled\n"); + printf(" -FF driver stays foregrounded and still saves the PID file\n"); + printf(" -B driver(s) stay backgrounded even if debugging is bumped\n"); + + printf("Signalling a running driver:\n"); + printf(" -c send via signal to running driver(s)\n"); + printf(" supported commands:\n"); +#ifndef WIN32 +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ + printf(" - data-dump: if the driver still has STDOUT attached (maybe\n"); + printf(" to log), dump its currently collected information there\n"); + printf(" - reload: re-read configuration files, ignoring changed\n"); + printf(" values which require a driver restart (can not be changed\n"); + printf(" on the fly)\n"); +#endif /* WIN32 */ + printf(" - reload-or-error: re-read configuration files, ignoring but\n"); + printf(" counting changed values which require a driver restart (can\n"); + printf(" not be changed on the fly), and return a success/fail code\n"); + printf(" based on that count, so the caller can decide the fate of\n"); + printf(" the currently running driver instance\n"); +#ifndef WIN32 +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ +# ifdef SIGCMD_RELOAD_OR_RESTART + printf(" - reload-or-restart: re-read configuration files (close the\n"); + printf(" old driver instance device connection if needed, and have\n"); + printf(" it effectively restart)\n"); +# endif + printf(" - reload-or-exit: re-read configuration files (exit the old\n"); + printf(" driver instance if needed, so an external caller like the\n"); + printf(" systemd or SMF frameworks would start another copy)\n"); +#endif /* WIN32 */ + + printf("Driver life cycle options:\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"); @@ -367,7 +982,11 @@ static void shutdown_driver(const ups_t *ups) upsdebugx(1, "Shutdown UPS: %s", ups->upsname); +#ifndef WIN32 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); +#else + snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); +#endif argv[arg++] = dfn; argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ @@ -394,27 +1013,29 @@ static void shutdown_driver(const ups_t *ups) } } -static void send_one_driver(void (*command)(const ups_t *), const char *upsname) +static void send_one_driver(void (*command_func)(const ups_t *), const char *arg_upsname) { ups_t *ups = upstable; if (!ups) fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf!\n"); + exec_error = 0; + exec_timeout = 0; while (ups) { - if (!strcmp(ups->upsname, upsname)) { - command(ups); + if (!strcmp(ups->upsname, arg_upsname)) { + command_func(ups); return; } ups = ups->next; } - fatalx(EXIT_FAILURE, "UPS %s not found in ups.conf", upsname); + fatalx(EXIT_FAILURE, "UPS %s not found in ups.conf", arg_upsname); } /* walk UPS table and send command to all UPSes according to sdorder */ -static void send_all_drivers(void (*command)(const ups_t *)) +static void send_all_drivers(void (*command_func)(const ups_t *)) { ups_t *ups; int i; @@ -422,11 +1043,34 @@ static void send_all_drivers(void (*command)(const ups_t *)) if (!upstable) fatalx(EXIT_FAILURE, "Error: no UPS definitions found in ups.conf"); - if (command != &shutdown_driver) { + exec_error = 0; + exec_timeout = 0; + if (command_func != &shutdown_driver) { ups = upstable; + /* Only warn when relevant - got more than one device to start */ + if (command_func == &start_driver + && ups->next + && ( (nut_foreground_passthrough > 0) + || (nut_foreground_passthrough != 0 + && nut_debug_level > 0 + && nut_debug_level_passthrough > 0) + ) + ) { + upslogx(LOG_WARNING, + "Starting \"all\" drivers but requested the %s!" + "This request will not wait for driver(s) to complete " + "their initialization%s.", + (nut_foreground_passthrough > 0 + ? "foreground mode" + : "debug mode without backgrounding"), + (nut_foreground_passthrough > 0 + ? ", but upsdrvctl tool will stay foregrounded" : "") + ); + } + while (ups) { - command(ups); + command_func(ups); ups = ups->next; } @@ -434,12 +1078,13 @@ static void send_all_drivers(void (*command)(const ups_t *)) return; } + /* Orderly processing of shutdowns */ for (i = 0; i <= maxsdorder; i++) { ups = upstable; while (ups) { if (ups->sdorder == i) - command(ups); + command_func(ups); ups = ups->next; } @@ -450,8 +1095,25 @@ static void exit_cleanup(void) { ups_t *tmp, *next; + upsdebugx(1, "Completed the job of upsdrvctl tool, cleaning up and exiting now"); + tmp = upstable; + if (command == &start_driver + && upscount > 0 + && nut_foreground_passthrough > 0 + ) { + /* First stop the drivers, if any are running */ + while (tmp) { + next = tmp->next; + if (tmp->pid != -1) { + stop_driver(tmp); + } + tmp = next; + } + } + + tmp = upstable; while (tmp) { next = tmp->next; @@ -464,19 +1126,20 @@ static void exit_cleanup(void) } free(driverpath); + + upsdebugx(1, "Completed the job of upsdrvctl tool, clean-up finished, exiting now"); } int main(int argc, char **argv) { - int i; + int i, lastarg = 0; char *prog; - void (*command)(const ups_t *) = NULL; printf("Network UPS Tools - UPS driver controller %s\n", UPS_VERSION); prog = argv[0]; - while ((i = getopt(argc, argv, "+htu:r:DV")) != -1) { + while ((i = getopt(argc, argv, "+htu:r:DdFBVc:")) != -1) { switch(i) { case 'r': pt_root = optarg; @@ -497,59 +1160,354 @@ int main(int argc, char **argv) nut_debug_level++; break; + case 'd': + nut_debug_level_passthrough = 1; + break; + + case 'F': + if (nut_foreground_passthrough > 0) { + /* specified twice to save PID file anyway */ + nut_foreground_passthrough = 2; + } else { + nut_foreground_passthrough = 1; + } + break; + + case 'B': + nut_foreground_passthrough = 0; + break; + + case 'c': + if (command || pt_cmd) { + fatalx(EXIT_FAILURE, + "Error: only one command per run can be " + "sent with option -%c. Try -h for help.", i); + } + command = &signal_driver; + + if (!strncmp(optarg, "reload-or-error", strlen(optarg))) { + signal_flag = SIGCMD_RELOAD_OR_ERROR; + } +#ifndef WIN32 +/* FIXME: port event loop from upsd/upsmon to allow messaging fellow drivers in WIN32 builds */ + else + if (!strncmp(optarg, "dump", strlen(optarg))) { + signal_flag = SIGCMD_DATA_DUMP; + } else + if (!strncmp(optarg, "data-dump", strlen(optarg))) { + signal_flag = SIGCMD_DATA_DUMP; + } else + if (!strncmp(optarg, "reload", strlen(optarg))) { + signal_flag = SIGCMD_RELOAD; + } else +# ifdef SIGCMD_RELOAD_OR_RESTART + if (!strncmp(optarg, "reload-or-restart", strlen(optarg))) { + signal_flag = SIGCMD_RELOAD_OR_RESTART; + } else +# endif + if (!strncmp(optarg, "reload-or-exit", strlen(optarg))) { + signal_flag = SIGCMD_RELOAD_OR_EXIT; + } +#endif /* WIN32 */ + + /* bad command given */ + if (!signal_flag) { + fatalx(EXIT_FAILURE, + "Error: unknown argument to option -%c. Try -h for help.", i); + } + + pt_cmd = optarg; +#ifndef WIN32 + upsdebugx(1, "Will send signal %d (%s) for command '%s' " + "to already-running driver (if any) and exit", + signal_flag, strsignal(signal_flag), optarg); +#else + upsdebugx(1, "Will send request '%s' for command '%s' " + "to already-running driver (if any) and exit", + signal_flag, optarg); +#endif /* WIN32 */ + break; case 'h': default: help(prog); } } + { /* scoping */ + char *s = getenv("NUT_DEBUG_LEVEL"); + int l; + if (s && str_to_int(s, &l, 10)) { + if (l > 0 && nut_debug_level < 1) { + upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d " + "since none was requested by command-line options", l); + nut_debug_level = l; + } /* else follow -D settings */ + } /* else nothing to bother about */ + } + argc -= optind; argv += optind; - if (argc < 1) + /* We expect maybe command (if not signalling above) and maybe UPS name */ + if (!command && argc < 1) help(prog); if (testmode) { - printf("*** Testing mode: not calling exec/kill\n"); + printf("*** Testing mode: not calling exec/kill/signal\n"); if (nut_debug_level < 2) 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 (!strcmp(argv[0], "start")) - command = &start_driver; - - if (!strcmp(argv[0], "stop")) - command = &stop_driver; + 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, and '-B' keeps it backgrounded.\n"); + } - if (!strcmp(argv[0], "shutdown")) - command = &shutdown_driver; + if (!command) { + if (!strcmp(argv[0], "start")) { + command = &start_driver; + } else + if (!strcmp(argv[0], "stop")) { + command = &stop_driver; + } else + if (!strcmp(argv[0], "shutdown")) { + command = &shutdown_driver; + } + lastarg = 1; + } if (!command) fatalx(EXIT_FAILURE, "Error: unrecognized command [%s]", argv[0]); +#ifndef WIN32 driverpath = xstrdup(DRVPATH); /* set default */ +#else + driverpath = getfullpath(NULL); /* Relative path in WIN32 */ +#endif atexit(exit_cleanup); - read_upsconf(); + read_upsconf(1); + + if (argc == lastarg) { + ups_t *tmp = upstable; + upscount = 0; - if (argc == 1) + while (tmp) { + tmp = tmp->next; + upscount++; + } + + upsdebugx(1, "upsdrvctl commanding all drivers (%d found): %s", + upscount, (pt_cmd ? pt_cmd : NUT_STRARG(argv[lastarg]))); send_all_drivers(command); - else - send_one_driver(command, argv[1]); + } else + if (argc == (lastarg + 1)) { + upscount = 1; + upsdebugx(1, "upsdrvctl commanding one driver (%s): %s", + argv[lastarg], (pt_cmd ? pt_cmd : NUT_STRARG(argv[lastarg - 1]))); + send_one_driver(command, argv[lastarg]); + } else { + fatalx(EXIT_FAILURE, "Error: extra arguments left on command line\n" + "(common options should be before a command and UPS name)"); + } + + /* Note that the numeric value here is not precise (it reflects + * the number of "timeouts" which grows with amount of drivers + * and retries. Below we re-check each driver to convert the + * value into some amount of known failures (or succeses). */ + if (exec_timeout) { +#ifndef WIN32 + ups_t *tmp = upstable; +#endif + upsdebugx(1, "upsdrvctl: got some timeouts with preceding operations, revising them now"); +#ifndef WIN32 + while (tmp) { + if (tmp->exceeded_timeout && tmp->pid) { + /* reap zombie if this child died, and + * get info if we know how it went (or + * still goes) */ + int wstat; + pid_t waitret = waitpid(tmp->pid, &wstat, WNOHANG); + + upsdebugx(1, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay but now waitpid() returns %" PRIdMAX + " and status bits 0x%.*X", + tmp->upsname, (intmax_t)tmp->pid, + (intmax_t)waitret, (int)(2*sizeof(wstat)), wstat); + + if (waitret == tmp->pid) { + upsdebugx(1, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay but has finished by now", + tmp->upsname, (intmax_t)tmp->pid); + tmp->exceeded_timeout = 0; + } else + if (waitret == 0) { + /* Special behavior for WNOHANG */ + upslogx(LOG_WARNING, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay and is still starting", + tmp->upsname, (intmax_t)tmp->pid); + /* TOTHINK: Should this "timeout" cause an error + * exit code, if this is the only problem? + * Maybe as a special case - if this is the only + * driver (dedicated starter) vs. start-all? + * if (argc != (lastarg + 1)) ... + * or if (upscount == 1) ... + */ + exec_error++; + } else + if (waitret == -1) { + upslog_with_errno(LOG_WARNING, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay and we got an error asking it again", + tmp->upsname, (intmax_t)tmp->pid); + exec_error++; + } else + if (WIFEXITED(wstat) == 0) { + upslogx(LOG_WARNING, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay and has exited abnormally by now", + tmp->upsname, (intmax_t)tmp->pid); + exec_error++; + } else + /* the rest only work when WIFEXITED is nonzero */ + if (WEXITSTATUS(wstat) != 0) { + upslogx(LOG_WARNING, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay and has failed to start by now " + "(exit status=%d)", + tmp->upsname, (intmax_t)tmp->pid, WEXITSTATUS(wstat)); + exec_error++; + } else + if (WIFSIGNALED(wstat)) { + upslog_with_errno(LOG_WARNING, + "Driver [%s] PID %" PRIdMAX " initially exceeded " + "maxstartdelay and has died after signal %d by now", + tmp->upsname, (intmax_t)tmp->pid, WTERMSIG(wstat)); + exec_error++; + } + } - if (exec_error) + tmp = tmp->next; + } +#else /* WIN32 */ + /* TOTHINK: Is there something we can do on the platform? */ + exec_error++; +#endif /* WIN32 */ + } + + if (exec_error) { + upsdebugx(1, "upsdrvctl: got some errors with preceding operations, exiting with failure now"); exit(EXIT_FAILURE); + } + + if (command == &start_driver + && upscount > 0 + && nut_foreground_passthrough > 0 + ) { + /* Note: for a single started driver, we just + * exec() it and should not even get here + */ + upsdebugx(1, "upsdrvctl: was asked for explicit foregrounding - " + "not exiting now (driver startup was completed)"); + + /* raise exit_flag upon SIGTERM, Ctrl+C, etc. */ + setup_signals(); + while (!exit_flag) { +#ifndef WIN32 + ups_t *tmp = upstable, *next; + /* Track if any child process has stopped (due to + * an error, normal exit, signal...) to kill others + * and exit the tool - with error if applicable. + */ + while (tmp) { + next = tmp->next; + if (tmp->pid != -1) { + int status; + if (waitpid(tmp->pid, &status, WNOHANG) == tmp->pid) { + if (WIFEXITED(status)) { + int es = WEXITSTATUS(status); + time_t now; + double elapsed; + + time(&now); + elapsed = difftime(now, last_dangerous_reload); + if (elapsed < 60 + || (es - 128) == SIGCMD_RELOAD_OR_EXIT +# ifdef SIGCMD_RELOAD_OR_RESTART + || (es - 128) == SIGCMD_RELOAD_OR_RESTART +# endif + ) { + /* Arbitrary but generous time to handle + * a reload including driver loop lag */ + upsdebugx(1, "Driver [%s] for [%s] exited " + "soon after reload-or-exit or " + "similar signal, restarting it", + tmp->driver, tmp->upsname); + tmp->pid = -1; + start_driver(tmp); + } else { + /* Quit without excuses, recycle myself */ + upsdebugx(1, "Driver [%s] for [%s] exited " + "inexplicably with code %d, aborting", + tmp->driver, tmp->upsname, es); + if (last_dangerous_reload) + upsdebugx(1, "Last 'dangerous' signal " + "was processed %f sec ago", + elapsed); + exit_flag = -1; + tmp->pid = -1; + } + } + } + } + tmp = next; + } + + if (exit_flag == -1) { + fatalx(EXIT_FAILURE, "At least one tracked driver running " + "in foreground mode has exited, stopping upsdrvctl " + "(and other tracked drivers) so the bundle can be " + "restarted by system properly"); + /* NOTE: Users really should run one driver per instance, + * wrapped in services where available */ + } + + if (signal_flag) { + upsdebugx(1, "upsdrvctl: handling signal: starting"); + if (signal_flag == SIGCMD_RELOAD_OR_EXIT +# ifdef SIGCMD_RELOAD_OR_RESTART + || signal_flag == SIGCMD_RELOAD_OR_RESTART +# endif + ) time(&last_dangerous_reload); + + tmp = upstable; + while (tmp) { + next = tmp->next; + signal_driver(tmp); + tmp = next; + } + reset_signal_flag(); + upsdebugx(1, "upsdrvctl: handling signal: finished"); + } +#endif /* WIN32 */ + + sleep(1); + } + } + upsdebugx(1, "upsdrvctl: successfully finished"); exit(EXIT_SUCCESS); } diff --git a/drivers/upsdrvquery.c b/drivers/upsdrvquery.c new file mode 100644 index 0000000000..0936631ab5 --- /dev/null +++ b/drivers/upsdrvquery.c @@ -0,0 +1,633 @@ +/* upsdrvquery.c - a single query shot over a driver socket, + tracked until a response arrives, returning + that line and closing a connection + + Copyright (C) 2023 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "config.h" /* must be the first header */ + +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#include +#else +#include "wincompat.h" +#endif + +#include "common.h" +#include "upsdrvquery.h" +#include "nut_stdint.h" + +udq_pipe_conn_t *upsdrvquery_connect(const char *sockfn) { + udq_pipe_conn_t *conn = (udq_pipe_conn_t*)xcalloc(1, sizeof(udq_pipe_conn_t)); + + /* Code borrowed from our numerous sock_connect() implems */ +#ifndef WIN32 + struct sockaddr_un sa; + ssize_t ret; + + memset(&sa, '\0', sizeof(sa)); + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", sockfn); + + conn->sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (conn->sockfd < 0) { + upslog_with_errno(LOG_ERR, "open socket"); + free(conn); + return NULL; + } + + if (connect(conn->sockfd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + upslog_with_errno(LOG_ERR, "connect to driver socket at %s", sockfn); + close(conn->sockfd); + free(conn); + return NULL; + } + + ret = fcntl(conn->sockfd, F_GETFL, 0); + if (ret < 0) { + upslog_with_errno(LOG_ERR, "fcntl get on driver socket %s failed", sockfn); + close(conn->sockfd); + free(conn); + return NULL; + } + + if (fcntl(conn->sockfd, F_SETFL, ret | O_NDELAY) < 0) { + upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on driver socket %s failed", sockfn); + close(conn->sockfd); + free(conn); + return NULL; + } +#else + BOOL result = WaitNamedPipe(sockfn, NMPWAIT_USE_DEFAULT_WAIT); + + if (result == FALSE) { + upslog_with_errno(LOG_ERR, "WaitNamedPipe : %d\n", GetLastError()); + return NULL; + } + + conn->sockfd = CreateFile( + sockfn, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (conn->sockfd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "CreateFile : %d\n", GetLastError()); + free(conn); + return NULL; + } + + memset(&(conn->overlapped), 0, sizeof(conn->overlapped)); + + conn->overlapped.hEvent = CreateEvent( + NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state = non signaled */ + NULL /* no name */ + ); + + if (conn->overlapped.hEvent == NULL) { + upslogx(LOG_ERR, "Can't create event for reading event log"); + free(conn); + return NULL; + } + + memset(conn->buf, 0, sizeof(conn->buf)); + + /* Start a read IO so we could wait on the event associated with it + * Requires the persistent buffer for the connection + */ + upsdebugx(6, "%s: Queue initial async read", __func__); + ReadFile(conn->sockfd, conn->buf, + sizeof(conn->buf) - 1, /* -1 to be sure to have a trailing 0 */ + NULL, &conn->overlapped); + conn->newread = 0; +#endif /* WIN32 */ + + /* Just for fun: stash the name */ + if (snprintf(conn->sockfn, sizeof(conn->sockfn), "%s", sockfn) < 0) + conn->sockfn[0] = '\0'; + return conn; +} + +udq_pipe_conn_t *upsdrvquery_connect_drvname_upsname(const char *drvname, const char *upsname) { + char pidfn[SMALLBUF]; +#ifndef WIN32 + struct stat fs; + snprintf(pidfn, sizeof(pidfn), "%s/%s-%s", + dflt_statepath(), drvname, upsname); + check_unix_socket_filename(pidfn); + if (stat(pidfn, &fs)) { + upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); + return NULL; + } +#else + snprintf(pidfn, sizeof(pidfn), "\\\\.\\pipe\\%s-%s", drvname, upsname); +#endif /* WIN32 */ + + return upsdrvquery_connect(pidfn); +} + +void upsdrvquery_close(udq_pipe_conn_t *conn) { + if (!conn) + return; + +#ifndef WIN32 + if (VALID_FD(conn->sockfd)) + close(conn->sockfd); +#else + if (VALID_FD(conn->overlapped.hEvent)) { + CloseHandle(conn->overlapped.hEvent); + } + memset(&(conn->overlapped), 0, sizeof(conn->overlapped)); + + if (VALID_FD(conn->sockfd)) { + if (DisconnectNamedPipe(conn->sockfd) == 0) { + upslogx(LOG_ERR, + "DisconnectNamedPipe error : %d", + (int)GetLastError()); + } + CloseHandle(conn->sockfd); + } + conn->newread = 0; +#endif /* WIN32 */ + + conn->sockfd = ERROR_FD; + memset(conn->buf, 0, sizeof(conn->buf)); + memset(conn->sockfn, 0, sizeof(conn->sockfn)); + /* caller should free the conn */ +} + +ssize_t upsdrvquery_read_timeout(udq_pipe_conn_t *conn, struct timeval tv) { + ssize_t ret; +#ifndef WIN32 + fd_set rfds; +#else + DWORD bytesRead = 0; + BOOL res = FALSE; + struct timeval start, now, presleep; +#endif + + if (!conn || INVALID_FD(conn->sockfd)) { + upslog_with_errno(LOG_ERR, "socket not initialized"); + return -1; + } + +#ifndef WIN32 + FD_ZERO(&rfds); + FD_SET(conn->sockfd, &rfds); + + if (select(conn->sockfd + 1, &rfds, NULL, NULL, &tv) < 0) { + upslog_with_errno(LOG_ERR, "select with socket"); + /* upsdrvquery_close(conn); */ + return -1; + } + + if (!FD_ISSET(conn->sockfd, &rfds)) { + upsdebugx(5, "%s: received nothing from driver socket", __func__); + return -2; /* timed out, no info */ + } + + memset(conn->buf, 0, sizeof(conn->buf)); + ret = read(conn->sockfd, conn->buf, sizeof(conn->buf)); +#else +/* + upslog_with_errno(LOG_ERR, "Support for this platform is not currently implemented"); + return -1; +*/ + + /* Is GetLastError() required to move on if pipe has more data? + * if (GetLastError() == ERROR_IO_PENDING) { + */ + if (conn->newread) { + upsdebugx(6, "%s: Restart async read", __func__); + memset(conn->buf, 0, sizeof(conn->buf)); + ReadFile(conn->sockfd, conn->buf, sizeof(conn->buf) - 1, NULL, &(conn->overlapped)); + conn->newread = 0; + } + + gettimeofday(&start, NULL); + while (res == FALSE /*&& bytesRead == 0*/) { + res = GetOverlappedResult(conn->sockfd, &conn->overlapped, &bytesRead, FALSE); + if (res != FALSE /*|| bytesRead != 0*/) + break; + + if (tv.tv_sec < 1 && tv.tv_usec < 1) { + upsdebugx(5, "%s: pipe read error (no incoming data), proceeding now", __func__); + break; + } + upsdebugx(6, "%s: pipe read error, still waiting for data", __func__); + + /* Throttle down a bit, 0.1 sec (10^5 * 10^-6) should do it conveniently */ + gettimeofday(&presleep, NULL); + usleep(100000); /* obsoleted in win32, so follow up below */ + + gettimeofday(&now, NULL); + /* NOTE: This code may report a "diff=1.-894714 (0.105286)" + * which looks bogus, but for troubleshooting we don't care + */ + upsdebugx(7, "%s: presleep=%ld.%06ld now=%ld.%06ld diff=%4.0f.%06ld (%f)", + __func__, presleep.tv_sec, presleep.tv_usec, + now.tv_sec, now.tv_usec, + difftime(now.tv_sec, presleep.tv_sec), + (long)(now.tv_usec - presleep.tv_usec), + difftimeval(now, presleep) + ); + + /* accept shorter delays, Windows does not guarantee a minimum sleep it seems */ + if (difftimeval(now, presleep) < 0.05) { + /* https://stackoverflow.com/a/17283549 */ + HANDLE timer; + LARGE_INTEGER ft; + + /* SetWaitableTimer() uses 100 nanosecond intervals, + * and a negative value indicates relative time: */ + ft.QuadPart = -(10*100000); /* 100 msec */ + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); + + gettimeofday(&now, NULL); + upsdebugx(7, "%s: presleep=%ld.%06ld now=%ld.%06ld diff=%4.0f.%06ld (%f)", + __func__, presleep.tv_sec, presleep.tv_usec, + now.tv_sec, now.tv_usec, + difftime(now.tv_sec, presleep.tv_sec), + (long)(now.tv_usec - presleep.tv_usec), + difftimeval(now, presleep) + ); + } + + /* If nothing was honored, doze off for a whole second */ + if (difftimeval(now, presleep) < 0.05) { + sleep(1); + + gettimeofday(&now, NULL); + upsdebugx(7, "%s: presleep=%ld.%06ld now=%ld.%06ld diff=%4.0f.%06ld (%f)", + __func__, presleep.tv_sec, presleep.tv_usec, + now.tv_sec, now.tv_usec, + difftime(now.tv_sec, presleep.tv_sec), + (long)(now.tv_usec - presleep.tv_usec), + difftimeval(now, presleep) + ); + } + + if (difftimeval(now, start) > (tv.tv_sec + 0.000001 * tv.tv_usec)) { + upsdebugx(5, "%s: pipe read error, timeout exceeded", __func__); + break; + } + } + + if (res != FALSE) + ret = (ssize_t)bytesRead; + else + ret = -1; +#endif /* WIN32 */ + + upsdebugx(ret > 0 ? 5 : 6, + "%s: received %" PRIiMAX " bytes from driver socket: %s", + __func__, (intmax_t)ret, (ret > 0 ? conn->buf : "")); + if (ret > 0 && conn->buf[0] == '\0') + upsdebug_hex(5, "payload starts with zero byte: ", + conn->buf, ((size_t)ret > sizeof(conn->buf) ? sizeof(conn->buf) : (size_t)ret)); + return ret; +} + +ssize_t upsdrvquery_write(udq_pipe_conn_t *conn, const char *buf) { + size_t buflen = strlen(buf); +#ifndef WIN32 + ssize_t ret; +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; +#endif /* WIN32 */ + + upsdebugx(5, "%s: write to driver socket: %s", __func__, buf); + + if (!conn || INVALID_FD(conn->sockfd)) { + upslog_with_errno(LOG_ERR, "socket not initialized"); + return -1; + } + +#ifndef WIN32 + ret = write(conn->sockfd, buf, buflen); + + if (ret < 0 || ret != (int)buflen) { + upslog_with_errno(LOG_ERR, "Write to socket %d failed", conn->sockfd); + goto socket_error; + } + + return ret; +#else + result = WriteFile(conn->sockfd, buf, buflen, &bytesWritten, NULL); + if (result == 0 || bytesWritten != (DWORD)buflen) { + upslog_with_errno(LOG_ERR, "Write to handle %p failed", conn->sockfd); + goto socket_error; + } + + return (ssize_t)bytesWritten; +#endif /* WIN32 */ + +socket_error: + /*upsdrvquery_close(conn);*/ + return -1; +} + +ssize_t upsdrvquery_prepare(udq_pipe_conn_t *conn, struct timeval tv) { + struct timeval start, now; + + /* Avoid noise */ + if (upsdrvquery_write(conn, "NOBROADCAST\n") < 0) + goto socket_error; + + if (tv.tv_sec < 1 && tv.tv_usec < 1) { + upsdebugx(5, "%s: proclaiming readiness for tracked commands without flush of server messages", __func__); + return 1; + } + + /* flush incoming, if any */ + gettimeofday(&start, NULL); + + if (upsdrvquery_write(conn, "PING\n") < 0) + goto socket_error; + + upsdebugx(5, "%s: waiting for a while to flush server messages", __func__); + while (1) { + char *buf; + upsdrvquery_read_timeout(conn, tv); + gettimeofday(&now, NULL); + if (difftimeval(now, start) > ((double)(tv.tv_sec) + 0.000001 * (double)(tv.tv_usec))) { + upsdebugx(5, "%s: requested timeout expired", __func__); + break; + } + + /* Await a PONG for quick confirmation of achieved quietness + * (should only happen after the driver handled NOBROADCAST) + */ +#ifdef WIN32 + /* Allow a new read to happen later */ + conn->newread = 1; +#endif + + buf = conn->buf; + while (buf && *buf) { + if (!strncmp(buf, "PONG\n", 5)) { + upsdebugx(5, "%s: got expected PONG", __func__); + goto finish; + } + buf = strchr(buf, '\n'); + if (buf) { +/* + upsdebugx(5, "%s: trying next line of multi-line response: %s", + __func__, buf); +*/ + buf++; /* skip EOL char */ + } + } + + /* Diminishing timeouts for read() */ + tv.tv_usec -= (suseconds_t)(difftimeval(now, start)); + while (tv.tv_usec < 0) { + tv.tv_sec--; + tv.tv_usec = 1000000 + tv.tv_usec; /* Note it is negative */ + } + if (tv.tv_sec <= 0 && tv.tv_usec <= 0) { + upsdebugx(5, "%s: requested timeout expired", __func__); + break; + } + } + + /* Check that we can have a civilized dialog -- + * nope, this one is for network protocol */ +/* + if (upsdrvquery_write(conn, "GET TRACKING\n") < 0) + goto socket_error; + if (upsdrvquery_read_timeout(conn, tv) < 1) + goto socket_error; + if (strcmp(conn->buf, "ON")) { + upslog_with_errno(LOG_ERR, "Driver does not have TRACKING support enabled"); + goto socket_error; + } +*/ + +finish: + upsdebugx(5, "%s: ready for tracked commands", __func__); + return 1; + +socket_error: + upsdrvquery_close(conn); + return -1; +} + +/* UUID v4 basic implementation + * Note: 'dest' must be at least `UUID4_LEN` long */ +#define UUID4_BYTESIZE 16 +static int upsdrvquery_nut_uuid_v4(char *uuid_str) +{ + size_t i; + uint8_t nut_uuid[UUID4_BYTESIZE]; + + if (!uuid_str) + return 0; + + for (i = 0; i < UUID4_BYTESIZE; i++) + nut_uuid[i] = (uint8_t)rand() + (uint8_t)rand(); + + /* set variant and version */ + nut_uuid[6] = (nut_uuid[6] & 0x0F) | 0x40; + nut_uuid[8] = (nut_uuid[8] & 0x3F) | 0x80; + + return snprintf(uuid_str, UUID4_LEN, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + nut_uuid[0], nut_uuid[1], nut_uuid[2], nut_uuid[3], + nut_uuid[4], nut_uuid[5], nut_uuid[6], nut_uuid[7], + nut_uuid[8], nut_uuid[9], nut_uuid[10], nut_uuid[11], + nut_uuid[12], nut_uuid[13], nut_uuid[14], nut_uuid[15]); +} + +ssize_t upsdrvquery_request( + udq_pipe_conn_t *conn, struct timeval tv, + const char *query +) { + /* Assume TRACKING works; post a socket-protocol + * query to driver and return whatever it says */ + char qbuf[LARGEBUF]; + size_t qlen; + char tracking_id[UUID4_LEN]; + struct timeval start, now; + + if (snprintf(qbuf, sizeof(qbuf), "%s", query) < 0) + goto socket_error; + + qlen = strlen(qbuf); + while (qlen > 0 && qbuf[qlen - 1] == '\n') { + qbuf[qlen - 1] = '\0'; + qlen--; + } + + upsdrvquery_nut_uuid_v4(tracking_id); + + if (snprintf(qbuf + qlen, sizeof(qbuf) - qlen, " TRACKING %s\n", tracking_id) < 0) + goto socket_error; + + /* Post the query and wait for reply */ + if (upsdrvquery_write(conn, qbuf) < 0) + goto socket_error; + + if (tv.tv_sec < 1 && tv.tv_usec < 1) { + upsdebugx(1, "%s: will wait indefinitely for response to %s", + __func__, query); + } else { + while (tv.tv_usec >= 1000000) { + tv.tv_usec -= 1000000; + tv.tv_sec++; + } + upsdebugx(5, "%s: will wait up to %" PRIiMAX + ".%06" PRIiMAX " sec for response to %s", + __func__, (intmax_t)tv.tv_sec, + (intmax_t)tv.tv_usec, query); + } + + gettimeofday(&start, NULL); + while (1) { + char *buf; + if (upsdrvquery_read_timeout(conn, tv) < 1) + goto socket_error; + +#ifdef WIN32 + /* Allow a new read to happen later */ + conn->newread = 1; +#endif + + buf = conn->buf; + while (buf && *buf) { + if (!strncmp(buf, "TRACKING ", 9) + && !strncmp(buf + 9, tracking_id, UUID4_LEN - 1) + ) { + int ret; + size_t offset = 9 + UUID4_LEN; + if (sscanf(buf + offset, " %d", &ret) < 1) { + upsdebugx(5, "%s: sscanf failed at offset %" PRIuSIZE " (char '%c')", + __func__, offset, buf[offset]); + goto socket_error; + } + upsdebugx(5, "%s: parsed out command status: %d", + __func__, ret); + return ret; + } else { + upsdebugx(5, "%s: response did not have expected format", + __func__); + /* Maybe a rogue send-to-all? */ + } + buf = strchr(buf, '\n'); + if (buf) { + upsdebugx(5, "%s: trying next line of multi-line response: %s", + __func__, buf); + buf++; /* skip EOL char */ + } + } + + gettimeofday(&now, NULL); + if (tv.tv_sec < 1 && tv.tv_usec < 1) { + if ( ((long)(difftimeval(now, start))) % 60 == 0 ) + upsdebugx(5, "%s: waiting indefinitely for response to %s", __func__, query); + sleep(1); + continue; + } + + if (difftimeval(now, start) > ((double)(tv.tv_sec) + 0.000001 * (double)(tv.tv_usec))) { + upsdebugx(5, "%s: timed out waiting for expected response", + __func__); + return -1; + } + } + +socket_error: + upsdrvquery_close(conn); + return -1; +} + +ssize_t upsdrvquery_oneshot( + const char *drvname, const char *upsname, + const char *query, + char *buf, const size_t bufsz, + struct timeval *ptv +) { + struct timeval tv; + ssize_t ret; + udq_pipe_conn_t *conn = upsdrvquery_connect_drvname_upsname(drvname, upsname); + + if (!conn || INVALID_FD(conn->sockfd)) + return -1; + + /* This depends on driver looping delay, polling frequency, + * being blocked on other commands, etc. Number so far is + * arbitrary and optimistic. A non-zero setting causes a + * long initial silence to flush incoming buffers after + * the NOBROADCAST. In practice, we do not expect messages + * from dstate::send_to_all() to be a nuisance, since we + * have just connected and posted the NOBROADCAST so there + * is little chance that something appears in that short + * time. Also now we know to ignore replies that are not + * TRACKING + */ + tv.tv_sec = 3; + tv.tv_usec = 0; + + /* Here we have a fragile simplistic parser that + * expects one line replies to a specific command, + * so want to rule out the noise. + */ + if (upsdrvquery_prepare(conn, tv) < 0) { + ret = -1; + goto finish; + } + + if (ptv) { + tv.tv_sec = ptv->tv_sec; + tv.tv_usec = ptv->tv_usec; + } else { + tv.tv_sec = 5; + tv.tv_usec = 0; + } + if ((ret = upsdrvquery_request(conn, tv, query)) < 0) { + ret = -1; + goto finish; + } + + if (buf) { + snprintf(buf, bufsz, "%s", conn->buf); + } +finish: + upsdrvquery_close(conn); + free(conn); + return ret; +} diff --git a/drivers/upsdrvquery.h b/drivers/upsdrvquery.h new file mode 100644 index 0000000000..6a7b879d6b --- /dev/null +++ b/drivers/upsdrvquery.h @@ -0,0 +1,51 @@ +/* upsdrvquery.h - a single query shot over a driver socket, + tracked until a response arrives, returning + that line and closing a connection + + Copyright (C) 2023 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_UPSDRVQUERY_H_SEEN +#define NUT_UPSDRVQUERY_H_SEEN + +#include "common.h" /* TYPE_FD etc. */ +#include "timehead.h" + +typedef struct udq_pipe_conn_s { + TYPE_FD sockfd; +#ifdef WIN32 + OVERLAPPED overlapped; + int newread; /* Set to 1 to start a new ReadFile, forget old buf */ +#endif /* WIN32 */ + char buf[LARGEBUF]; + char sockfn[LARGEBUF]; +} udq_pipe_conn_t; + +udq_pipe_conn_t *upsdrvquery_connect(const char *sockfn); +udq_pipe_conn_t *upsdrvquery_connect_drvname_upsname(const char *drvname, const char *upsname); +void upsdrvquery_close(udq_pipe_conn_t *conn); + +ssize_t upsdrvquery_read_timeout(udq_pipe_conn_t *conn, struct timeval tv); +ssize_t upsdrvquery_write(udq_pipe_conn_t *conn, const char *buf); + +ssize_t upsdrvquery_prepare(udq_pipe_conn_t *conn, struct timeval tv); +ssize_t upsdrvquery_request(udq_pipe_conn_t *conn, struct timeval tv, const char *query); + +/* if buf != NULL, last reply is copied there */ +ssize_t upsdrvquery_oneshot(const char *drvname, const char *upsname, const char *query, char *buf, const size_t bufsz, struct timeval *tv); + +#endif /* NUT_UPSDRVQUERY_H_SEEN */ diff --git a/drivers/upshandler.h b/drivers/upshandler.h index fea10bc20c..78e6ffef0a 100644 --- a/drivers/upshandler.h +++ b/drivers/upshandler.h @@ -22,18 +22,20 @@ /* return values for instcmd */ enum { - STAT_INSTCMD_HANDLED = 0, /* completed successfully */ - STAT_INSTCMD_UNKNOWN, /* unspecified error */ - STAT_INSTCMD_INVALID, /* invalid command */ - STAT_INSTCMD_FAILED /* command failed */ + STAT_INSTCMD_HANDLED = 0, /* completed successfully */ + STAT_INSTCMD_UNKNOWN, /* unspecified error */ + STAT_INSTCMD_INVALID, /* invalid command */ + STAT_INSTCMD_FAILED, /* command failed */ + STAT_INSTCMD_CONVERSION_FAILED /* could not convert value */ }; /* return values for setvar */ enum { - STAT_SET_HANDLED = 0, /* completed successfully */ - STAT_SET_UNKNOWN, /* unspecified error */ - STAT_SET_INVALID, /* not writeable */ - STAT_SET_FAILED /* writing failed */ + STAT_SET_HANDLED = 0, /* completed successfully */ + STAT_SET_UNKNOWN, /* unspecified error */ + STAT_SET_INVALID, /* not writeable */ + STAT_SET_FAILED, /* writing failed */ + STAT_SET_CONVERSION_FAILED /* could not convert value from string */ }; /* structure for funcs that get called by msg parse routine */ diff --git a/drivers/usb-common.c b/drivers/usb-common.c index 1dedfb0757..56ca016a99 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be first */ #include "common.h" #include "usb-common.h" @@ -54,27 +55,6 @@ int is_usb_device_supported(usb_device_id_t *usb_device_id_list, USBDevice_t *de /* ---------------------------------------------------------------------- */ /* matchers */ -/* helper function: version of strcmp that tolerates NULL - * pointers. NULL is considered to come before all other strings - * alphabetically. - */ -static int strcmp_null(char *s1, char *s2) -{ - if (s1 == NULL && s2 == NULL) { - return 0; - } - - if (s1 == NULL) { - return -1; - } - - if (s2 == NULL) { - return 1; - } - - return strcmp(s1, s2); -} - /* private callback function for exact matches */ static int match_function_exact(USBDevice_t *hd, void *privdata) @@ -119,6 +99,15 @@ static int match_function_exact(USBDevice_t *hd, void *privdata) return 0; } #endif +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + #ifdef DEBUG_EXACT_MATCH_BUSPORT + if (strcmp_null(hd->BusPort, data->BusPort) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "BusPort", hd->BusPort, data->BusPort); + return 0; + } + #endif +#endif #ifdef DEBUG_EXACT_MATCH_DEVICE if (strcmp_null(hd->Device, data->Device) != 0) { upsdebugx(2, "%s: failed match of %s: %s != %s", @@ -164,6 +153,13 @@ int USBNewExactMatcher(USBDeviceMatcher_t **matcher, USBDevice_t *hd) #ifdef DEBUG_EXACT_MATCH_DEVICE data->Device = hd->Device ? strdup(hd->Device) : NULL; #endif + + /* NOTE: Callers must pre-initialize to NULL! */ + if (matcher && *matcher) { + free(*matcher); + *matcher = NULL; + } + *matcher = m; return 0; @@ -193,114 +189,9 @@ void USBFreeExactMatcher(USBDeviceMatcher_t *matcher) free(matcher); } -/* Private function for compiling a regular expression. On success, - * store the compiled regular expression (or NULL) in *compiled, and - * return 0. On error with errno set, return -1. If the supplied - * regular expression is unparseable, return -2 (an error message can - * then be retrieved with regerror(3)). Note that *compiled will be an - * allocated value, and must be freed with regfree(), then free(), see - * regex(3). As a special case, if regex==NULL, then set - * *compiled=NULL (regular expression NULL is intended to match - * anything). - */ -static int compile_regex(regex_t **compiled, char *regex, int cflags) -{ - int r; - regex_t *preg; - - if (regex == NULL) { - *compiled = NULL; - return 0; - } - - preg = malloc(sizeof(*preg)); - if (!preg) { - return -1; - } - - r = regcomp(preg, regex, cflags); - if (r) { - free(preg); - return -2; - } - - *compiled = preg; - - return 0; -} - -/* Private function for regular expression matching. Check if the - * entire string str (minus any initial and trailing whitespace) - * matches the compiled regular expression preg. Return 1 if it - * matches, 0 if not. Return -1 on error with errno set. Special - * cases: if preg==NULL, it matches everything (no contraint). If - * str==NULL, then it is treated as "". - */ -static int match_regex(regex_t *preg, char *str) -{ - int r; - size_t len = 0; - char *string; - regmatch_t match; - - if (!preg) { - return 1; - } - - if (!str) { - string = xstrdup(""); - } else { - /* skip leading whitespace */ - for (len = 0; len < strlen(str); len++) { - - if (!strchr(" \t\n", str[len])) { - break; - } - } - - string = xstrdup(str+len); - - /* skip trailing whitespace */ - for (len = strlen(string); len > 0; len--) { - - if (!strchr(" \t\n", string[len-1])) { - break; - } - } - - string[len] = '\0'; - } - - /* test the regular expression */ - r = regexec(preg, string, 1, &match, 0); - free(string); - if (r) { - return 0; - } - - /* check that the match is the entire string */ - if ((match.rm_so != 0) || (match.rm_eo != (int)len)) { - return 0; - } - - return 1; -} - -/* Private function, similar to match_regex, but the argument being - * matched is a (hexadecimal) number, rather than a string. It is - * converted to a 4-digit hexadecimal string. */ -static int match_regex_hex(regex_t *preg, int n) -{ - char buf[10]; - - snprintf(buf, sizeof(buf), "%04x", n); - - return match_regex(preg, buf); -} - /* private data type: hold a set of compiled regular expressions. */ typedef struct regex_matcher_data_s { - regex_t *regex[7]; + regex_t *regex[USBMATCHER_REGEXP_ARRAY_LIMIT]; } regex_matcher_data_t; /* private callback function for regex matches */ @@ -311,6 +202,9 @@ static int match_function_regex(USBDevice_t *hd, void *privdata) upsdebugx(3, "%s: matching a device...", __func__); + /* NOTE: Here and below the "detailed" logging is commented away + * because data->regex[] elements are not strings anymore! */ + r = match_regex_hex(data->regex[0], hd->VendorID); if (r != 1) { /* @@ -387,6 +281,19 @@ static int match_function_regex(USBDevice_t *hd, void *privdata) __func__, "Device", hd->Device); return r; } + +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + r = match_regex(data->regex[7], hd->BusPort); + if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Device", hd->Device, data->regex[6]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Bus Port", hd->BusPort); + return r; + } +#endif return 1; } @@ -423,7 +330,7 @@ int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags) m->privdata = (void *)data; m->next = NULL; - for (i=0; i<7; i++) { + for (i=0; i < USBMATCHER_REGEXP_ARRAY_LIMIT; i++) { r = compile_regex(&data->regex[i], regex[i], cflags); if (r == -2) { r = i+1; @@ -434,6 +341,12 @@ int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags) } } + /* NOTE: Callers must pre-initialize to NULL! */ + if (matcher && *matcher) { + free(*matcher); + *matcher = NULL; + } + *matcher = m; return 0; @@ -450,7 +363,7 @@ void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) data = (regex_matcher_data_t *)matcher->privdata; - for (i = 0; i < 7; i++) { + for (i = 0; i < USBMATCHER_REGEXP_ARRAY_LIMIT; i++) { if (!data->regex[i]) { continue; } @@ -462,3 +375,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..ba75091f75 100644 --- a/drivers/usb-common.h +++ b/drivers/usb-common.h @@ -57,7 +57,13 @@ #ifndef NUT_USB_COMMON_H #define NUT_USB_COMMON_H -#include "config.h" /* be sure to know all about the system config */ +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif /* Note: usb-common.h (this file) is included by nut_libusb.h, * so not looping the includes ;) @@ -102,7 +108,7 @@ * The MIN/MAX definitions here are primarily to generalize range-check * code (especially if anything is done outside the libraries). * FIXME: It may make sense to constrain the limits to lowest common - * denominator that should fit alll of libusb-0.1, libusb-1.0 and libshut, + * denominator that should fit all of libusb-0.1, libusb-1.0 and libshut, * so that any build of the practical (driver) code knows to not exceed * any use-case. */ @@ -122,6 +128,10 @@ #define USB_CTRL_MSGVALUE_MIN 0 #define USB_CTRL_MSGVALUE_MAX UINT16_MAX + typedef uint8_t usb_ctrl_cfgindex; + #define USB_CTRL_CFGINDEX_MIN 0 + #define USB_CTRL_CFGINDEX_MAX UINT8_MAX + typedef uint16_t usb_ctrl_repindex; #define USB_CTRL_REPINDEX_MIN 0 #define USB_CTRL_REPINDEX_MAX UINT16_MAX @@ -162,6 +172,7 @@ #define USB_TYPE_CLASS LIBUSB_REQUEST_TYPE_CLASS #define USB_TYPE_VENDOR LIBUSB_REQUEST_TYPE_VENDOR +/* Codebase updated to use LIBUSB_* tokens: #define ERROR_ACCESS LIBUSB_ERROR_ACCESS #define ERROR_BUSY LIBUSB_ERROR_BUSY #define ERROR_IO LIBUSB_ERROR_IO @@ -170,6 +181,12 @@ #define ERROR_OVERFLOW LIBUSB_ERROR_OVERFLOW #define ERROR_PIPE LIBUSB_ERROR_PIPE #define ERROR_TIMEOUT LIBUSB_ERROR_TIMEOUT + #define ERROR_NO_MEM LIBUSB_ERROR_NO_MEM + #define ERROR_INVALID_PARAM LIBUSB_ERROR_INVALID_PARAM + #define ERROR_INTERRUPTED LIBUSB_ERROR_INTERRUPTED + #define ERROR_NOT_SUPPORTED LIBUSB_ERROR_NOT_SUPPORTED + #define ERROR_OTHER LIBUSB_ERROR_OTHER +*/ /* Functions, including range-checks to convert data types of the two APIs. * Follows an example from libusb-1.0 headers that liberally cast int args @@ -370,11 +387,19 @@ /* Note: Checked above that in practice we handle some one libusb API */ #if WITH_LIBUSB_0_1 -# include +# ifdef HAVE_USB_H +# include +# else +# ifdef HAVE_LUSB0_USB_H +# include +# else +# error "configure script error: Neither HAVE_USB_H nor HAVE_LUSB0_USB_H is set for the WITH_LIBUSB_0_1 build" +# endif +# endif /* Structures */ /* See detailed comments above, in libusb-1.0 definitions * FIXME: It may make sense to constrain the limits to lowest common - * denominator that should fit alll of libusb-0.1, libusb-1.0 and libshut, + * denominator that should fit all of libusb-0.1, libusb-1.0 and libshut, * so that any build of the practical (driver) code knows to not exceed * any use-case. */ @@ -396,6 +421,10 @@ #define USB_CTRL_MSGVALUE_MIN INT_MIN #define USB_CTRL_MSGVALUE_MAX INT_MAX + typedef uint8_t usb_ctrl_cfgindex; + #define USB_CTRL_CFGINDEX_MIN 0 + #define USB_CTRL_CFGINDEX_MAX UINT8_MAX + typedef int usb_ctrl_repindex; #define USB_CTRL_REPINDEX_MIN INT_MIN #define USB_CTRL_REPINDEX_MAX INT_MAX @@ -425,14 +454,19 @@ #define USB_CTRL_TIMEOUTMSEC_MAX INT_MAX /* defines */ - #define ERROR_ACCESS -EACCES - #define ERROR_BUSY -EBUSY - #define ERROR_IO -EIO - #define ERROR_NO_DEVICE -ENODEV - #define ERROR_NOT_FOUND -ENOENT - #define ERROR_OVERFLOW -EOVERFLOW - #define ERROR_PIPE -EPIPE - #define ERROR_TIMEOUT -ETIMEDOUT + #define LIBUSB_ERROR_ACCESS -EACCES + #define LIBUSB_ERROR_BUSY -EBUSY + #define LIBUSB_ERROR_IO -EIO + #define LIBUSB_ERROR_NO_DEVICE -ENODEV + #define LIBUSB_ERROR_NOT_FOUND -ENOENT + #define LIBUSB_ERROR_OVERFLOW -EOVERFLOW + #define LIBUSB_ERROR_PIPE -EPIPE + #define LIBUSB_ERROR_TIMEOUT -ETIMEDOUT + #define LIBUSB_ERROR_NO_MEM -ENOMEM + #define LIBUSB_ERROR_INVALID_PARAM -EINVAL + #define LIBUSB_ERROR_INTERRUPTED -EINTR + #define LIBUSB_ERROR_NOT_SUPPORTED -ENOSYS + #define LIBUSB_ERROR_OTHER -ERANGE /* Functions for which simple mappings seem to suffice (no build warnings emitted): */ #define nut_usb_strerror(a) usb_strerror() @@ -440,10 +474,15 @@ /* USB standard timeout [ms] */ #define USB_TIMEOUT 5000 +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) +# define USBMATCHER_REGEXP_ARRAY_LIMIT 8 +#else +# define USBMATCHER_REGEXP_ARRAY_LIMIT 7 +#endif /*! * USBDevice_t: Describe a USB device. This structure contains exactly - * the 5 pieces of information by which a USB device identifies + * the 7 pieces of information by which a USB device identifies * itself, so it serves as a kind of "fingerprint" of the device. This * information must be matched exactly when reopening a device, and * therefore must not be "improved" or updated by a client @@ -462,6 +501,9 @@ typedef struct USBDevice_s { char *Bus; /*!< Bus name, e.g. "003" */ uint16_t bcdDevice; /*!< Device release number */ char *Device; /*!< Device name on the bus, e.g. "001" */ +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + char *BusPort; /*!< Port name, e.g. "001" */ +#endif } USBDevice_t; /*! @@ -513,4 +555,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..4775d38fe8 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,18 +28,25 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.45" +#define DRIVER_VERSION "0.52" -#include "main.h" +#define HU_VAR_WAITBEFORERECONNECT "waitbeforereconnect" + +#include "main.h" /* Must be first, includes "config.h" */ +#include "nut_stdint.h" #include "libhid.h" #include "usbhid-ups.h" #include "hidparser.h" #include "hidtypes.h" +#include "common.h" +#ifdef WIN32 +#include "wincompat.h" +#endif /* include all known subdrivers */ #include "mge-hid.h" -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) /* explore stub goes first, others alphabetically */ #include "explore-hid.h" #include "apc-hid.h" @@ -56,15 +63,16 @@ #include "powervar-hid.h" #include "salicru-hid.h" #include "tripplite-hid.h" -#endif +#endif /* !SHUT_MODE => USB */ -/* Reference list of avaiable subdrivers */ +/* Reference list of available subdrivers */ static subdriver_t *subdriver_list[] = { -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) &explore_subdriver, -#endif +#endif /* !SHUT_MODE => USB */ + /* mge-hid.c supports both SHUT and USB */ &mge_subdriver, -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) &apc_subdriver, &arduino_subdriver, &belkin_subdriver, @@ -79,7 +87,7 @@ static subdriver_t *subdriver_list[] = { &powervar_subdriver, &salicru_subdriver, &tripplite_subdriver, -#endif +#endif /* !SHUT_MODE => USB */ NULL }; @@ -91,11 +99,11 @@ upsdrv_info_t upsdrv_info = { "Arjen de Korte \n" \ "John Stamp ", /*FIXME: link the subdrivers? do the same as for the mibs! */ -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) DRV_STABLE, -#else +#else /* SHUT_MODE */ DRV_EXPERIMENTAL, -#endif +#endif /* SHUT_MODE / USB */ { &comm_upsdrv_info, NULL } }; @@ -106,38 +114,21 @@ typedef enum { HU_WALKMODE_FULL_UPDATE } walkmode_t; -/* Compatibility layer between libusb 0.1 and 1.0, for errno/return codes */ -#if WITH_LIBUSB_0_1 || (defined SHUT_MODE) - #define ERROR_BUSY -EBUSY - #define ERROR_NO_DEVICE -ENODEV - #define ERROR_ACCESS -EACCES - #define ERROR_IO -EIO - #define ERROR_NOT_FOUND -ENOENT - #define ERROR_TIMEOUT -ETIMEDOUT - #define ERROR_OVERFLOW -EOVERFLOW - #define ERROR_PIPE -EPIPE -#else /* for libusb 1.0 */ - #define ERROR_BUSY LIBUSB_ERROR_BUSY - #define ERROR_NO_DEVICE LIBUSB_ERROR_NO_DEVICE - #define ERROR_ACCESS LIBUSB_ERROR_ACCESS - #define ERROR_IO LIBUSB_ERROR_IO - #define ERROR_NOT_FOUND LIBUSB_ERROR_NOT_FOUND - #define ERROR_TIMEOUT LIBUSB_ERROR_TIMEOUT - #define ERROR_OVERFLOW LIBUSB_ERROR_OVERFLOW - #define ERROR_PIPE LIBUSB_ERROR_PIPE -#endif - /* pointer to the active subdriver object (changed in callback() function) */ static subdriver_t *subdriver = NULL; /* Global vars */ static HIDDevice_t *hd = NULL; -static HIDDevice_t curDevice = { 0x0000, 0x0000, NULL, NULL, NULL, NULL, 0, NULL }; +static HIDDevice_t curDevice = { 0x0000, 0x0000, NULL, NULL, NULL, NULL, 0, NULL +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + , NULL +#endif +}; static HIDDeviceMatcher_t *subdriver_matcher = NULL; -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) static HIDDeviceMatcher_t *exact_matcher = NULL; static HIDDeviceMatcher_t *regex_matcher = NULL; -#endif +#endif /* !SHUT_MODE => USB */ static int pollfreq = DEFAULT_POLLFREQ; static unsigned ups_status = 0; static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */ @@ -146,8 +137,63 @@ bool_t use_interrupt_pipe = TRUE; #else bool_t use_interrupt_pipe = FALSE; #endif +static size_t interrupt_pipe_EIO_count = 0; /* How many times we had I/O errors since last reconnect? */ 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. + * NOTE: Also known by legacy alias "onlinedischarge", deprecated but tolerated + * since NUT v2.8.2. + */ +static int onlinedischarge_onbattery = 0; + +/** + * Some UPS models (e.g. APC were seen to do so) report OL & DISCHRG when they + * are in calibration mode. This usually happens after a few seconds reporting + * an "OFF" state as well, while the hardware is switching to on-battery mode. + */ +static int onlinedischarge_calibration = 0; + +/** + * If/when an UPS reports OL & DISCHRG and we do not use any other special + * settings (e.g. they do not match the actual device capabilities/behaviors), + * the driver logs messages about the unexpected situation - on every cycle + * if must be. This setting allows to throttle frequency of such messages. + * A value of 0 is small enough to log on every processing cycle (old noisy + * de-facto default before NUT v2.8.2 which this throttle alleviates). + * A value of -1 (any negative passed via configuration) disables repeated + * messages completely. + * Default (-2) below is not final: if this throttle variable is *not* set + * in the driver configuration section, and... + * - If the device reports a battery.charge, the driver would default to + * only repeat reports about OL & DISCHRG when this charge changes from + * what it was when the previous report was made, independent of time; + * - Otherwise 30 sec. + * If both the throttle is set and battery.charge is reported (and changes + * over time), then hitting either trigger allows the message to be logged. + */ +static int onlinedischarge_log_throttle_sec = -2; +/** + * When did we last emit the message? + */ +static time_t onlinedischarge_log_throttle_timestamp = 0; +/** + * Last known battery charge (rounded to whole percent units) + * as of when we last actually logged about OL & DISCHRG. + * Gets reset to -1 whenever this condition is not present. + */ +static int onlinedischarge_log_throttle_charge = -1; +/** + * If battery.charge is served and equals or exceeds this value, + * suppress logging about OL & DISCHRG if battery.charge varied + * since last logged message. Defaults to 100% as some devices + * only report this state combo when fully charged (probably + * they try to prolong battery life by not over-charging it). + */ +static int onlinedischarge_log_throttle_hovercharge = 100; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -169,7 +215,7 @@ static double interval(void); /* global variables */ HIDDesc_t *pDesc = NULL; /* parsed Report Descriptor */ reportbuf_t *reportbuf = NULL; /* buffer for most recent reports */ - +int disable_fix_report_desc = 0; /* by default we apply fix-ups for broken USB encoding, etc. */ /* --------------------------------------------------------------- */ /* Struct & data for boolean processing */ @@ -197,7 +243,7 @@ static status_lkp_t status_info[] = { { "bypassauto", STATUS(BYPASSAUTO) }, { "bypassman", STATUS(BYPASSMAN) }, { "off", STATUS(OFF) }, - { "cal", STATUS(CAL) }, + { "cal", STATUS(CALIB) }, { "overheat", STATUS(OVERHEAT) }, { "commfault", STATUS(COMMFAULT) }, { "depleted", STATUS(DEPLETED) }, @@ -416,6 +462,12 @@ info_lkp_t on_off_info[] = { done with result! */ static const char *date_conversion_fun(double value) { + /* Per spec https://www.usb.org/sites/default/files/pdcv11.pdf (page 38): + * 4.2.6 Battery Settings -> ManufacturerDate + * The date the pack was manufactured in a packed integer. + * The date is packed in the following fashion: + * (year ā€“ 1980)*512 + month*32 + day. + */ static char buf[32]; long year, month, day; @@ -436,9 +488,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 @@ -513,16 +579,137 @@ info_lkp_t kelvin_celsius_conversion[] = { { 0, NULL, kelvin_celsius_conversion_fun, NULL } }; +static subdriver_t *match_function_subdriver_name(int fatal_mismatch) { + char *subdrv = getval("subdriver"); + subdriver_t *info = NULL; + + /* Pick up the subdriver name if set explicitly */ + if (subdrv) { + int i, flag_HAVE_LIBREGEX = 0; +#if (defined HAVE_LIBREGEX && HAVE_LIBREGEX) + int res; + size_t len; + regex_t *regex_ptr = NULL; + flag_HAVE_LIBREGEX = 1; +#endif + + upsdebugx(2, + "%s: matching a subdriver by explicit " + "name%s: '%s'...", + __func__, + flag_HAVE_LIBREGEX ? "/regex" : "", + subdrv); + + /* First try exact match for strings like "TrippLite HID 0.85" + * Not likely to hit (due to versions etc.), but worth a try :) + */ + for (i=0; subdriver_list[i] != NULL; i++) { + if (strcmp_null(subdrv, subdriver_list[i]->name) == 0) { + info = subdriver_list[i]; + goto found; + } + } + +#if (defined HAVE_LIBREGEX && HAVE_LIBREGEX) + /* Then try a case-insensitive regex like "tripplite.*" + * if so provided by caller */ + upsdebugx(2, "%s: retry matching by regex 'as is'", __func__); + res = compile_regex(®ex_ptr, subdrv, REG_ICASE | REG_EXTENDED); + if (res == 0 && regex_ptr != NULL) { + for (i=0; subdriver_list[i] != NULL; i++) { + res = match_regex(regex_ptr, subdriver_list[i]->name); + if (res == 1) { + free(regex_ptr); + info = subdriver_list[i]; + goto found; + } + } + } + + if (regex_ptr) { + free(regex_ptr); + regex_ptr = NULL; + } + + /* Then try a case-insensitive regex like "tripplite.*" + * with automatically added ".*" */ + len = strlen(subdrv); + if ( + (len < 3 || (subdrv[len-2] != '.' && subdrv[len-1] != '*')) + && len < (LARGEBUF-3) + ) { + char buf[LARGEBUF]; + upsdebugx(2, "%s: retry matching by regex with added '.*'", __func__); + snprintf(buf, sizeof(buf), "%s.*", subdrv); + res = compile_regex(®ex_ptr, buf, REG_ICASE | REG_EXTENDED); + if (res == 0 && regex_ptr != NULL) { + for (i=0; subdriver_list[i] != NULL; i++) { + res = match_regex(regex_ptr, subdriver_list[i]->name); + if (res == 1) { + free(regex_ptr); + info = subdriver_list[i]; + goto found; + } + } + } + + if (regex_ptr) { + free(regex_ptr); + regex_ptr = NULL; + } + } +#endif /* HAVE_LIBREGEX */ + + if (fatal_mismatch) { + fatalx(EXIT_FAILURE, + "Configuration requested subdriver '%s' but none matched", + subdrv); + } else { + upslogx(LOG_WARNING, + "Configuration requested subdriver '%s' but none matched; " + "will try USB matching by other fields", + subdrv); + } + } + + /* No match (and non-fatal mismatch mode), or no + * "subdriver" was specified in configuration */ + return NULL; + +found: + upsdebugx(2, "%s: found a match: %s", __func__, info->name); + if (!getval("vendorid") || !getval("productid")) { + if (fatal_mismatch) { + fatalx(EXIT_FAILURE, + "When specifying a subdriver, " + "'vendorid' and 'productid' " + "are mandatory."); + } else { + upslogx(LOG_WARNING, + "When specifying a subdriver, " + "'vendorid' and 'productid' " + "are highly recommended."); + } + } + + return info; +} + /*! * subdriver matcher: only useful for USB mode * as SHUT is only supported by MGE UPS SYSTEMS units */ -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) static int match_function_subdriver(HIDDevice_t *d, void *privdata) { - int i; + int i; NUT_UNUSED_VARIABLE(privdata); + if (match_function_subdriver_name(1)) { + /* This driver can handle this device. Guessing so... */ + return 1; + } + upsdebugx(2, "%s (non-SHUT mode): matching a device...", __func__); for (i=0; subdriver_list[i] != NULL; i++) { @@ -542,7 +729,7 @@ static HIDDeviceMatcher_t subdriver_matcher_struct = { NULL, NULL }; -#endif +#endif /* !SHUT_MODE => USB */ /* --------------------------------------------- * driver functions implementations @@ -557,26 +744,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 +781,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 +825,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)) { @@ -739,12 +934,25 @@ void upsdrv_shutdown(void) return; } - fatalx(EXIT_FAILURE, "Shutdown failed!"); + upslogx(LOG_ERR, "Shutdown failed!"); + set_exit_flag(-1); } void upsdrv_help(void) { - /* FIXME: to be completed */ + size_t i; + printf("\nAcceptable values for 'subdriver' via -x or ups.conf " + "in this driver (exact names here, case-insensitive " + "sub-strings may be used, as well as regular expressions): "); + + for (i = 0; subdriver_list[i] != NULL; i++) { + if (i>0) + printf(", "); + printf("\"%s\"", subdriver_list[i]->name); + } + printf("\n\n"); + + printf("Read The Fine Manual ('man 8 usbhid-ups')\n"); } void upsdrv_makevartable(void) @@ -775,7 +983,27 @@ void upsdrv_makevartable(void) addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); -#ifndef SHUT_MODE + addvar(VAR_FLAG, "onlinedischarge", + "Set to treat discharging while online as being offline/on-battery (DEPRECATED, use onlinedischarge_onbattery)"); + + addvar(VAR_FLAG, "onlinedischarge_onbattery", + "Set to treat discharging while online as being offline/on-battery"); + + addvar(VAR_FLAG, "onlinedischarge_calibration", + "Set to treat discharging while online as doing calibration"); + + addvar(VAR_VALUE, "onlinedischarge_log_throttle_sec", + "Set to throttle log messages about discharging while online (only so often)"); + + addvar(VAR_VALUE, "onlinedischarge_log_throttle_hovercharge", + "Set to throttle log messages about discharging while online (only if battery.charge is under this value)"); + + addvar(VAR_FLAG, "disable_fix_report_desc", + "Set to disable fix-ups for broken USB encoding, etc. which we apply by default on certain vendors/products"); + +#if !((defined SHUT_MODE) && SHUT_MODE) + addvar(VAR_VALUE, "subdriver", "Explicit USB HID subdriver selection"); + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); @@ -787,10 +1015,13 @@ void upsdrv_makevartable(void) "Don't use polling, only use interrupt pipe"); addvar(VAR_VALUE, "interruptsize", "Number of bytes to read from interrupt pipe"); -#else + addvar(VAR_VALUE, HU_VAR_WAITBEFORERECONNECT, + "Seconds to wait before trying to reconnect"); + +#else /* SHUT_MODE */ addvar(VAR_VALUE, "notification", - "Set notification type, (ignored, only for backward compatibility)"); -#endif + "Set notification type (ignored, only for backward compatibility)"); +#endif /* SHUT_MODE / USB */ } #define MAX_EVENT_NUM 32 @@ -814,7 +1045,12 @@ void upsdrv_updateinfo(void) return; } - upsdebugx(1, "Got to reconnect!\n"); + upsdebugx(1, "Got to reconnect!"); + if (use_interrupt_pipe == TRUE && interrupt_pipe_EIO_count > 0) { + upsdebugx(0, "\nReconnecting. If you saw \"nut_libusb_get_interrupt: Input/Output Error\" " + "or similar message in the log above, try setting \"pollonly\" flag in \"ups.conf\" " + "options section for this driver!\n"); + } if (!reconnect_ups()) { lastpoll = now; @@ -823,6 +1059,7 @@ void upsdrv_updateinfo(void) } hd = &curDevice; + interrupt_pipe_EIO_count = 0; if (hid_ups_walk(HU_WALKMODE_INIT) == FALSE) { hd = NULL; @@ -832,26 +1069,34 @@ void upsdrv_updateinfo(void) #ifdef DEBUG interval(); #endif + /* Get HID notifications on Interrupt pipe first */ if (use_interrupt_pipe == TRUE) { evtCount = HIDGetEvents(udev, event, MAX_EVENT_NUM); switch (evtCount) { - case ERROR_BUSY: /* Device or resource busy */ + case LIBUSB_ERROR_BUSY: /* Device or resource busy */ upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); goto fallthrough_reconnect; #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -EPERM: /* Operation not permitted */ #endif - case ERROR_NO_DEVICE: /* No such device */ - case ERROR_ACCESS: /* Permission denied */ - case ERROR_IO: /* I/O error */ -#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ - case -ENXIO: /* No such device or address */ + case LIBUSB_ERROR_NO_DEVICE: /* No such device */ + case LIBUSB_ERROR_ACCESS: /* Permission denied */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -ENXIO: /* No such device or address */ #endif - case ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NO_MEM: /* Insufficient memory */ fallthrough_reconnect: /* Uh oh, got to reconnect! */ + dstate_setinfo("driver.state", "reconnect.trying"); + hd = NULL; + return; + case LIBUSB_ERROR_IO: /* I/O error */ + /* Uh oh, got to reconnect, with a special suggestion! */ + dstate_setinfo("driver.state", "reconnect.trying"); + interrupt_pipe_EIO_count++; hd = NULL; return; default: @@ -880,10 +1125,10 @@ void upsdrv_updateinfo(void) /* Skip Input reports, if we don't use the Feature report */ found_data = FindObject_with_Path(pDesc, &(event[i]->Path), interrupt_only ? ITEM_INPUT:ITEM_FEATURE); - if(!found_data && !interrupt_only) { + if (!found_data && !interrupt_only) { found_data = FindObject_with_Path(pDesc, &(event[i]->Path), ITEM_INPUT); } - if(!found_data) { + if (!found_data) { upsdebugx(2, "Could not find event as either ITEM_INPUT or ITEM_FEATURE?"); continue; } @@ -899,7 +1144,7 @@ void upsdrv_updateinfo(void) upsdebugx(1, "took %.3f seconds handling interrupt reports...\n", interval()); #endif - /* clear status buffer before begining */ + /* clear status buffer before beginning */ status_init(); /* Do a full update (polling) every pollfreq @@ -968,12 +1213,7 @@ void upsdrv_initups(void) int ret; char *val; - upsdebugx(2, "Initializing an USB-connected UPS with library %s " \ - "(NUT subdriver name='%s' ver='%s')", - dstate_getinfo("driver.version.usb"), - comm_driver->name, comm_driver->version ); - -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE /*! * SHUT is a serial protocol, so it needs * only the device path @@ -981,11 +1221,18 @@ void upsdrv_initups(void) upsdebugx(1, "upsdrv_initups (SHUT)..."); subdriver_matcher = device_path; -#else - char *regex_array[7]; +#else /* !SHUT_MODE => USB */ + char *regex_array[USBMATCHER_REGEXP_ARRAY_LIMIT]; upsdebugx(1, "upsdrv_initups (non-SHUT)..."); + upsdebugx(2, "Initializing an USB-connected UPS with library %s " \ + "(NUT subdriver name='%s' ver='%s')", + dstate_getinfo("driver.version.usb"), + comm_driver->name, comm_driver->version ); + + warn_if_bad_usb_port_filename(device_path); + subdriver_matcher = &subdriver_matcher_struct; /* enforce use of the "vendorid" option if "explore" is given */ @@ -1006,6 +1253,13 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); regex_array[6] = getval("device"); +#if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + regex_array[7] = getval("busport"); +#else + if (getval("busport")) { + upslogx(LOG_WARNING, "\"busport\" is configured for the device, but is not actually handled by current build combination of NUT and libusb (ignored)"); + } +#endif ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch(ret) @@ -1030,11 +1284,11 @@ void upsdrv_initups(void) /* link the matchers */ subdriver_matcher->next = regex_matcher; -#endif /* SHUT_MODE */ +#endif /* SHUT_MODE / USB */ /* Search for the first supported UPS matching the regular expression (USB) or device_path (SHUT) */ - ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, &callback); + ret = comm_driver->open_dev(&udev, &curDevice, subdriver_matcher, &callback); if (ret < 1) fatalx(EXIT_FAILURE, "No matching HID UPS found"); @@ -1048,6 +1302,40 @@ void upsdrv_initups(void) if (testvar("interruptonly")) { interrupt_only = 1; } + + /* Activate Cyberpower/APC tweaks */ + if (testvar("onlinedischarge") || testvar("onlinedischarge_onbattery")) { + onlinedischarge_onbattery = 1; + } + + if (testvar("onlinedischarge_calibration")) { + onlinedischarge_calibration = 1; + } + + val = getval("onlinedischarge_log_throttle_sec"); + if (val) { + int ipv = atoi(val); + if (ipv == 0 && strcmp("0", val)) { + onlinedischarge_log_throttle_sec = 30; + upslogx(LOG_WARNING, + "Warning: invalid value for " + "onlinedischarge_log_throttle_sec: %s, " + "defaulting to %d", + val, onlinedischarge_log_throttle_sec); + } else { + if (ipv < 0) { + /* Has a specific meaning: user said be quiet */ + onlinedischarge_log_throttle_sec = -1; + } else { + onlinedischarge_log_throttle_sec = ipv; + } + } + } + + if (testvar("disable_fix_report_desc")) { + disable_fix_report_desc = 1; + } + val = getval("interruptsize"); if (val) { int ipv = atoi(val); @@ -1107,10 +1395,10 @@ void upsdrv_cleanup(void) { upsdebugx(1, "upsdrv_cleanup..."); - comm_driver->close(udev); + comm_driver->close_dev(udev); Free_ReportDesc(pDesc); free_report_buffer(reportbuf); -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) USBFreeExactMatcher(exact_matcher); USBFreeRegexMatcher(regex_matcher); @@ -1119,7 +1407,10 @@ void upsdrv_cleanup(void) free(curDevice.Serial); free(curDevice.Bus); free(curDevice.Device); -#endif +# if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + free(curDevice.BusPort); +# endif +#endif /* !SHUT_MODE => USB */ } /********************************************************************** @@ -1176,9 +1467,9 @@ static int callback( { int i; const char *mfr = NULL, *model = NULL, *serial = NULL; -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) int ret; -#endif +#endif /* !SHUT_MODE => USB */ upsdebugx(2, "Report Descriptor size = %" PRI_NUT_USB_CTRL_CHARBUFSIZE, rdlen); #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) @@ -1222,13 +1513,17 @@ static int callback( } /* select the subdriver for this device */ - for (i=0; subdriver_list[i] != NULL; i++) { - if (subdriver_list[i]->claim(hd)) { - break; + subdriver = match_function_subdriver_name(0); + if (!subdriver) { + for (i=0; subdriver_list[i] != NULL; i++) { + if (subdriver_list[i]->claim(hd)) { + break; + } } + + subdriver = subdriver_list[i]; } - subdriver = subdriver_list[i]; if (!subdriver) { upsdebugx(1, "Manufacturer not supported!"); return 0; @@ -1241,7 +1536,7 @@ static int callback( } HIDDumpTree(udev, arghd, subdriver->utab); -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) /* create a new matcher for later matching */ USBFreeExactMatcher(exact_matcher); ret = USBNewExactMatcher(&exact_matcher, hd); @@ -1251,7 +1546,7 @@ static int callback( } regex_matcher->next = exact_matcher; -#endif /* SHUT_MODE */ +#endif /* !SHUT_MODE => USB */ /* apply subdriver specific formatting */ mfr = subdriver->format_mfr(hd); @@ -1304,6 +1599,9 @@ int fix_report_desc(HIDDevice_t *arg_pDev, HIDDesc_t *arg_pDesc) { NUT_UNUSED_VARIABLE(arg_pDev); NUT_UNUSED_VARIABLE(arg_pDesc); +/* Implementations should honor the user's toggle: + * if (disable_fix_report_desc) return 0; + */ return 0; } @@ -1314,11 +1612,11 @@ static bool_t hid_ups_walk(walkmode_t mode) double value; int retcode; -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) /* extract the VendorId for further testing */ int vendorID = curDevice.VendorID; int productID = curDevice.ProductID; -#endif +#endif /* !SHUT_MODE => USB */ /* 3 modes: HU_WALKMODE_INIT, HU_WALKMODE_QUICK_UPDATE * and HU_WALKMODE_FULL_UPDATE */ @@ -1326,13 +1624,14 @@ static bool_t hid_ups_walk(walkmode_t mode) /* Device data walk ----------------------------- */ for (item = subdriver->hid2nut; item->info_type != NULL; item++) { -#ifdef SHUT_MODE +#if (defined SHUT_MODE) && SHUT_MODE /* Check if we are asked to stop (reactivity++) in SHUT mode. * In USB mode, looping through this takes well under a second, * so any effort to improve reactivity here is wasted. */ if (exit_flag != 0) return TRUE; -#endif +#endif /* SHUT_MODE */ + /* filter data according to mode */ switch (mode) { @@ -1426,34 +1725,43 @@ static bool_t hid_ups_walk(walkmode_t mode) # pragma GCC diagnostic pop #endif -#ifndef SHUT_MODE +#if !((defined SHUT_MODE) && SHUT_MODE) /* skip report 0x54 for Tripplite SU3000LCD2UHV due to firmware bug */ if ((vendorID == 0x09ae) && (productID == 0x1330)) { if (item->hiddata && (item->hiddata->ReportID == 0x54)) { continue; } } -#endif +#endif /* !SHUT_MODE => USB */ retcode = HIDGetDataValue(udev, item->hiddata, &value, poll_interval); switch (retcode) { - case ERROR_BUSY: /* Device or resource busy */ + case LIBUSB_ERROR_BUSY: /* Device or resource busy */ upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); goto fallthrough_reconnect; + #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -EPERM: /* Operation not permitted */ #endif - case ERROR_NO_DEVICE: /* No such device */ - case ERROR_ACCESS: /* Permission denied */ - case ERROR_IO: /* I/O error */ -#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ - case -ENXIO: /* No such device or address */ + case LIBUSB_ERROR_NO_DEVICE: /* No such device */ + case LIBUSB_ERROR_ACCESS: /* Permission denied */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -ENXIO: /* No such device or address */ #endif - case ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NOT_FOUND: /* No such file or directory */ + case LIBUSB_ERROR_NO_MEM: /* Insufficient memory */ fallthrough_reconnect: /* Uh oh, got to reconnect! */ + dstate_setinfo("driver.state", "reconnect.trying"); + hd = NULL; + return FALSE; + + case LIBUSB_ERROR_IO: /* I/O error */ + /* Uh oh, got to reconnect, with a special suggestion! */ + dstate_setinfo("driver.state", "reconnect.trying"); + interrupt_pipe_EIO_count++; hd = NULL; return FALSE; @@ -1463,14 +1771,19 @@ static bool_t hid_ups_walk(walkmode_t mode) case 0: continue; - case ERROR_TIMEOUT: /* Connection timed out */ - case ERROR_OVERFLOW: /* Value too large for defined data type */ -#if EPROTO && WITH_LIBUSB_0_1 + case LIBUSB_ERROR_TIMEOUT: /* Connection timed out */ +/* libusb win32 does not know EPROTO and EOVERFLOW, + * it only returns EIO for any IO errors */ +#ifndef WIN32 + case LIBUSB_ERROR_OVERFLOW: /* Value too large for defined data type */ +# if EPROTO && WITH_LIBUSB_0_1 case -EPROTO: /* Protocol error */ +# endif #endif - case ERROR_PIPE: /* Broken pipe */ + case LIBUSB_ERROR_PIPE: /* Broken pipe */ default: /* Don't know what happened, try again later... */ + upsdebugx(1, "HIDGetDataValue unknown retcode '%i'", retcode); continue; } @@ -1525,17 +1838,44 @@ static bool_t hid_ups_walk(walkmode_t mode) static int reconnect_ups(void) { int ret; + char *val; + int wait_before_reconnect = 0; - upsdebugx(4, "=================================================="); - upsdebugx(4, "= device has been disconnected, try to reconnect ="); - upsdebugx(4, "=================================================="); + dstate_setinfo("driver.state", "reconnect.trying"); + + /* Init time to wait before trying to reconnect (seconds) */ + val = getval(HU_VAR_WAITBEFORERECONNECT); + if (val) { + wait_before_reconnect = atoi(val); + } - ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, NULL); + /* Try to close the previous handle */ + if (udev == HID_DEV_HANDLE_CLOSED) { + upsdebugx(4, "Not closing comm_driver previous handle: already closed"); + } else { + upsdebugx(4, "Closing comm_driver previous handle"); + comm_driver->close_dev(udev); + udev = HID_DEV_HANDLE_CLOSED; + } + + upsdebugx(4, "==================================================================="); + if (wait_before_reconnect > 0 ) { + upsdebugx(4, " device has been disconnected, trying to reconnect in %i seconds", wait_before_reconnect); + sleep(wait_before_reconnect); + upsdebugx(4, " trying to reconnect"); + } else { + upsdebugx(4, " device has been disconnected, try to reconnect"); + } + upsdebugx(4, "==================================================================="); + upsdebugx(4, "Opening comm_driver ..."); + ret = comm_driver->open_dev(&udev, &curDevice, subdriver_matcher, NULL); + upsdebugx(4, "Opening comm_driver returns ret=%i", ret); if (ret > 0) { return 1; } + dstate_setinfo("driver.state", "quiet"); return 0; } @@ -1599,11 +1939,160 @@ static void ups_status_set(void) dstate_delinfo("input.transfer.reason"); } - if (ups_status & STATUS(ONLINE)) { - status_set("OL"); /* on line */ - } else { + /* Report calibration mode first, because it looks like OFF or OB + * for many implementations (literally, it is a temporary OB state + * managed by the UPS hardware to become OL later... if it guesses + * correctly when to do so), and may cause false alarms for us to + * raise FSD urgently. So we first let upsmon know it is just a drill. + */ + if (ups_status & STATUS(CALIB)) { + status_set("CAL"); /* calibration */ + } + + if ((!(ups_status & STATUS(DISCHRG))) && ( + onlinedischarge_log_throttle_timestamp != 0 + || onlinedischarge_log_throttle_charge != -1 + )) { + upsdebugx(1, + "%s: seems that UPS [%s] was in OL+DISCHRG state, " + "but no longer is now.", + __func__, upsname); + onlinedischarge_log_throttle_timestamp = 0; + onlinedischarge_log_throttle_charge = -1; + } + + if (!(ups_status & STATUS(ONLINE))) { status_set("OB"); /* on battery */ + } else if ((ups_status & STATUS(DISCHRG))) { + int do_logmsg = 0, current_charge = 0; + + /* if online but discharging */ + if (onlinedischarge_calibration) { + /* if we treat OL+DISCHRG as calibrating */ + status_set("CAL"); /* calibration */ + } + + if (onlinedischarge_onbattery) { + /* if we treat OL+DISCHRG as being offline */ + status_set("OB"); /* on battery */ + } + + if (!onlinedischarge_onbattery && !onlinedischarge_calibration) { + /* Some situation not managed by end-user's hints */ + if (!(ups_status & STATUS(CALIB))) { + /* if in OL+DISCHRG unknowingly, warn user, + * unless we throttle it this time - see below */ + do_logmsg = 1; + } + /* if we're presumably calibrating */ + status_set("OL"); /* on line */ + } + + if (do_logmsg) { + /* Any throttling to apply? */ + const char *s; + + /* First disable, then enable if OK for noise*/ + do_logmsg = 0; + + /* Time or not, did the charge change since last log? */ + if ((s = dstate_getinfo("battery.charge"))) { + /* NOTE: "0" may mean a conversion error: */ + current_charge = atoi(s); + if (current_charge > 0 + && current_charge != onlinedischarge_log_throttle_charge + ) { + /* Charge has changed, but is it + * now low enough to worry? */ + if (onlinedischarge_log_throttle_hovercharge + < onlinedischarge_log_throttle_charge + ) { + upsdebugx(3, "%s: current " + "battery.charge=%d is under " + "onlinedischarge_log_throttle_charge=%d", + __func__, current_charge, + onlinedischarge_log_throttle_charge); + do_logmsg = 1; + } else { + /* All seems OK, don't spam log + * unless running at a really + * high debug verbosity */ + upsdebugx(5, "%s: current " + "battery.charge=%d " + "is okay compared to " + "onlinedischarge_log_throttle_charge=%d", + __func__, current_charge, + onlinedischarge_log_throttle_charge); + } + } + } else { + /* Should we default the time throttle? */ + if (onlinedischarge_log_throttle_sec == -2) { + onlinedischarge_log_throttle_sec = 30; + /* Report once, so almost loud */ + upsdebugx(1, "%s: seems battery.charge " + "is not served by this device " + "or subdriver; defaulting " + "onlinedischarge_log_throttle_sec " + "to %d", + __func__, + onlinedischarge_log_throttle_sec); + } + } + + /* Do we track and honor time since last log? */ + if (onlinedischarge_log_throttle_timestamp > 0 + && onlinedischarge_log_throttle_sec >= 0 + ) { + time_t now; + time(&now); + + if ((now - onlinedischarge_log_throttle_timestamp) + >= onlinedischarge_log_throttle_sec + ) { + /* Enough time elapsed */ + do_logmsg = 1; + } + } + } + + if (do_logmsg) { + char msg_charge[LARGEBUF]; + msg_charge[0] = '\0'; + + /* Remember when we last logged this message */ + time(&onlinedischarge_log_throttle_timestamp); + + if (current_charge > 0 + && current_charge != onlinedischarge_log_throttle_charge + ) { + /* Charge has changed, report and remember this */ + if (onlinedischarge_log_throttle_charge < 0) { + /* First sequential message like this */ + snprintf(msg_charge, sizeof(msg_charge), + "Battery charge is currently %d. ", + current_charge); + } else { + snprintf(msg_charge, sizeof(msg_charge), + "Battery charge changed from %d to %d " + "since last such report. ", + onlinedischarge_log_throttle_charge, + current_charge); + } + onlinedischarge_log_throttle_charge = current_charge; + } + + upslogx(LOG_WARNING, "%s: seems that UPS [%s] is in OL+DISCHRG state now. %s" + "Is it calibrating (perhaps you want to set 'onlinedischarge_calibration' option)? " + "Note that some UPS models (e.g. CyberPower UT series) emit OL+DISCHRG when " + "in fact offline/on-battery (perhaps you want to set 'onlinedischarge_onbattery' option).", + __func__, upsname, msg_charge); + } + } else if ((ups_status & STATUS(ONLINE))) { + /* if simply online */ + status_set("OL"); } + if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { status_set("DISCHRG"); /* discharging */ @@ -1633,9 +2122,6 @@ static void ups_status_set(void) if (ups_status & STATUS(OFF)) { status_set("OFF"); /* ups is off */ } - if (ups_status & STATUS(CAL)) { - status_set("CAL"); /* calibration */ - } } /* find info element definition in info array diff --git a/drivers/usbhid-ups.h b/drivers/usbhid-ups.h index 5cdf7e680d..679ea7de7c 100644 --- a/drivers/usbhid-ups.h +++ b/drivers/usbhid-ups.h @@ -26,11 +26,19 @@ #ifndef USBHID_UPS_H #define USBHID_UPS_H +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + #include #include #include #include -#include "config.h" + #include "libhid.h" extern hid_dev_handle_t udev; @@ -134,7 +142,7 @@ typedef enum { BYPASSAUTO, /* on automatic bypass */ BYPASSMAN, /* on manual/service bypass */ OFF, /* ups is off */ - CAL, /* calibration */ + CALIB, /* calibration */ OVERHEAT, /* overheat; Belkin, TrippLite */ COMMFAULT, /* UPS fault; Belkin, TrippLite */ DEPLETED, /* battery depleted; Belkin */ @@ -217,5 +225,7 @@ int setvar(const char *varname, const char *val); void possibly_supported(const char *mfr, HIDDevice_t *hd); +/* by default we apply fix-ups for broken USB encoding, etc. */ +extern int disable_fix_report_desc; int fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *arg_pDesc); #endif /* USBHID_UPS_H */ diff --git a/drivers/victronups.c b/drivers/victronups.c index f4f6ad6045..49901f43bf 100644 --- a/drivers/victronups.c +++ b/drivers/victronups.c @@ -32,7 +32,7 @@ #include "serial.h" #define DRIVER_NAME "GE/IMV/Victron UPS driver" -#define DRIVER_VERSION "0.21" +#define DRIVER_VERSION "0.22" /* driver description structure */ upsdrv_info_t upsdrv_info = { diff --git a/drivers/xppc-mib.c b/drivers/xppc-mib.c index f386ba5495..95dbdc3b43 100644 --- a/drivers/xppc-mib.c +++ b/drivers/xppc-mib.c @@ -24,53 +24,25 @@ #include "xppc-mib.h" -#define XPPC_MIB_VERSION "0.3" +#define XPPC_MIB_VERSION "0.40" #define XPPC_SYSOID ".1.3.6.1.4.1.935" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t xpcc_onbatt_info[] = { - * { 1, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, - * { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ /* upsBaseBatteryStatus */ static info_lkp_t xpcc_onbatt_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 2, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryNormal */ - { 3, "LB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* batteryLow */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, ""), /* batteryNormal */ + info_lkp_default(3, "LB"), /* batteryLow */ + info_lkp_sentinel }; /* @@ -87,63 +59,23 @@ upsBaseOutputStatus OBJECT-TYPE onBuck(9) } */ static info_lkp_t xpcc_power_info[] = { - { 1, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* unknown */ - { 2, "OL" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onLine */ - { 3, "OB" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onBattery */ - { 4, "OL BOOST" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onBoost */ - { 5, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* sleeping */ - { 6, "BYPASS" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onBypass */ - { 7, "" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* rebooting */ - { 8, "OFF" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* standBy */ - { 9, "OL TRIM" -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - }, /* onBuck */ - { 0, NULL -#if WITH_SNMP_LKP_FUN - , NULL, NULL, NULL, NULL -#endif - } + info_lkp_default(1, ""), /* unknown */ + info_lkp_default(2, "OL"), /* onLine */ + info_lkp_default(3, "OB"), /* onBattery */ + info_lkp_default(4, "OL BOOST"), /* onBoost */ + info_lkp_default(5, "OFF"), /* sleeping */ + info_lkp_default(6, "BYPASS"), /* onBypass */ + info_lkp_default(7, ""), /* rebooting */ + info_lkp_default(8, "OFF"), /* standBy */ + info_lkp_default(9, "OL TRIM"), /* onBuck */ + info_lkp_sentinel }; /* XPPC Snmp2NUT lookup table */ static snmp_info_t xppc_mib[] = { /* Data format: - * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * snmp_info_default(info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar), * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo @@ -155,41 +87,47 @@ static snmp_info_t xppc_mib[] = { * oid2info: lookup table between OID and NUT values * * Example: - * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, - * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, xpcc_onbatt_info }, + * snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL), + * snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, xpcc_onbatt_info), * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t xpcc_onbatt_info[] = { - * { 1, "OB" }, - * { 2, "OL" }, - * { 0, NULL } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + /* standard MIB items */ + snmp_info_default("device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL), + snmp_info_default("device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL), + + snmp_info_default("ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL), /* upsBaseIdentModel.0 = STRING: "Intelligent" */ - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.1.1.1.0", "Generic Phoenixtec SNMP device", SU_FLAG_OK, NULL }, + snmp_info_default("ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.1.1.1.0", "Generic Phoenixtec SNMP device", SU_FLAG_OK, NULL), /* upsBaseBatteryStatus.0 = INTEGER: batteryNormal(2) */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.2.1.1.0", "", SU_STATUS_BATT | SU_TYPE_INT | SU_FLAG_OK, xpcc_onbatt_info }, + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.2.1.1.0", "", SU_STATUS_BATT | SU_TYPE_INT | SU_FLAG_OK, xpcc_onbatt_info), /* upsSmartBatteryCapacity.0 = INTEGER: 100 */ - { "battery.charge", 0, 1, ".1.3.6.1.4.1.935.1.1.1.2.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("battery.charge", 0, 1, ".1.3.6.1.4.1.935.1.1.1.2.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsSmartBatteryTemperature.0 = INTEGER: 260 */ - { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.2.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("ups.temperature", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.2.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsSmartInputLineVoltage.0 = INTEGER: 1998 */ - { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.3.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.3.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsBaseOutputStatus.0 = INTEGER: onLine(2) */ - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.4.1.1.0", "", SU_TYPE_INT | SU_STATUS_PWR, xpcc_power_info }, + snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.4.1.1.0", "", SU_TYPE_INT | SU_STATUS_PWR, xpcc_power_info), /* upsSmartOutputVoltage.0 = INTEGER: 2309 */ - { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("output.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsSmartOutputFrequency.0 = INTEGER: 500 */ - { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.2.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("output.frequency", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.2.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* upsSmartOutputLoad.0 = INTEGER: 7 */ - { "ups.load", 0, 1, ".1.3.6.1.4.1.935.1.1.1.4.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + snmp_info_default("ups.load", 0, 1, ".1.3.6.1.4.1.935.1.1.1.4.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL), /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } + snmp_info_sentinel }; mib2nut_info_t xppc = { "xppc", XPPC_MIB_VERSION, NULL, NULL, xppc_mib, XPPC_SYSOID, NULL }; diff --git a/include/Makefile.am b/include/Makefile.am index 2a76249bb9..f451507fbd 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,7 +1,26 @@ -dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ +# Network UPS Tools: include + +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + +dist_noinst_HEADERS = attribute.h common.h extstate.h proto.h \ state.h str.h timehead.h upsconf.h nut_float.h nut_stdint.h nut_platform.h \ - nutstream.hpp nutwriter.hpp nutipc.hpp nutconf.h \ - dmf.h alist.h dmfsnmp.h dmfcore.h + nutstream.hpp nutwriter.hpp nutipc.hpp nutconf.hpp \ + dmf.h alist.h dmfsnmp.h dmfcore.h \ + wincompat.h + +# Optionally deliverable as part of NUT public API: +if WITH_DEV +include_HEADERS = parseconf.h +else +dist_noinst_HEADERS += parseconf.h +endif # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h @@ -11,8 +30,8 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # magic to include Git version information in NUT version string # (for builds not made from the tagged commit in a Git workspace) -nut_version.h: FORCE - @GITREV="`git describe --tags 2>/dev/null | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,'`" || GITREV=""; \ +nut_version.h: @FORCE_NUT_VERSION@ + @GITREV="`git describe --tags --match 'v[0-9]*.[0-9]*.[0-9]' --exclude '*-signed' --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*' 2>/dev/null | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,'`" || GITREV=""; \ { echo '/* Autogenerated file. Do not change. */' ; \ echo '/* This file was generated by "make". */' ; \ if [ -z "$$GITREV" ]; then \ @@ -38,4 +57,4 @@ FORCE: # deps of a local target, we must clean it by ourselves before the # distribution dist-hook: - rm -f $(distdir)/nut_version.h + $(AM_V_at)rm -f $(distdir)/nut_version.h diff --git a/include/common.h b/include/common.h index 67deaff905..db1168b80d 100644 --- a/include/common.h +++ b/include/common.h @@ -20,7 +20,20 @@ #ifndef NUT_COMMON_H_SEEN #define NUT_COMMON_H_SEEN 1 -#include "config.h" /* must be the first header */ +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + +#ifdef WIN32 +# ifndef __POSIX_VISIBLE +/* for fcntl() and its flags in MSYS2 */ +# define __POSIX_VISIBLE 200809 +# endif +#endif /* Need this on AIX when using xlc to get alloca */ #ifdef _AIX @@ -31,7 +44,7 @@ #include #include #include -#include +#include /* suseconds_t among other things */ #include #ifdef HAVE_SYS_SIGNAL_H @@ -50,8 +63,27 @@ #include /* for strdup() and many others */ #endif +#ifndef WIN32 #include -#include +#else +#include +#include +#include +#endif + +#include /* useconds_t */ +#ifndef HAVE_USECONDS_T +# define useconds_t unsigned long int +#endif +#ifndef HAVE_SUSECONDS_T +/* Note: WIN32 may have this defined as just "long" which should + * hopefully be identical to the definition below, which we test + * in our configure script. See also struct timeval fields for a + * platform, if in doubt. + */ +# define suseconds_t signed long int +#endif + #include #include "timehead.h" @@ -59,12 +91,94 @@ #include "proto.h" #include "str.h" +#if (defined HAVE_LIBREGEX && HAVE_LIBREGEX) +# include +#endif + #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif +/* POSIX requires these, and most but not all systems use same + * magical numbers for the file descriptors... yep, not all do! + */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 /* standard input file descriptor */ +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 /* standard output file descriptor */ +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 /* standard error file descriptor */ +#endif + +/* porting stuff for WIN32, used by serial and SHUT codebases */ +#ifndef WIN32 +/* Just match three macro groups defined for WIN32 */ + +/* Type of what file open() and close() use, + * including pipes for driver-upsd communications: */ +# define TYPE_FD int +# define ERROR_FD (-1) +# define VALID_FD(a) (a>=0) + +/* Type of what NUT serial/SHUT methods juggle: */ +# define TYPE_FD_SER TYPE_FD +# define ERROR_FD_SER ERROR_FD +# define VALID_FD_SER(a) VALID_FD(a) + +/* Type of what socket() returns, mostly for networked code: */ +# define TYPE_FD_SOCK TYPE_FD +# define ERROR_FD_SOCK ERROR_FD +# define VALID_FD_SOCK(a) VALID_FD(a) + +#else /* WIN32 */ + +/* Separate definitions of TYPE_FD, ERROR_FD, VALID_FD() macros + * for usual file descriptors vs. types needed for serial port + * work or for networking sockets. + */ +# define TYPE_FD HANDLE +# define ERROR_FD (INVALID_HANDLE_VALUE) +# define VALID_FD(a) (a!=INVALID_HANDLE_VALUE) + +# ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +# endif + +# define TYPE_FD_SOCK SOCKET +# define ERROR_FD_SOCK INVALID_SOCKET +# define VALID_FD_SOCK(a) (a!=INVALID_SOCKET) + +typedef struct serial_handler_s { + HANDLE handle; + OVERLAPPED io_status; + int overlapped_armed; + + unsigned int vmin_; + unsigned int vtime_; + unsigned int r_binary; + unsigned int w_binary; +} serial_handler_t; + +# define TYPE_FD_SER serial_handler_t * +# define ERROR_FD_SER (NULL) +# define VALID_FD_SER(a) (a!=NULL) + +/* difftime returns erroneous value so we use this macro */ +# undef difftime +# define difftime(t1,t0) (double)(t1 - t0) +#endif /* WIN32 */ + +/* Two uppercase letters are more readable than one exclamation */ +#define INVALID_FD_SER(a) (!VALID_FD_SER(a)) +#define INVALID_FD_SOCK(a) (!VALID_FD_SOCK(a)) +#define INVALID_FD(a) (!VALID_FD(a)) + +#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(a[0])) + extern const char *UPS_VERSION; /* Use in code to notify the developers and quiesce the compiler that @@ -103,8 +217,16 @@ 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 */ +#ifndef WIN32 int sendsignal(const char *progname, int sig); +#else +int sendsignal(const char *progname, const char * sig); +#endif int snprintfcat(char *dst, size_t size, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); @@ -112,8 +234,23 @@ 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); +/* 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 + */ +#ifndef WIN32 /* open , get the pid, then send it */ int sendsignalfn(const char *pidfn, int sig); +#else +int sendsignalfn(const char *pidfn, const char * sig); +#endif const char *xbasename(const char *file); @@ -130,6 +267,26 @@ 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); + +/* Send (daemon) state-change notifications to an + * external service management framework such as systemd. + * State types below are initially loosely modeled after + * https://www.freedesktop.org/software/systemd/man/sd_notify.html + */ +typedef enum eupsnotify_state { + NOTIFY_STATE_READY = 1, + NOTIFY_STATE_READY_WITH_PID, + NOTIFY_STATE_RELOADING, + NOTIFY_STATE_STOPPING, + NOTIFY_STATE_STATUS, /* Send a text message per "fmt" below */ + NOTIFY_STATE_WATCHDOG /* Ping the framework that we are still alive */ +} upsnotify_state_t; +/* Note: here fmt may be null, then the STATUS message would not be sent/added */ +int upsnotify(upsnotify_state_t state, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); + /* 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, ...) @@ -180,6 +337,16 @@ void fatal_with_errno(int status, const char *fmt, ...) void fatalx(int status, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))) __attribute__((noreturn)); +/* Report CONFIG_FLAGS used for this build of NUT similarly to how + * upsdebugx(1, ...) would do it, but not limiting the string length + */ +void nut_report_config_flags(void); + +/* Report search paths used by ltdl-augmented code to discover and + * load shared binary object files at run-time (nut-scanner, DMF...) */ +void upsdebugx_report_search_paths(int level, int report_search_paths_builtin); +void nut_prepare_search_paths(void); + extern int nut_debug_level; extern int nut_log_level; @@ -188,8 +355,51 @@ void *xcalloc(size_t number, size_t size); void *xrealloc(void *ptr, size_t size); char *xstrdup(const char *string); +/**** REGEX helper methods ****/ + +/* helper function: version of strcmp that tolerates NULL + * pointers. NULL is considered to come before all other strings + * alphabetically. + */ +int strcmp_null(const char *s1, const char *s2); + +#if (defined HAVE_LIBREGEX && HAVE_LIBREGEX) +/* Helper function for compiling a regular expression. On success, + * store the compiled regular expression (or NULL) in *compiled, and + * return 0. On error with errno set, return -1. If the supplied + * regular expression is unparseable, return -2 (an error message can + * then be retrieved with regerror(3)). Note that *compiled will be an + * allocated value, and must be freed with regfree(), then free(), see + * regex(3). As a special case, if regex==NULL, then set + * *compiled=NULL (regular expression NULL is intended to match + * anything). + */ +int compile_regex(regex_t **compiled, const char *regex, const int cflags); + +/* Helper function for regular expression matching. Check if the + * entire string str (minus any initial and trailing whitespace) + * matches the compiled regular expression preg. Return 1 if it + * matches, 0 if not. Return -1 on error with errno set. Special + * cases: if preg==NULL, it matches everything (no contraint). If + * str==NULL, then it is treated as "". + */ +int match_regex(const regex_t *preg, const char *str); + +/* Helper function, similar to match_regex, but the argument being + * matched is a (hexadecimal) number, rather than a string. It is + * converted to a 4-digit hexadecimal string. */ +int match_regex_hex(const regex_t *preg, const int n); +#endif /* HAVE_LIBREGEX */ + +/* Note: different method signatures instead of TYPE_FD_SER due to "const" */ +#ifndef WIN32 ssize_t select_read(const int fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); ssize_t select_write(const int fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); +#else +ssize_t select_read(serial_handler_t *fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); +/* Note: currently not implemented de-facto for Win32 */ +ssize_t select_write(serial_handler_t * fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); +#endif char * get_libname(const char* base_libname); @@ -223,6 +433,56 @@ extern int optind; # define setegid(x) setresgid(-1,x,-1) /* Works for HP-UX 10.20 */ #endif +#ifdef WIN32 +/* FIXME : this might not be the optimal mapping between syslog and ReportEvent*/ +#define LOG_ERR EVENTLOG_ERROR_TYPE +#define LOG_INFO EVENTLOG_INFORMATION_TYPE +#define LOG_DEBUG EVENTLOG_WARNING_TYPE +#define LOG_NOTICE EVENTLOG_INFORMATION_TYPE +#define LOG_ALERT EVENTLOG_ERROR_TYPE +#define LOG_WARNING EVENTLOG_WARNING_TYPE +#define LOG_CRIT EVENTLOG_ERROR_TYPE +#define LOG_EMERG EVENTLOG_ERROR_TYPE + +#define closelog() + +#define SVCNAME TEXT("Network UPS Tools") +#define EVENTLOG_PIPE_NAME TEXT("nut") +#define UPSMON_PIPE_NAME TEXT("upsmon") +#define UPSD_PIPE_NAME TEXT("upsd") + +char * getfullpath(char * relative_path); +#define PATH_ETC "\\..\\etc" +#define PATH_VAR_RUN "\\..\\var\\run" +#define PATH_SHARE "\\..\\share" +#define PATH_BIN "\\..\\bin" +#define PATH_SBIN "\\..\\sbin" +#define PATH_LIB "\\..\\lib" +#endif /* WIN32*/ + +/* Return a difference of two timevals as a floating-point number */ +double difftimeval(struct timeval x, struct timeval y); +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC +double difftimespec(struct timespec x, struct timespec y); +#endif + +#ifndef HAVE_USLEEP +/* int __cdecl usleep(unsigned int useconds); */ +/* Note: if we'd need to define an useconds_t for obscure systems, + * it should be an int capable of string 0..1000000 value range, + * so probably unsigned long int */ +int __cdecl usleep(useconds_t useconds); +#endif /* HAVE_USLEEP */ + +#ifndef HAVE_STRNLEN +size_t strnlen(const char *s, size_t maxlen); +#endif + +/* Not all platforms support the flag; this method abstracts + * its use (or not) to simplify calls in the actual codebase */ +/* TODO: Extend for TYPE_FD and WIN32 eventually? */ +void set_close_on_exec(int fd); + #ifdef __cplusplus /* *INDENT-OFF* */ } diff --git a/include/dmfcore.h b/include/dmfcore.h index afbe624ab5..33987af6d6 100644 --- a/include/dmfcore.h +++ b/include/dmfcore.h @@ -9,6 +9,7 @@ * Copyright (C) 2016 Carlos Dominguez * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016 Jim Klimov + * Copyright (C) 2024 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 @@ -33,7 +34,7 @@ #if WITH_NEON # include #else -#error "LibNEON is required to build DMF" +# error "LibNEON is required to build DMF" #endif /* @@ -42,11 +43,11 @@ */ #ifndef PATH_MAX_SIZE -#ifdef PATH_MAX -#define PATH_MAX_SIZE PATH_MAX -#else -#define PATH_MAX_SIZE 1024 -#endif +# ifdef PATH_MAX +# define PATH_MAX_SIZE PATH_MAX +# else +# define PATH_MAX_SIZE 1024 +# endif #endif typedef enum { @@ -92,7 +93,7 @@ typedef struct { * the format-specific parsed_data structure, and pass to dmfcore_parse*(). */ dmfcore_parser_t* - dmfcore_parser_new(); + dmfcore_parser_new(void); /* Ensure at compile-time tat currently required fields are populated */ dmfcore_parser_t* diff --git a/include/dmfsnmp.h b/include/dmfsnmp.h index 7a3d0b244f..3c2a0f9c68 100644 --- a/include/dmfsnmp.h +++ b/include/dmfsnmp.h @@ -9,6 +9,7 @@ * Copyright (C) 2016 Carlos Dominguez * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016-2021 Jim Klimov + * Copyright (C) 2024 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 @@ -74,10 +75,11 @@ * places the discovered information into a new entry under the "list" tree, * or into dynamically grown arrays "mib2nut_info_t *mib2nut_table" (snmp-ups) * and "snmp_device_id_t *device_table" (for nut-scanner), as appropriate. - * References to these tables can be received with `get_mib2nut_table()` and - * `get_device_table()` methods. You can also `get_device_table_counter()` to - * look up the two tables' lengths (they were last `realloc()`ed to this size) - * including the zeroed-out sentinel last entries, but historically the NUT + * References to these tables can be received with `mibdmf_get_mib2nut_table()` + * and `mibdmf_get_device_table()` methods. You can also utilize + * `mibdmf_get_device_table_counter()` to look up the two tables' lengths + * (they were last `realloc()`ed to this size) including the zeroed-out + * "sentinels" in their last entries, but historically the NUT table-parsing * way consisted of looking through the tables until hitting the sentinel * entry and so determining its size or otherwise end of loop - and this * remains the official and reliable manner of length determination (not @@ -119,6 +121,10 @@ # endif #endif +#ifndef WITH_DMF_LUA +# define WITH_DMF_LUA 0 +#endif + #if WITH_DMF_LUA # ifndef WITH_DMF_FUNCTIONS # define WITH_DMF_FUNCTIONS 1 @@ -259,7 +265,7 @@ typedef struct { /* Initialize the data for dmf.c */ mibdmf_parser_t * - mibdmf_parser_new(); + mibdmf_parser_new(void); /* Properly destroy the object hierarchy and NULLify the caller's pointer */ void @@ -328,7 +334,7 @@ void print_mib2nut_memory_struct (mib2nut_info_t *self); -/* Helpers for string comparison (includng NULL consideration); */ +/* Helpers for string comparison (including NULL consideration); */ bool dmf_streq (const char* x, const char* y); @@ -373,7 +379,7 @@ info_lkp_t * , long (*nuf_s2l)(const char *nut_value) , long (*fun_s2l)(const char *snmp_value) , const char *(*nuf_vp2s)(void *raw_nut_value) -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ ); /* Destroy and NULLify the reference to alist_t, list of collections */ diff --git a/include/nut_float.h b/include/nut_float.h index 290edb4909..0b89974b62 100644 --- a/include/nut_float.h +++ b/include/nut_float.h @@ -22,7 +22,13 @@ #ifndef NUT_FLOAT_H_SEEN #define NUT_FLOAT_H_SEEN 1 -#include "config.h" +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif #if defined HAVE_FLOAT_H # include diff --git a/include/nut_stdint.h b/include/nut_stdint.h index 722038ad6f..9d64a061b4 100644 --- a/include/nut_stdint.h +++ b/include/nut_stdint.h @@ -21,7 +21,13 @@ #ifndef NUT_STDINT_H_SEEN #define NUT_STDINT_H_SEEN 1 -#include "config.h" +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 @@ -52,20 +58,129 @@ #endif /* Printing format for size_t and ssize_t */ -#ifndef PRIsize -# if defined(__MINGW32__) -# define PRIsize "u" +#ifndef PRIuSIZE +# ifdef PRIsize +# define PRIuSIZE PRIsize # else -# define PRIsize "zu" +# if defined(__MINGW32__) || defined (WIN32) +# define PRIuSIZE "llu" +# else +# define PRIuSIZE "zu" +# endif # endif #endif -#ifndef PRIssize -# if defined(__MINGW32__) -# define PRIssize "d" +#ifndef PRIxSIZE +# if defined(__MINGW32__) || defined (WIN32) +# define PRIxSIZE "llx" +# else +# define PRIxSIZE "zx" +# endif +#endif + +/* Note: Windows headers are known to define at least "d" values, + * so macros below revolve around that and not "i" directly */ +#ifndef PRIiSIZE +# ifdef PRIssize +# define PRIiSIZE PRIssize # else -# define PRIssize "zd" +# ifdef PRIdSIZE +# define PRIiSIZE PRIdSIZE +# else +# if defined(__MINGW32__) || defined (WIN32) +# define PRIiSIZE "lld" +# else +# define PRIiSIZE "zd" +# endif +# define PRIdSIZE PRIiSIZE +# endif +# endif +#else +# ifndef PRIdSIZE +# define PRIdSIZE PRIiSIZE # endif #endif /* format for size_t and ssize_t */ +/* Printing format for uintmax_t and intmax_t */ +#ifndef PRIuMAX +# if defined(__MINGW32__) || defined (WIN32) +# if (SIZEOF_VOID_P == 8) +# ifdef PRIu64 +# define PRIuMAX PRIu64 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIuMAX "llu" +# endif +# endif +# if (SIZEOF_VOID_P == 4) +# ifdef PRIu32 +# define PRIuMAX PRIu32 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIuMAX "lu" +# endif +# endif +# else +/* assume new enough compiler and standard... check "%j" support via configure? */ +# define PRIuMAX "ju" +# endif +#endif /* format for uintmax_t and intmax_t */ + +#ifndef PRIdMAX +# ifdef PRIiMAX +# define PRIdMAX PRIiMAX +# else +# if defined(__MINGW32__) || defined (WIN32) +# if (SIZEOF_VOID_P == 8) +# ifdef PRId64 +# define PRIdMAX PRId64 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIdMAX "lld" +# endif +# endif +# if (SIZEOF_VOID_P == 4) +# ifdef PRId32 +# define PRIdMAX PRId32 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIdMAX "ld" +# endif +# endif +# else +/* assume new enough compiler and standard... check "%j" support via configure? */ +# define PRIdMAX "jd" +# endif +# define PRIiMAX PRIdMAX +# endif +#else +# ifndef PRIiMAX +# define PRIiMAX PRIdMAX +# endif +#endif /* format for uintmax_t and intmax_t */ + +#ifndef PRIxMAX +# if defined(__MINGW32__) || defined (WIN32) +# if (SIZEOF_VOID_P == 8) +# ifdef PRIx64 +# define PRIxMAX PRIx64 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIxMAX "llx" +# endif +# endif +# if (SIZEOF_VOID_P == 4) +# ifdef PRIx32 +# define PRIxMAX PRIx32 +# else +/* assume new enough compiler and standard, and no Windows %I64 etc... check "%ll" support via configure? */ +# define PRIxMAX "lx" +# endif +# endif +# else +/* assume new enough compiler and standard... check "%j" support via configure? */ +# define PRIxMAX "jx" +# endif +#endif /* format for uintmax_t and intmax_t */ + #endif /* NUT_STDINT_H_SEEN */ diff --git a/include/nutconf.h b/include/nutconf.hpp similarity index 86% rename from include/nutconf.h rename to include/nutconf.hpp index b79b11c11b..086db17e23 100644 --- a/include/nutconf.h +++ b/include/nutconf.hpp @@ -1,21 +1,22 @@ -/* nutconf.h - Nut configuration file manipulation API +/* + nutconf.hpp - Nut configuration file manipulation API - Copyright (C) + Copyright (C) 2012 Emilien Kia - 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 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUTCONF_H_SEEN @@ -34,6 +35,11 @@ #include #include +/* See include/common.h for details behind this */ +#ifndef NUT_UNUSED_VARIABLE +#define NUT_UNUSED_VARIABLE(x) (void)(x) +#endif + #ifdef __cplusplus namespace nut @@ -60,6 +66,11 @@ class Settable Settable(const Settable& val):_value(val._value), _set(val._set){} Settable(const Type& val):_value(val), _set(true){} + /* Avoid implicit copy/move operator declarations */ + Settable(Settable&&) = default; + Settable& operator=(const Settable&) = default; + Settable& operator=(Settable&&) = default; + bool set()const{return _set;} void clear(){_set = false;} @@ -75,7 +86,7 @@ class Settable { if(!set() && !val.set()) return false; - else + else return (set() && val.set() && _value==val._value); } @@ -93,8 +104,8 @@ class Settable /** * \brief Serialisable interface * - * Classes that implement this iface provide way to serialise - * and deserialise instances to/from streams. + * Classes that implement this interface provide way to serialize + * and deserialize instances to/from streams. */ class Serialisable { @@ -106,7 +117,7 @@ class Serialisable public: /** - * \brief Deserialiser + * \brief Deserializer * * \param istream Input stream * @@ -116,7 +127,7 @@ class Serialisable virtual bool parseFrom(NutStream & istream) = 0; /** - * \brief Serialiser + * \brief Serializer * * \param ostream Output stream * @@ -126,7 +137,7 @@ class Serialisable virtual bool writeTo(NutStream & ostream) const = 0; /** Destructor */ - virtual ~Serialisable() {} + virtual ~Serialisable(); }; // end of class Serialisable @@ -141,13 +152,15 @@ class NutParser { OPTION_DEFAULT = 0, /** Colon character is considered as string character and not as specific token. - Usefull for IPv6 addresses */ + Useful for IPv6 addresses */ OPTION_IGNORE_COLON = 1 }; - NutParser(const char* buffer = NULL, unsigned int options = OPTION_DEFAULT); + NutParser(const char* buffer = nullptr, unsigned int options = OPTION_DEFAULT); NutParser(const std::string& buffer, unsigned int options = OPTION_DEFAULT); + virtual ~NutParser(); + /** Parsing configuration functions * \{ */ void setOptions(unsigned int options){_options = options;} @@ -170,15 +183,20 @@ class NutParser TOKEN_EQUAL, TOKEN_COLON, TOKEN_EOL - }type; + } type; std::string str; Token():type(TOKEN_NONE),str(){} - Token(TokenType type, const std::string& str=""):type(type),str(str){} - Token(TokenType type, char c):type(type),str(1, c){} + Token(TokenType type_arg, const std::string& str_arg=""):type(type_arg),str(str_arg){} + Token(TokenType type_arg, char c):type(type_arg),str(1, c){} Token(const Token& tok):type(tok.type),str(tok.str){} - bool is(TokenType type)const{return this->type==type;} + /* Avoid implicit copy/move operator declarations */ + Token(Token&&) = default; + Token& operator=(const Token&) = default; + Token& operator=(Token&&) = default; + + bool is(TokenType type_arg)const{return this->type==type_arg;} bool operator==(const Token& tok)const{return tok.type==type && tok.str==str;} @@ -196,46 +214,30 @@ class NutParser #ifndef UNITEST_MODE protected: #endif /* UNITEST_MODE */ - size_t getPos()const; - void setPos(size_t pos); - char charAt(size_t pos)const; + size_t getPos()const; + void setPos(size_t pos); + char charAt(size_t pos)const; - void pushPos(); - size_t popPos(); - void rewind(); + void pushPos(); + size_t popPos(); + void rewind(); - void back(); + void back(); - char get(); - char peek(); + char get(); + char peek(); private: unsigned int _options; - std::string _buffer; - size_t _pos; - std::vector _stack; + std::string _buffer; + size_t _pos; + std::vector _stack; }; typedef std::list ConfigParamList; -class NutConfigParser : public NutParser -{ -public: - virtual void parseConfig(); - -protected: - NutConfigParser(const char* buffer = NULL, unsigned int options = OPTION_DEFAULT); - NutConfigParser(const std::string& buffer, unsigned int options = OPTION_DEFAULT); - - virtual void onParseBegin()=0; - virtual void onParseComment(const std::string& comment)=0; - virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "")=0; - virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "")=0; - virtual void onParseEnd()=0; -}; - struct GenericConfigSectionEntry { std::string name; @@ -260,42 +262,63 @@ struct GenericConfigSection void clear(); }; +class BaseConfiguration +{ + friend class GenericConfigParser; +public: + virtual ~BaseConfiguration(); +protected: + virtual void setGenericConfigSection(const GenericConfigSection& section) = 0; +}; + +class NutConfigParser : public NutParser +{ +public: + virtual void parseConfig(); + + /* Declared for cleaner overrides; arg ignored in current class */ + virtual void parseConfig(BaseConfiguration* config); + +protected: + NutConfigParser(const char* buffer = nullptr, unsigned int options = OPTION_DEFAULT); + NutConfigParser(const std::string& buffer, unsigned int options = OPTION_DEFAULT); + + virtual void onParseBegin()=0; + virtual void onParseComment(const std::string& comment)=0; + virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "")=0; + virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "")=0; + virtual void onParseEnd()=0; +}; + class DefaultConfigParser : public NutConfigParser { public: - DefaultConfigParser(const char* buffer = NULL); - DefaultConfigParser(const std::string& buffer); + DefaultConfigParser(const char* buffer = nullptr); + DefaultConfigParser(const std::string& buffer); protected: virtual void onParseSection(const GenericConfigSection& section)=0; - virtual void onParseBegin(); - virtual void onParseComment(const std::string& comment); - virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = ""); - virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = ""); - virtual void onParseEnd(); + virtual void onParseBegin() override; + virtual void onParseComment(const std::string& comment) override; + virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "") override; + virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "") override; + virtual void onParseEnd() override; GenericConfigSection _section; ///> Currently parsed section }; -class BaseConfiguration -{ - friend class GenericConfigParser; -protected: - virtual void setGenericConfigSection(const GenericConfigSection& section) = 0; -}; - class GenericConfigParser : public DefaultConfigParser { public: - GenericConfigParser(const char* buffer = NULL); - GenericConfigParser(const std::string& buffer); + GenericConfigParser(const char* buffer = nullptr); + GenericConfigParser(const std::string& buffer); - virtual void parseConfig(BaseConfiguration* config); + virtual void parseConfig(BaseConfiguration* config) override; protected: - virtual void onParseSection(const GenericConfigSection& section); + virtual void onParseSection(const GenericConfigSection& section) override; BaseConfiguration* _config; }; @@ -309,14 +332,16 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable GenericConfiguration(){} + virtual ~GenericConfiguration() override; + void parseFromString(const std::string& str); /** Serialisable interface implementation \{ */ - bool parseFrom(NutStream & istream); - bool writeTo(NutStream & ostream) const; + bool parseFrom(NutStream & istream) override; + bool writeTo(NutStream & ostream) const override; /** \} */ - // FIXME Let me public or set it as protected with public accessors ? + // FIXME Let be public or set it as protected with public accessors ? SectionMap sections; const GenericConfigSection& operator[](const std::string& secname)const{return sections.find(secname)->second;} @@ -324,7 +349,7 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable protected: - virtual void setGenericConfigSection(const GenericConfigSection& section); + virtual void setGenericConfigSection(const GenericConfigSection& section) override; /** * \brief Configuration parameters getter @@ -555,11 +580,11 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable * \param min Minimum * \param max Maximum * - * \return \c number casted to target type + * \return \c number which was cast to target type */ template static T range_cast(long long int number, long long int min, long long int max) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::range_error) #endif { @@ -587,17 +612,17 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable * * \param str String * - * \retval true iff the string expresses a known true value + * \retval true IFF the string expresses a known true value * \retval false otherwise */ static bool str2bool(const std::string & str); /** - * \brief Convert boolean value to string + * \brief Convert Boolean value to string * * \param val Boolean value * - * \return \c vla as string + * \return \c val as string */ static const std::string & bool2str(bool val); @@ -608,53 +633,53 @@ class GenericConfiguration : public BaseConfiguration, public Serialisable class UpsmonConfiguration : public Serialisable { public: - UpsmonConfiguration(); - void parseFromString(const std::string& str); - - Settable runAsUser, shutdownCmd, notifyCmd, powerDownFlag; - Settable minSupplies, poolFreq, poolFreqAlert, hotSync; - Settable deadTime, rbWarnTime, noCommWarnTime, finalDelay; - - enum NotifyFlag { - NOTIFY_IGNORE = 0, - NOTIFY_SYSLOG = 1, - NOTIFY_WALL = 1 << 1, - NOTIFY_EXEC = 1 << 2 - }; - - enum NotifyType { - NOTIFY_ONLINE, - NOTIFY_ONBATT, - NOTIFY_LOWBATT, - NOTIFY_FSD, - NOTIFY_COMMOK, - NOTIFY_COMMBAD, - NOTIFY_SHUTDOWN, - NOTIFY_REPLBATT, - NOTIFY_NOCOMM, - NOTIFY_NOPARENT, - NOTIFY_TYPE_MAX - }; + UpsmonConfiguration(); + void parseFromString(const std::string& str); + + Settable runAsUser, shutdownCmd, notifyCmd, powerDownFlag; + Settable minSupplies, poolFreq, poolFreqAlert, hotSync; + Settable deadTime, rbWarnTime, noCommWarnTime, finalDelay; + + enum NotifyFlag { + NOTIFY_IGNORE = 0, + NOTIFY_SYSLOG = 1, + NOTIFY_WALL = 1 << 1, + NOTIFY_EXEC = 1 << 2 + }; + + enum NotifyType { + NOTIFY_ONLINE, + NOTIFY_ONBATT, + NOTIFY_LOWBATT, + NOTIFY_FSD, + NOTIFY_COMMOK, + NOTIFY_COMMBAD, + NOTIFY_SHUTDOWN, + NOTIFY_REPLBATT, + NOTIFY_NOCOMM, + NOTIFY_NOPARENT, + NOTIFY_TYPE_MAX + }; static NotifyFlag NotifyFlagFromString(const std::string& str); static NotifyType NotifyTypeFromString(const std::string& str); - Settable notifyFlags[NOTIFY_TYPE_MAX]; + Settable notifyFlags[NOTIFY_TYPE_MAX]; Settable notifyMessages[NOTIFY_TYPE_MAX]; - struct Monitor { - std::string upsname, hostname; - unsigned short port; - unsigned int powerValue; - std::string username, password; - bool isMaster; - }; + struct Monitor { + std::string upsname, hostname; + uint16_t port; + unsigned int powerValue; + std::string username, password; + bool isMaster; + }; - std::list monitors; + std::list monitors; /** Serialisable interface implementation \{ */ - bool parseFrom(NutStream & istream); - bool writeTo(NutStream & ostream) const; + bool parseFrom(NutStream & istream) override; + bool writeTo(NutStream & ostream) const override; /** \} */ }; // end of class UpsmonConfiguration @@ -664,28 +689,28 @@ class UpsmonConfiguration : public Serialisable class UpsmonConfigParser : public NutConfigParser { public: - UpsmonConfigParser(const char* buffer = NULL); - UpsmonConfigParser(const std::string& buffer); + UpsmonConfigParser(const char* buffer = nullptr); + UpsmonConfigParser(const std::string& buffer); - void parseUpsmonConfig(UpsmonConfiguration* config); + void parseUpsmonConfig(UpsmonConfiguration* config); protected: - virtual void onParseBegin(); - virtual void onParseComment(const std::string& comment); - virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = ""); - virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = ""); - virtual void onParseEnd(); + virtual void onParseBegin() override; + virtual void onParseComment(const std::string& comment) override; + virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "") override; + virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "") override; + virtual void onParseEnd() override; - UpsmonConfiguration* _config; + UpsmonConfiguration* _config; }; class NutConfiguration: public Serialisable { public: - NutConfiguration(); - void parseFromString(const std::string& str); + NutConfiguration(); + void parseFromString(const std::string& str); - enum NutMode { + enum NutMode { MODE_UNKNOWN = -1, MODE_NONE = 0, MODE_STANDALONE, @@ -693,15 +718,15 @@ class NutConfiguration: public Serialisable MODE_NETCLIENT, MODE_CONTROLLED, MODE_MANUAL, - }; + }; Settable mode; static NutMode NutModeFromString(const std::string& str); /** Serialisable interface implementation \{ */ - bool parseFrom(NutStream & istream); - bool writeTo(NutStream & ostream) const; + bool parseFrom(NutStream & istream) override; + bool writeTo(NutStream & ostream) const override; /** \} */ }; @@ -709,18 +734,18 @@ class NutConfiguration: public Serialisable class NutConfConfigParser : public NutConfigParser { public: - NutConfConfigParser(const char* buffer = NULL); - NutConfConfigParser(const std::string& buffer); + NutConfConfigParser(const char* buffer = nullptr); + NutConfConfigParser(const std::string& buffer); - void parseNutConfConfig(NutConfiguration* config); + void parseNutConfConfig(NutConfiguration* config); protected: - virtual void onParseBegin(); - virtual void onParseComment(const std::string& comment); - virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = ""); - virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = ""); - virtual void onParseEnd(); + virtual void onParseBegin() override; + virtual void onParseComment(const std::string& comment) override; + virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "") override; + virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "") override; + virtual void onParseEnd() override; - NutConfiguration* _config; + NutConfiguration* _config; }; @@ -728,15 +753,15 @@ class UpsdConfiguration : public Serialisable { public: UpsdConfiguration(); - void parseFromString(const std::string& str); + void parseFromString(const std::string& str); - Settable maxAge, maxConn; - Settable statePath, certFile; + Settable maxAge, maxConn; + Settable statePath, certFile; struct Listen { std::string address; - Settable port; + Settable port; inline bool operator==(const Listen& listen)const { @@ -746,8 +771,8 @@ class UpsdConfiguration : public Serialisable std::list listens; /** Serialisable interface implementation \{ */ - bool parseFrom(NutStream & istream); - bool writeTo(NutStream & ostream) const; + bool parseFrom(NutStream & istream) override; + bool writeTo(NutStream & ostream) const override; /** \} */ }; @@ -757,18 +782,18 @@ class UpsdConfiguration : public Serialisable class UpsdConfigParser : public NutConfigParser { public: - UpsdConfigParser(const char* buffer = NULL); - UpsdConfigParser(const std::string& buffer); + UpsdConfigParser(const char* buffer = nullptr); + UpsdConfigParser(const std::string& buffer); - void parseUpsdConfig(UpsdConfiguration* config); + void parseUpsdConfig(UpsdConfiguration* config); protected: - virtual void onParseBegin(); - virtual void onParseComment(const std::string& comment); - virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = ""); - virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = ""); - virtual void onParseEnd(); + virtual void onParseBegin() override; + virtual void onParseComment(const std::string& comment) override; + virtual void onParseSectionName(const std::string& sectionName, const std::string& comment = "") override; + virtual void onParseDirective(const std::string& directiveName, char sep = 0, const ConfigParamList& values = ConfigParamList(), const std::string& comment = "") override; + virtual void onParseEnd() override; - UpsdConfiguration* _config; + UpsdConfiguration* _config; }; @@ -1041,6 +1066,7 @@ class UpsConfiguration : public GenericConfiguration /** \} */ + virtual ~UpsConfiguration() override; }; // end of class UpsConfiguration @@ -1096,8 +1122,8 @@ class UpsdUsersConfiguration : public GenericConfiguration /** \} */ /** Serialisable interface implementation overload \{ */ - bool parseFrom(NutStream & istream); - bool writeTo(NutStream & ostream) const; + bool parseFrom(NutStream & istream) override; + bool writeTo(NutStream & ostream) const override; /** \} */ }; // end of class UpsdUsersConfiguration diff --git a/include/nutipc.hpp b/include/nutipc.hpp index 29537f90de..d668363b3c 100644 --- a/include/nutipc.hpp +++ b/include/nutipc.hpp @@ -1,22 +1,23 @@ -/* nutipc.hpp - NUT IPC +/* + nutipc.hpp - NUT IPC - Copyright (C) 2012 Eaton + Copyright (C) 2012 Eaton - Author: Vaclav Krpec + Author: Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUT_NUTIPC_HPP @@ -35,10 +36,22 @@ extern "C" { #include #include #include -#include -#include + +#ifndef WIN32 +# include +#else +# include +#endif + +#ifdef HAVE_PTHREAD +# include +#endif } +/* See include/common.h for details behind this */ +#ifndef NUT_UNUSED_VARIABLE +#define NUT_UNUSED_VARIABLE(x) (void)(x) +#endif namespace nut { @@ -54,10 +67,18 @@ class Process { public: /** Get current process ID */ - static pid_t getPID() throw(); + static pid_t getPID() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** Get parent process ID */ - static pid_t getPPID() throw(); + static pid_t getPPID() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * Process main routine functor prototype @@ -67,9 +88,15 @@ class Process { /** Formal constructor */ Main() {} + virtual ~Main(); public: + /* Avoid implicit copy/move operator declarations */ + Main(Main&&) = default; + Main& operator=(const Main&) = default; + //Main& operator=(Main&&) = default; + /** Routine */ virtual int operator () () = 0; @@ -99,7 +126,7 @@ class Process { * \param main Child process main routine */ Child(M main) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif ; @@ -118,7 +145,7 @@ class Process { * \return Child process exit code */ int wait() -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif ; @@ -192,11 +219,10 @@ class Process { /** Execution of the binary */ int operator () () -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif - ; - + override; }; // end of class Executor /** @@ -266,33 +292,55 @@ class Process { template Process::Child::Child(M main) -#if (defined __cplusplus) && (__cplusplus < 201700) -throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif : m_pid(0), m_exited(false), m_exit_code(0) { +#ifdef WIN32 + NUT_UNUSED_VARIABLE(main); + + /* FIXME: Implement (for NUT processes) via pipes? + * See e.g. upsdrvctl implementation. */ + std::stringstream e; + + e << "Can't fork: not implemented on this platform yet"; + + throw std::logic_error(e.str()); +#else m_pid = ::fork(); if (!m_pid) ::exit(main()); +#endif } template int Process::Child::wait() -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif { +#ifdef WIN32 + /* FIXME: Implement (for NUT processes) via pipes? + * See e.g. upsdrvctl implementation. */ + std::stringstream e; + + e << "Can't wait for PID " << m_pid << + ": not implemented on this platform yet"; + + throw std::logic_error(e.str()); +#else if (m_exited) return m_exit_code; pid_t wpid = ::waitpid(m_pid, &m_exit_code, 0); - if (-1 == m_pid) { + if (-1 == wpid) { int erno = errno; std::stringstream e; @@ -307,6 +355,7 @@ int Process::Child::wait() m_exit_code = WEXITSTATUS(m_exit_code); return m_exit_code; +#endif } @@ -320,6 +369,7 @@ class Signal { /** Signals */ typedef enum { +#ifndef WIN32 HUP = SIGHUP, /** Hangup */ INT = SIGINT, /** Interrupt */ QUIT = SIGQUIT, /** Quit */ @@ -338,15 +388,16 @@ class Signal { CHILD = SIGCHLD, /** Child stopped or terminated */ CONT = SIGCONT, /** Continue if stopped */ STOP = SIGSTOP, /** Stop process (unmaskable) */ - TSTOP = SIGTSTP, /** Stop typed at tty */ - TTYIN = SIGTTIN, /** tty input for background process */ - TTYOUT = SIGTTOU, /** tty output for background process */ + TSTOP = SIGTSTP, /** Stop typed at TTY */ + TTYIN = SIGTTIN, /** TTY input for background process */ + TTYOUT = SIGTTOU, /** TTY output for background process */ PROF = SIGPROF, /** Profiling timer expired */ SYS = SIGSYS, /** Bad argument to routine */ URG = SIGURG, /** Urgent condition on socket */ VTALRM = SIGVTALRM, /** Virtual alarm clock */ XCPU = SIGXCPU, /** CPU time limit exceeded */ XFSZ = SIGXFSZ, /** File size limit exceeded */ +#endif } enum_t; // end of typedef enum /** Signal list */ @@ -373,7 +424,7 @@ class Signal { virtual void operator () (enum_t signal) = 0; /** Formal destructor */ - virtual ~Handler() {} + virtual ~Handler(); }; // end of class Handler @@ -393,8 +444,8 @@ class Signal { /** Control commands */ typedef enum { - QUIT = 0, /**< Shutdown the thread */ - SIGNAL = 1, /**< Signal obtained */ + HT_QUIT = 0, /**< Shutdown the thread */ + HT_SIGNAL = 1, /**< Signal obtained */ } command_t; /** Communication pipe */ @@ -411,7 +462,8 @@ class Signal { * It passes all signals to signal handler instance of \ref H * Which must implement the \ref Signal::Handler interface. * The handler is instantiated in scope of the routine. - * It closes the communication pipe read end in reaction to \ref QUIT command. + * It closes the communication pipe read end in reaction to + * \ref HT_QUIT command. * * \param comm_pipe_read_end Communication pipe read end * @@ -425,12 +477,12 @@ class Signal { * The actual signal handler routine executed by the OS when the process * obtains signal to be handled. * The function simply writes the signal number to the signal handler - * thread communication pipe (as parameter of the \ref SIGNAL command). + * thread communication pipe (as parameter of the \ref HT_SIGNAL command). * The signal handling itself (whatever necessary) shall be done - * by the dedicated thread (to avoid possible re-entrancy issues). + * by the dedicated thread (to avoid possible re-entrance issues). * * Note that \c ::write is required to be an async-signal-safe function by - * POSIX.1-2004; also note that up to \c PIPE_BUF bytes are written atomicaly + * POSIX.1-2004; also note that up to \c PIPE_BUF bytes are written atomically * as required by IEEE Std 1003.1, 2004 Edition,\c PIPE_BUF being typically * hundreds of bytes at least (POSIX requires 512B, Linux provides whole 4KiB * page). @@ -460,7 +512,7 @@ class Signal { * \param siglist List of signals that shall be handled by the thread */ HandlerThread(const Signal::List & siglist) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error, std::runtime_error) #endif ; @@ -473,7 +525,7 @@ class Signal { * Closes the communication pipe write end. */ void quit() -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif ; @@ -484,7 +536,7 @@ class Signal { * Forces the signal handler thread termination (unless already down). */ ~HandlerThread() -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif ; @@ -504,7 +556,7 @@ class Signal { * \retval ESRCH if the process (group) identified doesn't exist */ static int send(enum_t signame, pid_t pid) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif ; @@ -527,14 +579,14 @@ class Signal { }; // end of class Signal -/** Initialisation of the communication pipes */ +/** Initialization of the communication pipes */ template int Signal::HandlerThread::s_comm_pipe[2] = { -1, -1 }; template void * Signal::HandlerThread::main(void * comm_pipe_read_end) { - int rfd = *(int *)comm_pipe_read_end; + int rfd = *(reinterpret_cast(comm_pipe_read_end)); H handler; @@ -547,7 +599,7 @@ void * Signal::HandlerThread::main(void * comm_pipe_read_end) { // Note that direct blocking read could be also used; // however, select allows timeout specification // which might come handy... - int fdno = ::select(FD_SETSIZE, &rfds, NULL, NULL, NULL); + int fdno = ::select(FD_SETSIZE, &rfds, nullptr, nullptr, nullptr); // TBD: Die or recover on error? if (-1 == fdno) { @@ -579,17 +631,17 @@ void * Signal::HandlerThread::main(void * comm_pipe_read_end) { assert(sizeof(word) == read_out); - command_t command = (command_t)word; + command_t command = static_cast(word); switch (command) { - case QUIT: + case HT_QUIT: // Close comm. pipe read end ::close(rfd); // Terminate thread - pthread_exit(NULL); + ::pthread_exit(nullptr); - case SIGNAL: + case HT_SIGNAL: // Read signal number read_out = ::read(rfd, &word, sizeof(word)); @@ -605,7 +657,7 @@ void * Signal::HandlerThread::main(void * comm_pipe_read_end) { assert(sizeof(word) == read_out); - Signal::enum_t sig = (Signal::enum_t)word; + Signal::enum_t sig = static_cast(word); // Handle signal handler(sig); @@ -620,15 +672,15 @@ void * Signal::HandlerThread::main(void * comm_pipe_read_end) { /** * \brief Write command to command pipe * - * \param fh Pipe writing end + * \param fh Pipe writing end (file handle) * \param cmd Command - * \param cmd_size Comand size + * \param cmd_size Command size * * \retval 0 on success * \retval errno on error */ int sigPipeWriteCmd(int fh, void * cmd, size_t cmd_size) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif ; @@ -637,24 +689,35 @@ int sigPipeWriteCmd(int fh, void * cmd, size_t cmd_size) template void Signal::HandlerThread::signalNotifier(int signal) { int sig[2] = { - (int)Signal::HandlerThread::SIGNAL, + static_cast(Signal::HandlerThread::HT_SIGNAL), }; sig[1] = signal; // TBD: The return value is silently ignored. // Either the write should've succeeded or the handling - // thread is already comming down... + // thread is already coming down... sigPipeWriteCmd(s_comm_pipe[1], sig, sizeof(sig)); } template Signal::HandlerThread::HandlerThread(const Signal::List & siglist) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error, std::runtime_error) #endif { +#ifdef WIN32 + NUT_UNUSED_VARIABLE(siglist); + + /* FIXME: Implement (for NUT processes) via pipes? + * See e.g. upsdrvctl implementation. */ + std::stringstream e; + + e << "Can't prepare signal handling thread: not implemented on this platform yet"; + + throw std::logic_error(e.str()); +#else // At most one instance per process allowed if (-1 != s_comm_pipe[1]) throw std::logic_error( @@ -670,7 +733,7 @@ Signal::HandlerThread::HandlerThread(const Signal::List & siglist) } // Start the thread - int status = ::pthread_create(&m_impl, NULL, &main, s_comm_pipe); + int status = ::pthread_create(&m_impl, nullptr, &main, s_comm_pipe); if (status) { std::stringstream e; @@ -687,14 +750,19 @@ Signal::HandlerThread::HandlerThread(const Signal::List & siglist) struct sigaction action; ::memset(&action, 0, sizeof(action)); +# ifdef sigemptyset + // no :: here because macro + sigemptyset(&action.sa_mask); +# else ::sigemptyset(&action.sa_mask); +# endif action.sa_handler = &signalNotifier; int signo = static_cast(*sig); // TBD: We might want to save the old handlers... - status = ::sigaction(signo, &action, NULL); + status = ::sigaction(signo, &action, nullptr); if (status) { std::stringstream e; @@ -705,20 +773,21 @@ Signal::HandlerThread::HandlerThread(const Signal::List & siglist) throw std::runtime_error(e.str()); } } +#endif } template void Signal::HandlerThread::quit() -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif { - static int quit = (int)Signal::HandlerThread::QUIT; + static int quit = static_cast(Signal::HandlerThread::HT_QUIT); sigPipeWriteCmd(s_comm_pipe[1], &quit, sizeof(quit)); - int status = ::pthread_join(m_impl, NULL); + int status = ::pthread_join(m_impl, nullptr); if (status) { std::stringstream e; @@ -741,8 +810,8 @@ void Signal::HandlerThread::quit() template -Signal::HandlerThread::~HandlerThread() -#if (defined __cplusplus) && (__cplusplus < 201700) +Signal::HandlerThread::~HandlerThread() +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif { diff --git a/include/nutstream.hpp b/include/nutstream.hpp index 0b1eb06d3c..13c9cf76ed 100644 --- a/include/nutstream.hpp +++ b/include/nutstream.hpp @@ -1,21 +1,22 @@ -/* nutstream.hpp - NUT stream +/* + nutstream.hpp - NUT stream - Copyright (C) + Copyright (C) 2012 Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef nut_nutstream_h @@ -33,9 +34,26 @@ extern "C" { #include #include #include -#include +#ifndef WIN32 +# include +#else +# if HAVE_WINSOCK2_H +# include +# endif +# if HAVE_WS2TCPIP_H +# include +# endif +/* Using a private implementation in nutstream.cpp + * similar to nutclient.cpp; do not call wincompat.h! + * FIXME: refactor to reuse the C++ adaptation? + */ +#endif } +/* See include/common.h for details behind this */ +#ifndef NUT_UNUSED_VARIABLE +#define NUT_UNUSED_VARIABLE(x) (void)(x) +#endif namespace nut { @@ -43,7 +61,7 @@ namespace nut /** * \brief Data stream interface * - * The intarface provides character-based streamed I/O. + * The interface provides character-based streamed I/O. */ class NutStream { public: @@ -52,7 +70,7 @@ class NutStream { typedef enum { NUTS_OK = 0, /** Operation successful */ NUTS_EOF, /** End of stream reached */ - NUTS_ERROR, /** Error ocurred */ + NUTS_ERROR, /** Error occurred */ } status_t; protected: @@ -126,7 +144,7 @@ class NutStream { * \brief Put data to the stream end * * The difference between \ref putString and this method - * is that it is able to serialise also data containing + * is that it is able to serialize also data containing * null characters. * * \param[in] data Data @@ -136,8 +154,42 @@ class NutStream { */ virtual status_t putData(const std::string & data) = 0; + /** + * \brief Flush output buffers for the stream being written + * + * \param[out] err_code Error code + * \param[out] err_msg Error message + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + virtual bool flush(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + = 0; + + /** + * \brief Flush output buffers for the stream being written + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + virtual bool flush() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + = 0; + + /** Flush output buffers for the file (or throw exception) */ + virtual void flushx() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) +#endif + = 0; + /** Formal destructor */ - virtual ~NutStream() {} + virtual ~NutStream(); }; // end of class NutStream @@ -165,13 +217,21 @@ class NutMemory: public NutStream { NutMemory(const std::string & str): m_impl(str), m_pos(0) {} // NutStream interface implementation - status_t getChar(char & ch); - void readChar(); - status_t getString(std::string & str); - status_t putChar(char ch); - status_t putString(const std::string & str); - status_t putData(const std::string & data); - + status_t getChar(char & ch) override; + void readChar() override; + status_t getString(std::string & str) override; + status_t putChar(char ch) override; + status_t putString(const std::string & str) override; + status_t putData(const std::string & data) override; + + // No-op for this class: + inline bool flush (int & err_code, std::string & err_msg) override { + NUT_UNUSED_VARIABLE(err_code); + NUT_UNUSED_VARIABLE(err_msg); + return true; + } + inline bool flush() override {return true;} + inline void flushx() override {} }; // end of class NutMemory @@ -223,14 +283,16 @@ class NutFile: public NutStream { bool m_current_ch_valid; /** - * \brief Generate temporary file name + * \brief Convert enum access_t mode values to strings + * for standard library methods * - * Throws an exception on file name generation error. + * Throws an exception on unexpected input (should never + * happen with proper enum usage). * - * \return Temporary file name + * \return Non-null "const char *" string */ - std::string tmpName() -#if (defined __cplusplus) && (__cplusplus < 201700) + const char *strAccessMode(access_t mode) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif ; @@ -243,7 +305,7 @@ class NutFile: public NutStream { */ NutFile(const std::string & name): m_name(name), - m_impl(NULL), + m_impl(nullptr), m_current_ch('\0'), m_current_ch_valid(false) {} @@ -254,6 +316,30 @@ class NutFile: public NutStream { */ NutFile(anonymous_t); + /** + * \brief Detected temporary path name getter + * + * \return Path name + */ + inline static const std::string & tmp_dir() { + return m_tmp_dir; + } + + /** + * \brief OS-dependent path separator character(s) + * + * \return Path separator + */ + inline static const std::string & path_sep() { + static std::string pathsep = +#ifdef WIN32 + "\\"; +#else + "/"; +#endif + return pathsep; + } + /** * \brief File name getter * @@ -269,18 +355,26 @@ class NutFile: public NutStream { * \param[out] err_code Error code * \param[out] err_msg Error message * - * \retval true iff the file exists + * \retval true IFF the file exists * \retval false otherwise */ - bool exists(int & err_code, std::string & err_msg) const throw(); + bool exists(int & err_code, std::string & err_msg) const +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Check whether file exists * - * \retval true iff the file exists + * \retval true IFF the file exists * \retval false otherwise */ - inline bool exists() const throw() { + inline bool exists() const +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -290,11 +384,11 @@ class NutFile: public NutStream { /** * \brief Check whether file exists (or throw exception) * - * \retval true iff the file exists + * \retval true IFF the file exists * \retval false otherwise */ inline bool existsx() const -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::runtime_error) #endif { @@ -323,7 +417,11 @@ class NutFile: public NutStream { * \retval true if open succeeded * \retval false if open failed */ - bool open(access_t mode, int & err_code, std::string & err_msg) throw(); + bool open(access_t mode, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Open file @@ -349,8 +447,8 @@ class NutFile: public NutStream { * \retval false if open failed */ inline void openx(access_t mode = READ_ONLY) -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -360,7 +458,58 @@ class NutFile: public NutStream { return; std::stringstream e; - e << "Failed to open file " << m_name << ": " << ec << ": " << em; + e << "Failed to open file " << m_name << ": " + << ec << ": " << em; + + throw std::runtime_error(e.str()); + } + + /** + * \brief Flush output buffers for the file + * + * \param[out] err_code Error code + * \param[out] err_msg Error message + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + bool flush(int & err_code, std::string & err_msg) override +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; + + /** + * \brief Flush output buffers for the file + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + inline bool flush() override +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { + int ec; + std::string em; + + return flush(ec, em); + } + + /** Flush output buffers for the file (or throw exception) */ + inline void flushx() override +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) +#endif + { + int ec; + std::string em; + + if (flush(ec, em)) + return; + + std::stringstream e; + e << "Failed to flush file " << m_name << ": " << ec << ": " << em; throw std::runtime_error(e.str()); } @@ -374,7 +523,11 @@ class NutFile: public NutStream { * \retval true if close succeeded * \retval false if close failed */ - bool close(int & err_code, std::string & err_msg) throw(); + bool close(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Close file @@ -382,7 +535,11 @@ class NutFile: public NutStream { * \retval true if close succeeded * \retval false if close failed */ - inline bool close() throw() { + inline bool close() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -391,8 +548,8 @@ class NutFile: public NutStream { /** Close file (or throw exception) */ inline void closex() -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -416,7 +573,11 @@ class NutFile: public NutStream { * \retval true if \c unlink succeeded * \retval false if \c unlink failed */ - bool remove(int & err_code, std::string & err_msg) throw(); + bool remove(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Remove file @@ -424,7 +585,11 @@ class NutFile: public NutStream { * \retval true if \c unlink succeeded * \retval false if \c unlink failed */ - inline bool remove() throw() { + inline bool remove() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -433,8 +598,8 @@ class NutFile: public NutStream { /** Remove file (or throw exception) */ inline void removex() -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -473,15 +638,44 @@ class NutFile: public NutStream { NutFile(access_t mode = READ_WRITE_CLEAR); // NutStream interface implementation - status_t getChar(char & ch) throw(); - void readChar() throw(); - status_t getString(std::string & str) throw(); - status_t putChar(char ch) throw(); - status_t putString(const std::string & str) throw(); - status_t putData(const std::string & data) throw(); + status_t getChar(char & ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + void readChar() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t getString(std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t putChar(char ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t putString(const std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t putData(const std::string & data) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; /** Destructor (closes the file) */ - ~NutFile(); + ~NutFile() override; private: @@ -493,7 +687,12 @@ class NutFile: public NutStream { * * \param orig Original file */ - NutFile(const NutFile & orig) { + NutFile(const NutFile & orig) +#if (defined __cplusplus) && (__cplusplus >= 201100) + __attribute__((noreturn)) +#endif + { + NUT_UNUSED_VARIABLE(orig); throw std::logic_error("NOT IMPLEMENTED"); } @@ -506,7 +705,12 @@ class NutFile: public NutStream { * * \param rval Right value */ - NutFile & operator = (const NutFile & rval) { + NutFile & operator = (const NutFile & rval) +#if (defined __cplusplus) && (__cplusplus >= 201100) + __attribute__((noreturn)) +#endif + { + NUT_UNUSED_VARIABLE(rval); throw std::logic_error("NOT IMPLEMENTED"); } @@ -522,12 +726,14 @@ class NutSocket: public NutStream { NUTSOCKD_UNIX = AF_UNIX, /** Unix */ NUTSOCKD_INETv4 = AF_INET, /** IPv4 */ NUTSOCKD_INETv6 = AF_INET6, /** IPv6 */ + NUTSOCKD_UNDEFINED = -1 } domain_t; /** Socket type */ typedef enum { NUTSOCKT_STREAM = SOCK_STREAM, /** Stream */ NUTSOCKT_DGRAM = SOCK_DGRAM, /** Datagram */ + NUTSOCKT_UNDEFINED = -1 } type_t; /** Socket protocol */ @@ -552,10 +758,10 @@ class NutSocket: public NutStream { * * Invalid address may be produced e.g. by failed DNS resolving. */ - Address(): m_sock_addr(NULL), m_length(0) {} + Address(): m_sock_addr(nullptr), m_length(0) {} /** - * \brief Initialise UNIX socket address + * \brief Initialize UNIX socket address * * \param addr UNIX socket address * \param path Pathname @@ -563,7 +769,7 @@ class NutSocket: public NutStream { static void init_unix(Address & addr, const std::string & path); /** - * \brief Initialise IPv4 address + * \brief Initialize IPv4 address * * \param addr IPv4 address * \param qb Byte quadruplet (MSB is at index 0) @@ -572,7 +778,7 @@ class NutSocket: public NutStream { static void init_ipv4(Address & addr, const std::vector & qb, uint16_t port); /** - * \brief Initialise IPv6 address + * \brief Initialize IPv6 address * * \param addr IPv6 address * \param hb 16 bytes of the address (MSB is at index 0) @@ -588,8 +794,12 @@ class NutSocket: public NutStream { * \retval true if the address is valid * \retval false otherwise */ - inline bool valid() throw() { - return NULL != m_sock_addr; + inline bool valid() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { + return nullptr != m_sock_addr; } /** @@ -605,8 +815,8 @@ class NutSocket: public NutStream { * \brief IPv4 address constructor * * \param msb Most significant byte - * \param msb2 2nd most significant byte - * \param lsb2 2nd least significant byte + * \param msb2 Second most significant byte + * \param lsb2 Second least significant byte * \param lsb Least significant byte * \param port Port number */ @@ -620,14 +830,14 @@ class NutSocket: public NutStream { * \brief IP address constructor * * Creates either IPv4 or IPv6 address (depending on - * how many bytes are provided bia the \c bytes argument). - * Throws an exception if the bytecount is invalid. + * how many bytes are provided via the \c bytes argument). + * Throws an exception if the byte-count is invalid. * * \param bytes 4 or 16 address bytes (MSB is at index 0) * \param port Port number */ Address(const std::vector & bytes, uint16_t port) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif ; @@ -646,7 +856,7 @@ class NutSocket: public NutStream { */ std::string str() const; - /** Stringifisation */ + /** Stringification */ inline operator std::string() const { return str(); } @@ -665,6 +875,8 @@ class NutSocket: public NutStream { /** Socket implementation */ int m_impl; + domain_t m_domain; + type_t m_type; /** Current character cache */ char m_current_ch; @@ -688,10 +900,10 @@ class NutSocket: public NutStream { const NutSocket & listen_sock, int & err_code, std::string & err_msg) -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::logic_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::logic_error) #endif - ; + ; /** * \brief Accept client connection on a listen socket @@ -705,7 +917,7 @@ class NutSocket: public NutStream { inline static bool accept( NutSocket & sock, const NutSocket & listen_sock) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error) #endif { @@ -724,7 +936,7 @@ class NutSocket: public NutStream { inline static void acceptx( NutSocket & sock, const NutSocket & listen_sock) -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::logic_error, std::runtime_error) #endif { @@ -745,10 +957,14 @@ class NutSocket: public NutStream { /** * \brief Socket valid check * - * \retval true if the socket is initialised + * \retval true if the socket is initialized * \retval false otherwise */ - inline bool valid() throw() { + inline bool valid() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { return -1 != m_impl; } @@ -776,6 +992,8 @@ class NutSocket: public NutStream { */ NutSocket(accept_flag_t, const NutSocket & listen_sock, int & err_code, std::string & err_msg): m_impl(-1), + m_domain(NUTSOCKD_UNDEFINED), + m_type(NUTSOCKT_UNDEFINED), m_current_ch('\0'), m_current_ch_valid(false) { @@ -791,6 +1009,8 @@ class NutSocket: public NutStream { */ NutSocket(accept_flag_t, const NutSocket & listen_sock): m_impl(-1), + m_domain(NUTSOCKD_UNDEFINED), + m_type(NUTSOCKT_UNDEFINED), m_current_ch('\0'), m_current_ch_valid(false) { @@ -807,7 +1027,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - bool bind(const Address & addr, int & err_code, std::string & err_msg) throw(); + bool bind(const Address & addr, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Bind socket to an address @@ -817,7 +1041,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - inline bool bind(const Address & addr) throw() { + inline bool bind(const Address & addr) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -830,8 +1058,8 @@ class NutSocket: public NutStream { * \param addr Socket address */ inline void bindx(const Address & addr) -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -858,7 +1086,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - bool listen(int backlog, int & err_code, std::string & err_msg) throw(); + bool listen(int backlog, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Listen on socket @@ -868,7 +1100,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - inline bool listen(int backlog) throw() { + inline bool listen(int backlog) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -881,8 +1117,8 @@ class NutSocket: public NutStream { * \param[in] backlog Limit of pending connections */ inline void listenx(int backlog) -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -907,7 +1143,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - bool connect(const Address & addr, int & err_code, std::string & err_msg) throw(); + bool connect(const Address & addr, int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Connect to a listen socket @@ -917,7 +1157,11 @@ class NutSocket: public NutStream { * \retval true on success * \retval false on error */ - inline bool connect(const Address & addr) throw() { + inline bool connect(const Address & addr) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -930,8 +1174,8 @@ class NutSocket: public NutStream { * \param[in] addr Remote address */ inline void connectx(const Address & addr) -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -946,6 +1190,56 @@ class NutSocket: public NutStream { throw std::runtime_error(e.str()); } + /** + * \brief Flush output data into socket + * + * \param[out] err_code Error code + * \param[out] err-msg Error message + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + bool flush(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + /** + * \brief Flush output data into socket + * + * \retval true if flush succeeded + * \retval false if flush failed + */ + inline bool flush() override +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { + int ec; + std::string em; + + return flush(ec, em); + } + + /** Flush output data into socket (or throw exception) */ + inline void flushx() override +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) +#endif + { + int ec; + std::string em; + + if (flush(ec, em)) + return; + + std::stringstream e; + e << "Failed to flush socket " << m_impl << ": " << ec << ": " << em; + + throw std::runtime_error(e.str()); + } + /** * \brief Close socket * @@ -955,7 +1249,11 @@ class NutSocket: public NutStream { * \retval true if close succeeded * \retval false if close failed */ - bool close(int & err_code, std::string & err_msg) throw(); + bool close(int & err_code, std::string & err_msg) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + ; /** * \brief Close socket @@ -963,7 +1261,11 @@ class NutSocket: public NutStream { * \retval true if close succeeded * \retval false if close failed */ - inline bool close() throw() { + inline bool close() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + { int ec; std::string em; @@ -972,8 +1274,8 @@ class NutSocket: public NutStream { /** Close socket (or throw exception) */ inline void closex() -#if (defined __cplusplus) && (__cplusplus < 201700) - throw(std::runtime_error) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw(std::runtime_error) #endif { int ec; @@ -989,17 +1291,43 @@ class NutSocket: public NutStream { } // NutStream interface implementation - status_t getChar(char & ch) throw(); - void readChar() throw(); - status_t getString(std::string & str) throw(); - status_t putChar(char ch) throw(); - status_t putString(const std::string & str) throw(); - inline status_t putData(const std::string & data) { + status_t getChar(char & ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + void readChar() +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t getString(std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t putChar(char ch) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + status_t putString(const std::string & str) +#if (defined __cplusplus) && (__cplusplus < 201100) + throw() +#endif + override; + + + inline status_t putData(const std::string & data) override { return putString(data); // no difference on sockets } /** Destructor (closes socket if necessary) */ - ~NutSocket(); + ~NutSocket() override; private: @@ -1011,7 +1339,12 @@ class NutSocket: public NutStream { * * \param orig Original file */ - NutSocket(const NutSocket & orig) { + NutSocket(const NutSocket & orig) +#if (defined __cplusplus) && (__cplusplus >= 201100) + __attribute__((noreturn)) +#endif + { + NUT_UNUSED_VARIABLE(orig); throw std::logic_error("NOT IMPLEMENTED"); } @@ -1025,6 +1358,7 @@ class NutSocket: public NutStream { * \param rval Right value */ NutSocket & operator = (const NutSocket & rval) { + NUT_UNUSED_VARIABLE(rval); throw std::logic_error("NOT IMPLEMENTED"); } diff --git a/include/nutwriter.hpp b/include/nutwriter.hpp index ccb50a1605..b04c44acbb 100644 --- a/include/nutwriter.hpp +++ b/include/nutwriter.hpp @@ -1,21 +1,22 @@ -/* nutwriter.hpp - NUT writer +/* + nutwriter.hpp - NUT writer - Copyright (C) + Copyright (C) 2012 Vaclav Krpec - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef nut_nutwriter_h @@ -24,7 +25,7 @@ #ifdef __cplusplus #include "nutstream.hpp" -#include "nutconf.h" +#include "nutconf.hpp" #include @@ -125,7 +126,7 @@ class NutWriter { /** - * \brief NUT consfiguration writer interface + * \brief NUT configuration writer interface */ class NutConfigWriter: public NutWriter { protected: @@ -166,15 +167,15 @@ class NutConfigWriter: public NutWriter { virtual status_t writeDirective(const std::string & str) = 0; /** Virtual destructor */ - virtual ~NutConfigWriter() {} + virtual ~NutConfigWriter(); }; // end of class NutConfigWriter /** - * \brief NUT section-less configuration writer specialisation + * \brief NUT section-less configuration writer specialization * - * Partial implementaton of \ref NutConfigWriter for section-less + * Partial implementation of \ref NutConfigWriter for section-less * configuration files. */ class SectionlessConfigWriter: public NutConfigWriter { @@ -190,19 +191,19 @@ class SectionlessConfigWriter: public NutConfigWriter { public: // Partial \ref NutConfigWriter interface implementation - status_t writeDirective(const std::string & str); - status_t writeComment(const std::string & str); + status_t writeDirective(const std::string & str) override; + status_t writeComment(const std::string & str) override; private: // Section name writing is forbidden (no sections) - status_t writeSectionName(const std::string & name); + status_t writeSectionName(const std::string & name) override; }; // end of class SectionlessConfigWriter /** - * \brief \c nut.conf configuration file serialiser + * \brief \c nut.conf configuration file serializer */ class NutConfConfigWriter: public SectionlessConfigWriter { public: @@ -215,7 +216,7 @@ class NutConfConfigWriter: public SectionlessConfigWriter { NutConfConfigWriter(NutStream & ostream): SectionlessConfigWriter(ostream) {} /** - * \brief Serialise configuration container + * \brief Serialize configuration container * * \param config Configuration * @@ -224,11 +225,13 @@ class NutConfConfigWriter: public SectionlessConfigWriter { */ status_t writeConfig(const NutConfiguration & config); + /* Ensure an out-of-line method to avoid "weak-vtables" warning */ + virtual ~NutConfConfigWriter() override; }; // end of class NutConfConfigWriter /** - * \brief \c upsmon.conf configuration file serialiser + * \brief \c upsmon.conf configuration file serializer */ class UpsmonConfigWriter: public SectionlessConfigWriter { public: @@ -241,7 +244,7 @@ class UpsmonConfigWriter: public SectionlessConfigWriter { UpsmonConfigWriter(NutStream & ostream): SectionlessConfigWriter(ostream) {} /** - * \brief Serialise configuration container + * \brief Serialize configuration container * * \param config Configuration * @@ -250,11 +253,13 @@ class UpsmonConfigWriter: public SectionlessConfigWriter { */ status_t writeConfig(const UpsmonConfiguration & config); + /* Ensure an out-of-line method to avoid "weak-vtables" warning */ + virtual ~UpsmonConfigWriter() override; }; // end of class UpsmonConfigWriter /** - * \brief \c upsd.conf configuration file serialiser + * \brief \c upsd.conf configuration file serializer */ class UpsdConfigWriter: public SectionlessConfigWriter { public: @@ -267,7 +272,7 @@ class UpsdConfigWriter: public SectionlessConfigWriter { UpsdConfigWriter(NutStream & ostream): SectionlessConfigWriter(ostream) {} /** - * \brief Serialise configuration container + * \brief Serialize configuration container * * \param config Configuration * @@ -276,6 +281,8 @@ class UpsdConfigWriter: public SectionlessConfigWriter { */ status_t writeConfig(const UpsdConfiguration & config); + /* Ensure an out-of-line method to avoid "weak-vtables" warning */ + virtual ~UpsdConfigWriter() override; }; // end of class UpsdConfigWriter @@ -299,14 +306,14 @@ class DefaultConfigWriter: public NutConfigWriter { public: // \ref NutConfigWriter interface implementation - status_t writeComment(const std::string & str); - status_t writeSectionName(const std::string & name); - status_t writeDirective(const std::string & str); + status_t writeComment(const std::string & str) override; + status_t writeSectionName(const std::string & name) override; + status_t writeDirective(const std::string & str) override; /** * \brief Write configuration section * - * Serialise generic configuration section. + * Serialize generic configuration section. * * \param section Configuration section * @@ -321,25 +328,25 @@ class DefaultConfigWriter: public NutConfigWriter { /** * \brief NUT generic configuration writer * - * Base configuration file serialiser. + * Base configuration file serializer. * Implements the \ref DefaultConfigWriter \c writeSection method - * and adds \c writeConfig routine for configuration file serialisation. + * and adds \c writeConfig routine for configuration file serialization. */ class GenericConfigWriter: public DefaultConfigWriter { protected: - /** Default indentation of the key/ value pair in section entry */ + /** Default indentation of the key/value pair in section entry */ static const std::string s_default_section_entry_indent; - /** Default separator of the key/ value pair in section entry */ + /** Default separator of the key/value pair in section entry */ static const std::string s_default_section_entry_separator; /** - * \brief Section entry serialiser + * \brief Section entry serializer * * \param entry Section entry * \param indent Indentation - * \param kv_sep Key/ value separator + * \param kv_sep Key/value separator * * \retval NUTW_OK on success * \retval NUTW_ERROR otherwise @@ -358,11 +365,11 @@ class GenericConfigWriter: public DefaultConfigWriter { */ GenericConfigWriter(NutStream & ostream): DefaultConfigWriter(ostream) {} - // Section serialiser implementation - status_t writeSection(const GenericConfigSection & section); + // Section serializer implementation + status_t writeSection(const GenericConfigSection & section) override; /** - * \brief Base configuration serialiser + * \brief Base configuration serializer * * \param config Base configuration * @@ -377,9 +384,9 @@ class GenericConfigWriter: public DefaultConfigWriter { /** * \brief NUT upsd.users configuration file writer * - * upsd.users configuration file serialiser. - * Overloads the generic section serialiser because of the upsmon section, - * which contains anomal upsmon (master|slave) directive. + * upsd.users configuration file serializer. + * Overloads the generic section serializer because of the upsmon section, + * which contains an anomalous upsmon (master|slave) directive. */ class UpsdUsersConfigWriter: public GenericConfigWriter { public: @@ -391,8 +398,8 @@ class UpsdUsersConfigWriter: public GenericConfigWriter { */ UpsdUsersConfigWriter(NutStream & ostream): GenericConfigWriter(ostream) {} - // Section serialiser overload - status_t writeSection(const GenericConfigSection & section); + // Section serializer overload + status_t writeSection(const GenericConfigSection & section) override; }; // end of class UpsdUsersConfigWriter diff --git a/include/parseconf.h b/include/parseconf.h index bb7584b5d5..828a1e09b3 100644 --- a/include/parseconf.h +++ b/include/parseconf.h @@ -22,6 +22,19 @@ #include +/* 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" { diff --git a/include/proto.h b/include/proto.h index c78375573a..68c251a118 100644 --- a/include/proto.h +++ b/include/proto.h @@ -24,6 +24,14 @@ #ifndef NUT_PROTO_H_SEEN #define NUT_PROTO_H_SEEN 1 +/* "config.h" is generated by autotools and lacks a header guard, so + * we use an unambiguously named macro we know we must have, as one. + * It must be the first header: be sure to know all about system config. + */ +#ifndef NUT_NETVERSION +# include "config.h" +#endif + #include "attribute.h" #ifdef __cplusplus @@ -62,6 +70,12 @@ extern "C" { # endif #endif +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + #if !defined (HAVE_SNPRINTF) || defined (__Lynx__) int snprintf (char *str, size_t count, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); diff --git a/include/state.h b/include/state.h index 97249a2726..9857e4d584 100644 --- a/include/state.h +++ b/include/state.h @@ -32,6 +32,14 @@ extern "C" { #define ST_SOCK_BUF_LEN 512 +#include "timehead.h" + +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC +typedef struct timespec st_tree_timespec_t; +#else +typedef struct timeval st_tree_timespec_t; +#endif + typedef struct st_tree_s { char *var; char *val; /* points to raw or safe */ @@ -45,6 +53,12 @@ typedef struct st_tree_s { int flags; long aux; + /* When was this entry last written (meaning that + * val/raw/safe, flags, aux, enum or range value + * was added, changed or deleted)? + */ + st_tree_timespec_t lastset; + struct enum_s *enum_list; struct range_s *range_list; @@ -52,6 +66,8 @@ typedef struct st_tree_s { struct st_tree_s *right; } st_tree_t; +int state_get_timestamp(st_tree_timespec_t *now); +int st_tree_node_compare_timestamp(const st_tree_t *node, const st_tree_timespec_t *cutoff); int state_setinfo(st_tree_t **nptr, const char *var, const char *val); int state_addenum(st_tree_t *root, const char *var, const char *val); int state_addrange(st_tree_t *root, const char *var, const int min, const int max); @@ -67,6 +83,7 @@ void state_infofree(st_tree_t *node); void state_cmdfree(cmdlist_t *list); int state_delcmd(cmdlist_t **list, const char *cmd); int state_delinfo(st_tree_t **root, const char *var); +int state_delinfo_olderthan(st_tree_t **root, const char *var, const st_tree_timespec_t *cutoff); int state_delenum(st_tree_t *root, const char *var, const char *val); int state_delrange(st_tree_t *root, const char *var, const int min, const int max); st_tree_t *state_tree_find(st_tree_t *node, const char *var); diff --git a/include/str.h b/include/str.h index 89a030ddfb..12272d67c6 100644 --- a/include/str.h +++ b/include/str.h @@ -31,9 +31,10 @@ extern "C" { /* Some compilers and/or C libraries do not handle printf("%s", NULL) correctly */ #ifndef NUT_STRARG -# ifdef HAVE_PRINTF_STRING_NULL +# if (defined REQUIRE_NUT_STRARG) && (REQUIRE_NUT_STRARG == 0) # define NUT_STRARG(x) x # else +/* Is required, or not defined => err on safe side */ # define NUT_STRARG(x) (x?x:"(null)") # endif #endif @@ -129,6 +130,17 @@ int str_to_ulong_strict(const char *string, unsigned long *number, const int bas int str_to_double(const char *string, double *number, const int base); int str_to_double_strict(const char *string, double *number, const int base); +/* Return non-zero if string s ends exactly with suff + * Note: s=NULL always fails the test; otherwise suff=NULL always matches + */ +int str_ends_with(const char *s, const char *suff); + +#ifndef HAVE_STRSEP +/* Makefile should add the implem to libcommon(client).la */ +char *strsep(char **stringp, const char *delim); +#define HAVE_STRSEP 1 +#endif + /* Concatenates "count" strings into a dynamically allocated object which * the caller can use and must free() later on */ char * str_concat(size_t count, ...); diff --git a/include/timehead.h b/include/timehead.h index aabcf2ad97..ed36d1b168 100644 --- a/include/timehead.h +++ b/include/timehead.h @@ -22,6 +22,12 @@ #ifndef NUT_TIMEHEAD_H_SEEN #define NUT_TIMEHEAD_H_SEEN 1 +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + #ifdef TIME_WITH_SYS_TIME # include # include @@ -33,4 +39,52 @@ # endif #endif +#ifndef HAVE_STRPTIME +/* Use fallback implementation provided in e.g. libcommon(client).la: */ +char * strptime(const char *buf, const char *fmt, struct tm *tm); +#endif + +#ifndef HAVE_LOCALTIME_R +# ifdef HAVE_LOCALTIME_S +/* A bit of a silly trick, but should help on MSYS2 builds it seems */ +# define localtime_r(timer, buf) localtime_s(timer, buf) +# else +# include /* memcpy */ +static inline struct tm *localtime_r( const time_t *timer, struct tm *buf ) { + /* Note: not thread-safe per se! */ + struct tm *tmp = localtime (timer); + memcpy(buf, tmp, sizeof(struct tm)); + return buf; +} +# endif +#endif + +#ifndef HAVE_GMTIME_R +# ifdef HAVE_GMTIME_S +# define gmtime_r(timer, buf) gmtime_s(timer, buf) +# else +# include /* memcpy */ +static inline struct tm *gmtime_r( const time_t *timer, struct tm *buf ) { + /* Note: not thread-safe per se! */ + struct tm *tmp = gmtime (timer); + memcpy(buf, tmp, sizeof(struct tm)); + return buf; +} +# endif +#endif + +#ifndef HAVE_TIMEGM +# ifdef HAVE__MKGMTIME +# define timegm(tm) _mkgmtime(tm) +# else +# error "No fallback implementation for timegm" +# endif +#endif + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif /* NUT_TIMEHEAD_H_SEEN */ diff --git a/include/upsconf.h b/include/upsconf.h index 4ae1b271c5..c90ed95284 100644 --- a/include/upsconf.h +++ b/include/upsconf.h @@ -32,8 +32,12 @@ extern "C" { /* callback function from read_upsconf */ void do_upsconf_args(char *upsname, char *var, char *val); -/* open the ups.conf, parse it, and call back do_upsconf_args() */ -void read_upsconf(void); +/* open the ups.conf, parse it, and call back do_upsconf_args() + * returns -1 (or aborts the program) in case of errors; + * returns 1 if processing finished successfully + * See also reload_flag support in main.c for live-reload feature + */ +int read_upsconf(int fatal_errors); #ifdef __cplusplus /* *INDENT-OFF* */ diff --git a/include/wincompat.h b/include/wincompat.h new file mode 100644 index 0000000000..c5178d05c9 --- /dev/null +++ b/include/wincompat.h @@ -0,0 +1,360 @@ +#ifndef NUT_WINCOMPAT_H +#define NUT_WINCOMPAT_H 1 + +/* + Copyright (C) 2001 Andrew Delpha (delpha@computer.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, WRITE to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + This header is provided to map Windows system calls to be compatible + with the NUT code. +*/ + +#include "common.h" +#include + +/* This value is defined in the error.h file of the libusb-win32 sources + * FIXME: Should only be relevant for builds WITH_LIBUSB_0_1 - #ifdef it so? + * Conflicts with e.g. /msys64/mingw64/include/errno.h which defines it as 138 + */ +#ifndef ETIMEDOUT +# define ETIMEDOUT 116 +#endif + +#define random() rand() +typedef const char * uid_t; +struct passwd { + const char *pw_name; + int pw_uid; /* Fake value, alwaus set to 0 */ +}; + +uid_t getuid(void); +struct passwd *getpwuid(uid_t uid); +char *getpass( const char *prompt); + +#define system(a) win_system(a) +#define sleep(n) Sleep(1000 * n) +char * strtok_r(char *str, const char *delim, char **saveptr); +char * filter_path(const char * source); + +/* Network compatibility */ + +/* This is conditional because read/write are generic in unix, and this will make them network specific */ +#ifdef W32_NETWORK_CALL_OVERRIDE +#define close(h) sktclose(h) +#define read(h,b,s) sktread(h,b,s) +#define write(h,b,s) sktwrite( h ,(char *) b , s ) +#define connect(h,n,l) sktconnect(h,n,l) +#endif + +#ifndef strcasecmp +#define strcasecmp(a,b) _stricmp(a,b) +#endif +#ifndef snprintf +#define snprintf _snprintf +#endif +#ifndef vsnprintf +#define vsnprintf _vsnprintf +#endif + +int sktconnect(int fh, struct sockaddr * name, int len); +int sktread(int fh, char *buf, int size); +int sktwrite(int fh, char *buf, int size); +int sktclose(int fh); + +#if ! HAVE_INET_NTOP +/* NOTE: This fallback can get used on systems that do have inet_ntop() + * but their antivirus precluded the configure-script test program linking. + * A symptom of that would be: + * warning: 'inet_ntop' redeclared without dllimport attribute: + * previous dllimport ignored [-Wattributes] + * ...and adding the attributes would cause a conflict of private fallback + * and system DLL symbols. So the lesser of two evils, we use fallback. + * People who see the warning should however fix their build environment + * and use the native OS-provided implementation ;) + */ +# if (0) +/* Some old winapi? or just sloppy original commits? + * Why is this here at all if there's something per ws2tcpip.h - + * maybe should be configure-detected? + */ +const char* inet_ntop(int af, const void* src, char* dst, int cnt); +# else +const char* inet_ntop(int af, const void* src, char* dst, size_t /* socklen_t */ cnt); +# endif +#endif + +#if ! HAVE_INET_PTON +int inet_pton(int af, const char *src, void *dst); +#endif + +/* from the MSDN getaddrinfo documentation : */ +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +/* not from MS docs : */ +#define EAI_SYSTEM WSANO_RECOVERY +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +/* "system" function */ +int win_system(const char * command); + +/* syslog compatibility */ + +void syslog(int priority, const char *fmt, ...); + +extern const char * EventLogName; + +/* Signal emulation via named pipe */ +typedef struct pipe_conn_s { + HANDLE handle; + OVERLAPPED overlapped; + char buf[LARGEBUF]; + struct pipe_conn_s *prev; + struct pipe_conn_s *next; +} pipe_conn_t; + +extern pipe_conn_t *pipe_connhead; +extern OVERLAPPED pipe_connection_overlapped; +void pipe_create(const char * pipe_name); +void pipe_connect(); +void pipe_disconnect(pipe_conn_t *conn); +int pipe_ready(pipe_conn_t *conn); +int send_to_named_pipe(const char * pipe_name, const char * data); + +#define COMMAND_FSD "COMMAND_FSD" +#define COMMAND_STOP "COMMAND_STOP" +#define COMMAND_RELOAD "COMMAND_RELOAD" + +/* serial function compatibility */ + +int w32_setcomm ( serial_handler_t * h, int * flags ); +int w32_getcomm ( serial_handler_t * h, int * flags ); +int tcsendbreak (serial_handler_t * sh, int duration); + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +serial_handler_t * w32_serial_open (const char *name, int flags); +int w32_serial_close (serial_handler_t * sh); +int w32_serial_write (serial_handler_t * sh, const void *ptr, size_t len); +int w32_serial_read (serial_handler_t * sh, void *ptr, size_t ulen, DWORD timeout); +int tcgetattr (serial_handler_t * sh, struct termios *t); +int tcsetattr (serial_handler_t * sh, int action, const struct termios *t); +int tcflush (serial_handler_t * sh, int queue); +#define HAVE_CFSETISPEED +void cfsetispeed(struct termios * t, speed_t speed); +void cfsetospeed(struct termios * t, speed_t speed); +speed_t cfgetispeed(const struct termios *t); +speed_t cfgetospeed(const struct termios *t); + +#define _POSIX_VDISABLE '\0' + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define BOTHER 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#define TIOCM_DTR 0x0001 +#define TIOCM_RTS 0x0002 +#define TIOCM_ST 0x0004 +#define TIOCM_CTS MS_CTS_ON /* 0x0010*/ +#define TIOCM_DSR MS_DSR_ON /* 0x0020*/ +#define TIOCM_RNG MS_RING_ON /*0x0040*/ +#define TIOCM_RI TIOCM_RNG /* at least that's the definition in Linux */ +#define TIOCM_CD MS_RLSD_ON /*0x0080*/ + +#if !defined(PATH_MAX) && defined(MAX_PATH) +/* PATH_MAX is the POSIX equivalent for Microsoft's MAX_PATH + * both should be defined in (mingw) limits.h + */ +# define PATH_MAX MAX_PATH +#endif + +#endif /* NUT_WINCOMPAT_H */ diff --git a/indent.sh b/indent.sh index 15dfdbc0dd..67cfd72352 100755 --- a/indent.sh +++ b/indent.sh @@ -1,7 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash # Filter NUT C source file style to conform to recommendations of -# http://networkupstools.org/docs/developer-guide.chunked/ar01s03.html#_coding_style +# https://www.networkupstools.org/docs/developer-guide.chunked/ar01s03.html#_coding_style # Note that the sed filter "command does a reasonable job of converting # most C++ style comments (but not URLs and DOCTYPE strings)" so a manual # pass may be needed to revise the changes. @@ -43,7 +43,7 @@ convertFile() { TOTALCODE=0 if [ $# = 0 ] ; then - git ls-files | egrep '\.(c|h)$' | while read F ; do + git ls-files | grep -E '\.(c|h)$' | while read F ; do convertFile "$F" done else diff --git a/lib/.gitignore b/lib/.gitignore index 67496d0dbe..2c9f306f9b 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -3,4 +3,4 @@ /libnutscan.pc /libupsclient-config /libupsclient.pc -/libnutclientstub.pc +/README diff --git a/lib/Makefile.am b/lib/Makefile.am index f4d36ccc2a..7d272d8de6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,16 @@ # Network UPS Tools: lib +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + +EXTRA_DIST = README.adoc + if WITH_DEV if WITH_PKG_CONFIG pkgconfig_DATA = libupsclient.pc libnutscan.pc libnutclient.pc libnutclientstub.pc @@ -8,5 +19,5 @@ else endif endif -CLEANFILES = *-spellchecked +CLEANFILES = *-spellchecked README MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/lib/README b/lib/README.adoc similarity index 100% rename from lib/README rename to lib/README.adoc diff --git a/lib/libnutclientstub.pc.in b/lib/libnutclientstub.pc.in index b730a63054..0f8921b9f8 100644 --- a/lib/libnutclientstub.pc.in +++ b/lib/libnutclientstub.pc.in @@ -7,7 +7,7 @@ statepath=@STATEPATH@ nutuser=@RUN_AS_USER@ Name: libnutclientstub -Description: Stub for UPS monitoring with Network UPS Tools +Description: Stub for UPS monitoring with Network UPS Tools (primarily for C++ tests) Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnutclientstub Cflags: -I${includedir} diff --git a/lib/libnutscan.pc.in b/lib/libnutscan.pc.in index cf9041840f..204e92f52a 100644 --- a/lib/libnutscan.pc.in +++ b/lib/libnutscan.pc.in @@ -1,4 +1,4 @@ -prefix=/usr +prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ diff --git a/lib/libupsclient-config.in b/lib/libupsclient-config.in index 2a59f70238..f24b6685c3 100644 --- a/lib/libupsclient-config.in +++ b/lib/libupsclient-config.in @@ -7,11 +7,17 @@ # See the distribution lib/README for usage information # # **********************************************************# +# Note: PACKAGE_VERSION is baked into configure script used +# to prepare the build of NUT deployed here. Actual programs +# report a NUT_VERSION_MACRO which may differ if NUT was built +# from Git and not a release or snapshot "dist" tarball (value +# is determined during build then). Version="@PACKAGE_VERSION@" -prefix=@prefix@ -exec_prefix=@exec_prefix@ +prefix="@prefix@" +exec_prefix="@exec_prefix@" Libs="-L@libdir@ -lupsclient @LIBSSL_LIBS@" Cflags="-I@includedir@ @LIBSSL_CFLAGS@" +ConfigFlags="@CONFIG_FLAGS@" case "$1" in @@ -22,10 +28,13 @@ case "$1" in echo "$Libs" ;; --version) - echo $Version + echo "$Version" + ;; + --config_flags|--config-flags) + echo "$ConfigFlags" ;; *) - echo "Usage: libupsclient-config {--cflags|--libs|--version}" + echo "Usage: libupsclient-config {--cflags|--libs|--version|--config_flags}" exit 1 esac diff --git a/lib/libupsclient.pc.in b/lib/libupsclient.pc.in index d2d31ff006..8efce40205 100644 --- a/lib/libupsclient.pc.in +++ b/lib/libupsclient.pc.in @@ -1,4 +1,4 @@ -prefix=/usr +prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ diff --git a/m4/ax_c___attribute__.m4 b/m4/ax_c___attribute__.m4 index 0e952e7841..780bc2bb14 100644 --- a/m4/ax_c___attribute__.m4 +++ b/m4/ax_c___attribute__.m4 @@ -67,7 +67,7 @@ AC_DEFUN([AX_C___ATTRIBUTE__], [ foo(__attribute__ ((unused)) int i) { return; } - ]], [func(1);])], + ]], [foo(1);])], [ax_cv___attribute__unused_arg=yes], [ax_cv___attribute__unused_arg=no] ) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index c6376f9928..5d175082bf 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -188,6 +188,21 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunused-function"]) ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wdeprecated-declarations"], + [ax_cv__pragma__gcc__diags_ignored_deprecated_declarations], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_deprecated_declarations=yes], + [ax_cv__pragma__gcc__diags_ignored_deprecated_declarations=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_deprecated_declarations" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_DEPRECATED_DECLARATIONS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wdeprecated-declarations"]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-nonliteral"], [ax_cv__pragma__gcc__diags_ignored_format_nonliteral], [AC_COMPILE_IFELSE( @@ -350,6 +365,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( @@ -647,6 +689,33 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wextra-semi-stmt" (outside functions)]) ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Waddress"], + [ax_cv__pragma__gcc__diags_ignored_address], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Waddress" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_address=yes], + [ax_cv__pragma__gcc__diags_ignored_address=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_address" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Waddress"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Waddress" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_address_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Waddress"]], [])], + [ax_cv__pragma__gcc__diags_ignored_address_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_address_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_address_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ADDRESS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Waddress" (outside functions)]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcast-align"], [ax_cv__pragma__gcc__diags_ignored_cast_align], [AC_COMPILE_IFELSE( @@ -674,6 +743,34 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcast-align" (outside functions)]) ]) + dnl https://reviews.llvm.org/D134831 + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcast-function-type-strict"], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict=yes], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcast-function-type-strict"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcast-function-type-strict" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wcast-function-type-strict"]], [])], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cast_function_type_strict_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcast-function-type-strict" (outside functions)]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wstrict-prototypes"], [ax_cv__pragma__gcc__diags_ignored_strict_prototypes], [AC_COMPILE_IFELSE( @@ -936,45 +1033,77 @@ fi ]) AC_DEFUN([AX_C_PRINTF_STRING_NULL], [ +dnl The following code crashes on some libc implementations (e.g. Solaris 8) +dnl TODO: We may need to update NUT codebase to use NUT_STRARG() macro more +dnl often and consistently, or find a way to tweak upsdebugx() etc. varargs. + 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 Here we do not care if the compiler formally complains about + dnl undefined behavior of printf("%s", NULL) as long as it works + dnl in practice (compiler or libc implement a sane fallback): + myWARN_CFLAGS="" + AS_IF([test "${GCC}" = "yes" || test "${CLANGCC}" = "yes"], + [myWARN_CFLAGS="-Wno-format-truncation -Wno-format-overflow"]) dnl ### To be sure, bolt the language AC_LANG_PUSH([C]) - AC_CACHE_CHECK([for practical support to pritnf("%s", NULL)], + AC_CACHE_CHECK([for practical support to printf("%s", NULL)], [ax_cv__printf_string_null], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([dnl #include -#include -], [dnl +#include +], [[ char buf[128]; char *s = NULL; +/* The following line may issue pedantic static analysis warnings (ignored); + * it may also crash (segfault) during a run on some systems - hence the test. + */ int res = snprintf(buf, sizeof(buf), "%s", s); buf[sizeof(buf)-1] = '\0'; if (res < 0) { - printf(stderr, "FAILED to snprintf() a NULL string argument"); - exit 1; + fprintf(stderr, "FAILED to snprintf() a variable NULL string argument\n"); + return 1; } -if (buf[0] == '\0') - printf(stderr, "RETURNED empty string from snprintf() with a NULL string argument"); - exit 0; +if (buf[0] == '\0') { + fprintf(stderr, "RETURNED empty string from snprintf() with a variable NULL string argument\n"); + return 0; } -if (strcasestr(buf, 'null') == NULL) - printf(stderr, "RETURNED some string from snprintf() with a NULL string argument: '%s'", buf); - exit 0; +if (strstr(buf, "null") == NULL) { + fprintf(stderr, "RETURNED some string from snprintf() with a variable NULL string argument: '%s'\n", buf); + return 0; +} +fprintf(stderr, "SUCCESS: RETURNED a string that contains something like 'null' from snprintf() with a variable NULL string argument: '%s'\n", buf); + +res = printf("%s", NULL); +if (res < 0) { + fprintf(stderr, "FAILED to printf() an explicit NULL string argument (to stdout)\n"); + return 1; } -printf(stderr, "SUCCESS: RETURNED a string that contains something like 'null' from snprintf() with a NULL string argument: '%s'", buf); -exit 0; - ])], - [ax_cv__printf_string_null=yes], - [ax_cv__printf_string_null=no] +return 0; + ]])], + [ax_cv__printf_string_null=yes + ], + [ax_cv__printf_string_null=no + ], + [${myWARN_CFLAGS}] )] ) + unset myWARN_CFLAGS AS_IF([test "$ax_cv__printf_string_null" = "yes"],[ - AC_DEFINE([HAVE_PRINTF_STRING_NULL], 1, [define if your libc can printf("%s", NULL) sanely]) + AM_CONDITIONAL([REQUIRE_NUT_STRARG], [false]) + AC_DEFINE([REQUIRE_NUT_STRARG], [0], + [Define to 0 if your libc can printf("%s", NULL) sanely, or to 1 if your libc requires workarounds to print NULL values.]) + ],[ + AM_CONDITIONAL([REQUIRE_NUT_STRARG], [true]) + AC_DEFINE([REQUIRE_NUT_STRARG], [1], + [Define to 0 if your libc can printf("%s", NULL) sanely, or to 1 if your libc requires workarounds to print NULL values.]) + AC_MSG_WARN([Your C library requires workarounds to print NULL values; if something crashes with a segmentation fault (especially during verbose debug) - that may be why]) ]) AC_LANG_POP([C]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 index bd753b34d7..9316a72448 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 @@ -37,13 +40,21 @@ #serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], -[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +[ +dnl UPDATE: As tested on CentOS 6, its "autoconf-2.63-5.1.el6.noarch" also suffices. +AC_PREREQ(2.63) +dnl #AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], - [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_lua.m4 b/m4/ax_lua.m4 index 9feb352255..52fab0d92f 100644 --- a/m4/ax_lua.m4 +++ b/m4/ax_lua.m4 @@ -638,9 +638,39 @@ AC_DEFUN([AX_LUA_LIBS], [$_ax_lua_extra_libs]) LIBS=$_ax_lua_saved_libs - AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && - test "x$ac_cv_search_lua_load" != 'xnone required'], - [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) + dnl Retry with addidional library paths + AS_IF([test "x$ac_cv_search_lua_load" = 'xno'], + [ + for _ax_lua_libdir in \ + /usr/lib \ + /usr/local/lib \ + ; do + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS -L${_ax_lua_libdir} $LUA_LIB" + AS_UNSET([ac_cv_search_lua_load]) + AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && + test "x$ac_cv_search_lua_load" != 'xnone required'], + [LUA_LIB="-L${_ax_lua_libdir} $ac_cv_search_lua_load $_ax_lua_extra_libs" + break]) + done + unset _ax_lua_libdir + ],[ + AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && + test "x$ac_cv_search_lua_load" != 'xnone required'], + [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) + ]) ]) dnl Test the result and run user code. diff --git a/m4/ax_realpath.m4 b/m4/ax_realpath.m4 new file mode 100644 index 0000000000..6b8cdbc642 --- /dev/null +++ b/m4/ax_realpath.m4 @@ -0,0 +1,209 @@ +dnl Resolve given path (if we can), for filenames that may be symlinks, +dnl or in a relative directory, or where directories are symlinks... +dnl Copyright (C) 2023 by Jim Klimov +dnl Licensed under the terms of GPLv2 or newer. + +dnl Calling script is welcome to pre-detect external REALPATH implementation, +dnl otherwise shell implementation would be used (hopefully capable enough): +dnl AC_CHECK_PROGS([REALPATH], [realpath], []) + +AC_DEFUN([AX_REALPATH_SHELL_ONELEVEL], +[ + dnl # resolve links - #1 value (not quoted by caller) or some its + dnl # ancestor directory may be a symlink; save into varname #2. + dnl # In case of problems return #1 and set non-zero RESOLVE_ERROR. + dnl # We recurse backwards from original name to root "/" + dnl # (or abort mid-way). + + AS_IF([test x"$1" = x], [AC_MSG_ERROR([Bad call to REALPATH_SHELL_ONELEVEL macro (arg1)])]) + AS_IF([test x"$2" = x], [AC_MSG_ERROR([Bad call to REALPATH_SHELL_ONELEVEL macro (arg2)])]) + AS_IF([test x"$RESOLVE_ERROR" = x], [RESOLVE_ERROR=0]) + + AS_IF([test x"$RESOLVE_ERROR" != x0 || test x"$1" = x/], [dnl Quick bail out + $2="$1" + ], [ + dnl # Code below was adapted from Apache Tomcat startup.sh + TGT="$1" + + while test -h "$TGT" ; do + LS_OUT="`ls -ld "$TGT"`" || { RESOLVE_ERROR=$? ; break ; } + LINK="`expr "$LS_OUT" : '.*-> \(.*\)$'`" || { RESOLVE_ERROR=$? ; break ; } + if expr "$LINK" : '/.*' > /dev/null; then + TGT="$LINK" + else + TGT="`dirname "$TGT"`/$LINK" + fi + done + + if test "$RESOLVE_ERROR" = 0 ; then + TGTDIR="`dirname "$TGT"`" && \ + TGTDIR="`cd "$TGTDIR" && pwd`" || { + TGTDIR="`dirname "$TGT"`" || \ + RESOLVE_ERROR=$? ; } + + if test "$RESOLVE_ERROR" = 0 ; then + while test -h "$TGTDIR" ; do + LS_OUT="`ls -ld "$TGTDIR"`" || { RESOLVE_ERROR=$? ; break ; } + LINK="`expr "$LS_OUT" : '.*-> \(.*\)$'`" || { RESOLVE_ERROR=$? ; break ; } + if expr "$LINK" : '/.*' > /dev/null; then + TGTDIR="$LINK" + else + PARENTDIR="`dirname "$TGTDIR"`" + case "$PARENTDIR" in + /) TGTDIR="/$LINK" ; break ;; + *) TGTDIR="$PARENTDIR/$LINK" ;; + esac + fi + done + fi + fi + + if test "$RESOLVE_ERROR" = 0 ; then + $2="$TGTDIR/`basename "$TGT"`" + else + $2="$1" + fi + + unset TGT TGTDIR PARENTDIR + unset LS_OUT LINK + ]) +]) + +AC_DEFUN([AX_REALPATH_SHELL_RECURSIVE], +[ + dnl Autoconf/m4 is not really friendly for recursive functions + dnl so we have to do it like a loop. Sanity-checking is in the + dnl helper method above and will abort the script upon problems. + dnl The RESOLVE_ERROR is provided and unset by caller. + + RESOLVE_PREFIX="$1" + RESOLVE_SUFFIX="" + + while \ + test x"$RESOLVE_PREFIX" != x \ + && test x"$RESOLVE_PREFIX" != x/ \ + && test x"$RESOLVE_ERROR" = x0 \ + ; do + dnl In case of non-fatal resolve error, value in RESOLVE_PREFIX + dnl should remain unchanged, and a RESOLVE_ERROR flag raised. + dnl Note that the recursion would technically re-check the last + dnl seen object (the parent directory), but should quickly move + dnl on since it is not a symlink anymore. So not too ineffecient. + AX_REALPATH_SHELL_ONELEVEL([$RESOLVE_PREFIX], [RESOLVE_PREFIX]) + if test x"$RESOLVE_ERROR" = x0 ; then + dnl Recurse to check the (grand)parent dir (if any) + if test -n "$RESOLVE_SUFFIX" ; then + RESOLVE_SUFFIX="`basename "$RESOLVE_PREFIX"`/$RESOLVE_SUFFIX" + else + RESOLVE_SUFFIX="`basename "$RESOLVE_PREFIX"`" + fi + RESOLVE_PREFIX="`dirname "$RESOLVE_PREFIX"`" + else + dnl Bail out, keep latest answer + break + fi + done + + if test -n "$RESOLVE_SUFFIX" ; then + if test x"$RESOLVE_PREFIX" = x ; then + RESOLVE_PREFIX="/" + fi + if test x"$RESOLVE_PREFIX" = x/ ; then + $2="/$RESOLVE_SUFFIX" + else + $2="$RESOLVE_PREFIX/$RESOLVE_SUFFIX" + fi + else + $2="$RESOLVE_PREFIX" + fi + + unset RESOLVE_PREFIX RESOLVE_SUFFIX +]) + +AC_DEFUN([AX_REALPATH], +[ + dnl # resolve links - #1 value (not quoted by caller) + dnl # or its directory may be a softlink + dnl # save into varname #2 + AS_IF([test x"$1" = x], [AC_MSG_ERROR([Bad call to REALPATH macro (arg1)])]) + AS_IF([test x"$2" = x], [AC_MSG_ERROR([Bad call to REALPATH macro (arg2)])]) + + AC_MSG_CHECKING([for "real path" of '$1']) + + REALPRG="" + AS_IF([test -n "$REALPATH"], [ + REALPRG="`${REALPATH} "$1"`" + ]) + + AS_IF([test -z "$REALPRG"], [ + RESOLVE_ERROR=0 + + dnl Note: not all "test" implementations have "-e", so got fallbacks: + AS_IF([test -e "$1" || test -f "$1" || test -s "$1" || test -d "$1" || test -L "$1" || test -h "$1" || test -c "$1" || test -b "$1" || test -p "$1"], + [], [ + AC_MSG_WARN([Path name '$1' not found (absent or access to ancestor directories denied)]) + dnl We can still try to resolve, e.g. to find + dnl the real location an absent file would be in + dnl RESOLVE_ERROR=1 + ]) + AX_REALPATH_SHELL_RECURSIVE([$1], [REALPRG]) + ]) + + AS_IF([test -n "$REALPRG"], [ + AS_IF([test x"$REALPRG" = x"$1"], + [AC_MSG_RESULT(['$REALPRG'])], + [AC_MSG_RESULT(['$REALPRG' (differs from input)])] + ) + $2="$REALPRG" + ], [ + dnl Indent due to newline from warning and/or tool errors above + AC_MSG_RESULT([...failed to resolve, keeping original: '$1']) + $2="$1" + ]) + + unset REALPRG RESOLVE_ERROR +]) + +AC_DEFUN([UNITTEST_AX_REALPATH_EXPECT], +[ + AX_REALPATH([$1], [TMPNAME]) + AS_IF([test x"$TMPNAME" != x"$2"], [AC_MSG_WARN([>>> Got: '$TMPNAME' (should be '$2')])]) +]) + +AC_DEFUN([UNITTEST_AX_REALPATH], +[ + AC_MSG_NOTICE([======= starting UNITTEST for REALPATH macro]) + AC_MSG_NOTICE([=== Testing macro for realpath; .../q/x are directories, qwe is a file inside, and .../Q is a symlink to .../q]) + TESTDIR="`mktemp -d`" && test -d "$TESTDIR" && test -w "$TESTDIR" || TESTDIR="/tmp" + rm -rf "$TESTDIR"/q ; mkdir -p "$TESTDIR"/q/x ; echo qwe > "$TESTDIR"/q/x/qwe ; ln -fs q "$TESTDIR"/Q + dnl Do not quote TESTDIR in macro calls below, shell quotes are added in implem + AC_MSG_NOTICE([=======]) + UNITTEST_AX_REALPATH_EXPECT([$TESTDIR/q/x], [$TESTDIR/q/x]) + UNITTEST_AX_REALPATH_EXPECT([$TESTDIR/Q/x], [$TESTDIR/q/x]) + UNITTEST_AX_REALPATH_EXPECT([$TESTDIR/Q/x/qwe], [$TESTDIR/q/x/qwe]) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should not have access to next file (does not exist) in a readable dir]) + UNITTEST_AX_REALPATH_EXPECT([$TESTDIR/q/x/absent], [$TESTDIR/q/x/absent]) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should not have access to next file (does not exist) in a SYMLINK to readable dir]) + UNITTEST_AX_REALPATH_EXPECT([$TESTDIR/Q/x/absent], [$TESTDIR/q/x/absent]) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Present and visible or not, unless this is (behind) a symlink it should remain the same path]) + UNITTEST_AX_REALPATH_EXPECT([/etc/nut/ups.conf], [/etc/nut/ups.conf]) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should be a shell interpreter here (if procfs is supported on the platform)]) + AX_REALPATH([/proc/$$/exe], [TMPNAME]) + AC_MSG_NOTICE([>>> Got: '$TMPNAME']) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should be an stdin socket here (if procfs is supported on the platform)]) + AX_REALPATH([/proc/$$/fd/1], [TMPNAME]) + AC_MSG_NOTICE([>>> Got: '$TMPNAME']) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should not have access to next SYMLINK (reading link content is forbidden, if procfs is supported on the platform and if not root)]) + UNITTEST_AX_REALPATH_EXPECT([/proc/1/exe], [/proc/1/exe]) + AC_MSG_NOTICE([=======]) + AC_MSG_NOTICE([=== Should not have access to next dir (don't know if file exists, if procfs is supported on the platform and if not root)]) + UNITTEST_AX_REALPATH_EXPECT([/proc/1/fd/1], [/proc/1/fd/1]) + AC_MSG_NOTICE([======= end of UNITTEST for REALPATH macro]) + AS_IF([test x"$TESTDIR" = x"/tmp"], [rm -rf "$TESTDIR/q" "$TESTDIR/Q"], [rm -rf "$TESTDIR"]) +]) diff --git a/m4/ax_run_or_link_ifelse.m4 b/m4/ax_run_or_link_ifelse.m4 new file mode 100644 index 0000000000..0a54cbc419 --- /dev/null +++ b/m4/ax_run_or_link_ifelse.m4 @@ -0,0 +1,39 @@ +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 Here the fourth and fifth arguments can be used to pass custom +dnl CFLAGS and CXXFLAGS options (e.g. warnings to cover or ignore), +dnl respectively. For the test to succeed, the build/link must pass +dnl without warnings. + +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], +[ + myCFLAGS="$CFLAGS" + myCXXFLAGS="$CXXFLAGS" + AS_IF([test "${GCC}" = "yes" || test "${CLANGCC}" = "yes"], [ + CFLAGS="$myCFLAGS -Werror -Werror=implicit-function-declaration $4" + dnl # cc1plus: error: '-Werror=' argument '-Werror=implicit-function-declaration' is not valid for C++ [-Werror] + CXXFLAGS="$myCXXFLAGS -Werror $5" + ], [ + dnl # Don't know what to complain about for unknown compilers + dnl # FIXME: Presume here they have at least a "-Werror" option + CFLAGS="$myCFLAGS -Werror $4" + CXXFLAGS="$myCXXFLAGS -Werror $5" + ]) + 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]) + ] + ) + CFLAGS="$myCFLAGS" + CXXFLAGS="$myCXXFLAGS" +]) diff --git a/m4/nut_arg_with.m4 b/m4/nut_arg_with.m4 index 686306b871..e85f7ee8ed 100644 --- a/m4/nut_arg_with.m4 +++ b/m4/nut_arg_with.m4 @@ -5,8 +5,8 @@ dnl https://www.gnu.org/software/autoconf/manual/autoconf-2.66/html_node/Externa AC_DEFUN([NUT_ARG_WITH], [ AC_ARG_WITH($1, AS_HELP_STRING([--with-$1], [$2 ($3)]), - [nut_with_$1="${withval}"], - [nut_with_$1="$3"] + [[nut_with_]m4_translit($1, [-], [_])="${withval}"], + [[nut_with_]m4_translit($1, [-], [_])="$3"] ) ]) @@ -15,7 +15,7 @@ dnl https://www.gnu.org/software/autoconf/manual/autoconf-2.66/html_node/Package AC_DEFUN([NUT_ARG_ENABLE], [ AC_ARG_ENABLE($1, AS_HELP_STRING([--enable-$1], [$2 ($3)]), - [nut_enable_$1="${enableval}"], - [nut_enable_$1="$3"] + [[nut_enable_]m4_translit($1, [-], [_])="${enableval}"], + [[nut_enable_]m4_translit($1, [-], [_])="$3"] ) ]) diff --git a/m4/nut_check_asciidoc.m4 b/m4/nut_check_asciidoc.m4 index 5837e13d91..7d1d108a76 100644 --- a/m4/nut_check_asciidoc.m4 +++ b/m4/nut_check_asciidoc.m4 @@ -15,9 +15,13 @@ AC_DEFUN([NUT_CHECK_ASCIIDOC], [ if test -z "${nut_have_asciidoc_seen}"; then nut_have_asciidoc_seen=yes - # Note: this is for both asciidoc and a2x at this time - ASCIIDOC_MIN_VERSION="8.6.3" - # Note: this is checked in the configure script if PDF is of interest at all + dnl # Note: this is typically same for both asciidoc and a2x: + dnl # most systems nowadays (2023) have both at 8.6.3 or newer. + dnl # Slackware 15 has asciidoc-8.1.0 and a2x-1.0.0 served in + dnl # a side-project repository... + ASCIIDOC_MIN_VERSION="8.1.0" + A2X_MIN_VERSION="1.0.0" + dnl # Note: this is checked in the configure script if PDF is of interest at all DBLATEX_MIN_VERSION="0.2.5" AC_PATH_PROGS([ASCIIDOC], [asciidoc]) @@ -76,10 +80,6 @@ if test -z "${nut_have_asciidoc_seen}"; then AC_PATH_PROGS([SOURCE_HIGHLIGHT], [source-highlight]) AM_CONDITIONAL([HAVE_SOURCE_HIGHLIGHT], [test -n "$SOURCE_HIGHLIGHT"]) - dnl check for spell checking deps - AC_PATH_PROGS([ASPELL], [aspell]) - AM_CONDITIONAL([HAVE_ASPELL], [test -n "$ASPELL"]) - dnl Note that a common "nut_have_asciidoc" variable is in fact a flag dnl that we have several tools needed for the documentation generation dnl TODO? Rename the script variable and makefile flags to reflect this? @@ -92,8 +92,8 @@ if test -z "${nut_have_asciidoc_seen}"; then nut_have_asciidoc="no" ]) - AC_MSG_CHECKING([if a2x version can build manpages (minimum required ${ASCIIDOC_MIN_VERSION})]) - AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ + AC_MSG_CHECKING([if a2x version can build manpages (minimum required ${A2X_MIN_VERSION})]) + AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${A2X_MIN_VERSION}], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) diff --git a/m4/nut_check_aspell.m4 b/m4/nut_check_aspell.m4 new file mode 100644 index 0000000000..dbccd27878 --- /dev/null +++ b/m4/nut_check_aspell.m4 @@ -0,0 +1,167 @@ +dnl Check for tools used in spell-checking of documentation source files. +dnl On success, set nut_have_aspell="yes" (meaning we can do at least some +dnl documentation checks) and lots of automake macros and configure vars. +dnl On failure, set nut_have_aspell="no" (meaning we can't run the checks). +dnl This macro can be run multiple times, but will do the checking only once. + +AC_DEFUN([NUT_CHECK_ASPELL], +[ +if test -z "${nut_have_aspell_seen}"; then + nut_have_aspell_seen=yes + + dnl # Note: this is just a known-working version on NUT CI platforms: + dnl # Legacy baselines (CentOS 7, OpenBSD 6.5): + dnl # @(#) International Ispell Version 3.1.20 (but really Aspell 0.60.6.1) + dnl # More recent distros (as of 2022-2023): + dnl # @(#) International Ispell Version 3.1.20 (but really Aspell 0.60.8) + ASPELL_MIN_VERSION="0.60.6" + + dnl check for spell checking deps + AC_PATH_PROGS([ASPELL], [aspell]) + + dnl Some builds of aspell (e.g. in mingw) claim they do not know mode "tex" + dnl even though they can list it as a built-in filter and files exist. + dnl It seems that specifying the path helps in those cases. + ASPELL_FILTER_LIB_PATH="none" + ASPELL_FILTER_SHARE_PATH="none" + dnl Location of "tex.amf" may be shifted, especially if binary filters + dnl are involved (happens in some platform packages but not others). + ASPELL_FILTER_TEX_PATH="none" + if test -n "${ASPELL}" ; then + dnl # e.g.: @(#) International Ispell Version 3.1.20 (but really Aspell 0.60.8) + AC_MSG_CHECKING([for aspell version]) + ASPELL_VERSION="`LANG=C LC_ALL=C ${ASPELL} --version 2>/dev/null | sed -e 's,^.*@<:@Aa@:>@spell \(@<:@0-9.@:>@*\),\1,' -e 's,@<:@^0-9.@:>@.*,,'`" || ASPELL_VERSION="none" + AC_MSG_RESULT([${ASPELL_VERSION}]) + + ASPELL_VERSION_MINMAJ="`echo "${ASPELL_VERSION}" | sed 's,\.@<:@0-9@:>@@<:@0-9@:>@*$,,'`" + + dnl FIXME: Some systems have more complicated layouts, e.g. + dnl /usr/lib/amd64/aspell-0.60/tex-filter.so + dnl /usr/lib/aspell-0.60/tex-filter.so + dnl which require matching of the `aspell` binary architecture + dnl with the module. We currently avoid the hassle thanks to a + dnl fallback to built-in paths below if this initial guesswork + dnl failed. This may need some more-direct addressing later. + AC_MSG_CHECKING([for aspell "lib" filtering resources directory]) + ASPELL_BINDIR="`dirname "$ASPELL"`" + if test -d "${ASPELL_BINDIR}/../lib" ; then + if test x"${ASPELL_VERSION}" != x"none" && test -d "${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION}" ; then + ASPELL_FILTER_LIB_PATH="`cd "${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION}" && pwd`" \ + || ASPELL_FILTER_LIB_PATH="${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION}" + else + if test x"${ASPELL_VERSION_MINMAJ}" != x"none" && test -d "${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION_MINMAJ}" ; then + ASPELL_FILTER_LIB_PATH="`cd "${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION_MINMAJ}" && pwd`" \ + || ASPELL_FILTER_LIB_PATH="${ASPELL_BINDIR}/../lib/aspell-${ASPELL_VERSION_MINMAJ}" + else + if test -d "${ASPELL_BINDIR}/../lib/aspell" ; then + ASPELL_FILTER_LIB_PATH="`cd "${ASPELL_BINDIR}/../lib/aspell" && pwd`" \ + || ASPELL_FILTER_LIB_PATH="${ASPELL_BINDIR}/../lib/aspell" + fi + fi + fi + fi + AC_MSG_RESULT([${ASPELL_FILTER_LIB_PATH}]) + + AC_MSG_CHECKING([for aspell "share" filtering resources directory]) + ASPELL_BINDIR="`dirname "$ASPELL"`" + if test -d "${ASPELL_BINDIR}/../share" ; then + if test x"${ASPELL_VERSION}" != x"none" && test -d "${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION}" ; then + ASPELL_FILTER_SHARE_PATH="`cd "${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION}" && pwd`" \ + || ASPELL_FILTER_SHARE_PATH="${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION}" + else + if test x"${ASPELL_VERSION_MINMAJ}" != x"none" && test -d "${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION_MINMAJ}" ; then + ASPELL_FILTER_SHARE_PATH="`cd "${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION_MINMAJ}" && pwd`" \ + || ASPELL_FILTER_SHARE_PATH="${ASPELL_BINDIR}/../share/aspell-${ASPELL_VERSION_MINMAJ}" + else + if test -d "${ASPELL_BINDIR}/../share/aspell" ; then + ASPELL_FILTER_SHARE_PATH="`cd "${ASPELL_BINDIR}/../share/aspell" && pwd`" \ + || ASPELL_FILTER_SHARE_PATH="${ASPELL_BINDIR}/../share/aspell" + fi + fi + fi + fi + AC_MSG_RESULT([${ASPELL_FILTER_SHARE_PATH}]) + + AC_MSG_CHECKING([for aspell "tex" filtering resources directory]) + dnl # May be in a platform-dependent subdir (e.g. Debian Linux) + dnl # or not (e.g. MinGW/MSYS2, OpenIndiana): + if test -d "${ASPELL_FILTER_LIB_PATH}" ; then + ASPELL_FILTER_TEX_PATH="`find "${ASPELL_FILTER_LIB_PATH}" -name "tex.amf"`" \ + && test x"${ASPELL_FILTER_TEX_PATH}" != x \ + && ASPELL_FILTER_TEX_PATH="`dirname "${ASPELL_FILTER_TEX_PATH}"`" \ + && test -d "${ASPELL_FILTER_TEX_PATH}" \ + || ASPELL_FILTER_TEX_PATH="none" + fi + dnl # Fallback (e.g. on FreeBSD): + if test x"${ASPELL_FILTER_TEX_PATH}" = xnone \ + && test -d "${ASPELL_FILTER_SHARE_PATH}" ; then + ASPELL_FILTER_TEX_PATH="`find "${ASPELL_FILTER_SHARE_PATH}" -name "tex.amf"`" \ + && test x"${ASPELL_FILTER_TEX_PATH}" != x \ + && ASPELL_FILTER_TEX_PATH="`dirname "${ASPELL_FILTER_TEX_PATH}"`" \ + && test -d "${ASPELL_FILTER_TEX_PATH}" \ + || ASPELL_FILTER_TEX_PATH="none" + fi + + AC_MSG_RESULT([${ASPELL_FILTER_TEX_PATH}]) + fi + + AC_MSG_CHECKING([if aspell version can do our documentation spell checks (minimum required ${ASPELL_MIN_VERSION})]) + AX_COMPARE_VERSION([${ASPELL_VERSION}], [ge], [${ASPELL_MIN_VERSION}], [ + AC_MSG_RESULT(yes) + AC_MSG_CHECKING([if detected aspell configuration works]) + dnl Roughly following docs/Makefile.am setup for "make spellcheck": + ASPELL_NUT_TEXMODE_ARGS="-t" + AS_IF([test -n "$ASPELL_FILTER_TEX_PATH" -a -d "$ASPELL_FILTER_TEX_PATH"], [ASPELL_NUT_TEXMODE_ARGS="--filter-path='${ASPELL_FILTER_TEX_PATH}' ${ASPELL_NUT_TEXMODE_ARGS}"]) + ASPELL_NUT_COMMON_ARGS="-d en -a" + dnl Using "eval" to handle quotes, in case of funny paths + out0="`LANG=C; LC_ALL=C; export LANG; export LC_ALL; exec -- 2>&1; set -x; echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}`"; res0=$? + AS_IF([test x"$res0" != x0], [ + AC_MSG_NOTICE([FAILED CMD: ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}]) + AC_MSG_NOTICE([aspell result ($res0) and output: $out0]) + ]) + AS_CASE([$out0], + [*ELFCLASS*|*"wrong ELF class"*], [ + dnl Retry without the filter path, we must have caught a wrong one + dnl and *most* platforms do serve a trustworthy built-in after all: + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if detected aspell configuration works with built-in paths (tweaked one finds wrong binary modules)]) + ASPELL_NUT_TEXMODE_ARGS="-t" + out0="`LANG=C; LC_ALL=C; export LANG; export LC_ALL; exec -- 2>&1; set -x; echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}`"; res0=$? + AS_IF([test x"$res0" = x0], [ASPELL_FILTER_TEX_PATH=""], [ + AC_MSG_NOTICE([FAILED CMD: ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}]) + AC_MSG_NOTICE([aspell result ($res0) and output: $out0]) + ]) + ] + ) + out1="`echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS} | grep test`"; res1=$? + out2="`echo qwer | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS} | grep qwer`"; res2=$? + AS_IF([test x"$out1" = x -a x"$out2" != x], [ + AC_MSG_RESULT(yes) + nut_have_aspell="yes" + ], [ + AC_MSG_RESULT(no) + AC_MSG_NOTICE([aspell result ($res1) for 'test' (should be empty): $out1]) + AC_MSG_NOTICE([aspell result ($res2) for 'qwer' (should have suggestions): $out2]) + nut_have_aspell="no" + ]) + ], [ + AC_MSG_RESULT(no) + nut_have_aspell="no" + ]) + + AM_CONDITIONAL([HAVE_ASPELL_FILTER_LIB_PATH], [test -d "$ASPELL_FILTER_LIB_PATH"]) + AC_SUBST(ASPELL_FILTER_LIB_PATH) + AM_CONDITIONAL([HAVE_ASPELL_FILTER_SHARE_PATH], [test -d "$ASPELL_FILTER_SHARE_PATH"]) + AC_SUBST(ASPELL_FILTER_SHARE_PATH) + AM_CONDITIONAL([HAVE_ASPELL_FILTER_TEX_PATH], [test -d "$ASPELL_FILTER_TEX_PATH"]) + AC_SUBST(ASPELL_FILTER_TEX_PATH) + + dnl Notes: we also keep HAVE_ASPELL for implicit targets, such as + dnl addition to "make check" target + dnl ### AM_CONDITIONAL([HAVE_ASPELL], [test -n "$ASPELL"]) + AM_CONDITIONAL([HAVE_ASPELL], [test "${nut_have_aspell}" = "yes"]) + + AC_MSG_CHECKING([if we have all the tools mandatory for documentation spell checks]) + AC_MSG_RESULT([${nut_have_aspell}]) +fi +]) diff --git a/m4/nut_check_headers_windows.m4 b/m4/nut_check_headers_windows.m4 index 6f30c92499..ce0286d045 100644 --- a/m4/nut_check_headers_windows.m4 +++ b/m4/nut_check_headers_windows.m4 @@ -12,24 +12,37 @@ dnl Check for compilable and valid windows.h header AC_DEFUN([NUT_CHECK_HEADER_WINDOWS], [ AC_CACHE_CHECK([for windows.h], [nut_cv_header_windows_h], [ AC_LANG_PUSH([C]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ + TESTPROG_H=' #undef inline #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include - ]],[[ +' + TESTPROG_C=' #if defined(__CYGWIN__) || defined(__CEGCC__) HAVE_WINDOWS_H shall not be defined. #else int dummy=2*WINVER; #endif - ]]) +' + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([$TESTPROG_H], [$TESTPROG_C]) ],[ nut_cv_header_windows_h="yes" ],[ - nut_cv_header_windows_h="no" + dnl Try extending default search path as relevant for e.g. MSYS2 + dnl (though there this should be compiler built-in default): + SAVED_CFLAGS="$CFLAGS" + CFLAGS="-I/usr/include/w32api $CFLAGS" + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([$TESTPROG_H], [$TESTPROG_C]) + ],[ + nut_cv_header_windows_h="yes" + ],[ + nut_cv_header_windows_h="no" + CFLAGS="$SAVED_CFLAGS" + ]) ]) AC_LANG_POP([C]) ]) @@ -39,6 +52,7 @@ AC_DEFUN([NUT_CHECK_HEADER_WINDOWS], [ [Define to 1 if you have the windows.h header file.]) ;; esac + AM_CONDITIONAL(HAVE_WINDOWS_H, test "x$nut_cv_header_windows_h" = xyes) ]) @@ -111,6 +125,7 @@ AC_DEFUN([NUT_CHECK_HEADER_WINSOCK], [ [Define to 1 if you have the winsock.h header file.]) ;; esac + AM_CONDITIONAL(HAVE_WINSOCK_H, test "x$nut_cv_header_winsock_h" = xyes) ]) @@ -150,6 +165,7 @@ AC_DEFUN([NUT_CHECK_HEADER_WINSOCK2], [ [Define to 1 if you have the winsock2.h header file.]) ;; esac + AM_CONDITIONAL(HAVE_WINSOCK2_H, test "x$nut_cv_header_winsock2_h" = xyes) ]) @@ -190,4 +206,5 @@ AC_DEFUN([NUT_CHECK_HEADER_WS2TCPIP], [ [Define to 1 if you have the ws2tcpip.h header file.]) ;; esac + AM_CONDITIONAL(HAVE_WS2TCPIP_H, test "x$nut_cv_header_ws2tcpip_h" = xyes) ]) diff --git a/m4/nut_check_libgd.m4 b/m4/nut_check_libgd.m4 index 858ae021f4..a8c7de5d17 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,29 @@ 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=yes], [nut_have_libgd=no]) + ]) + unset _LIBS_PRIVATE + dnl At least mingw 32-bit builds of the DLL seem to not + dnl tell the linker how to get from GD to PNG lib + AS_IF([test x"$nut_have_libgd" = xno], [ + AC_MSG_CHECKING([with explicit -lpng in the loop]) + LDFLAGS="$LDFLAGS -lgd" + unset ac_cv_search_gdImagePng + AC_SEARCH_LIBS(gdImagePng, png png16, [nut_have_libgd=yes], [nut_have_libgd=no]) + ]) + ]) + ]) 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_libgpiod.m4 b/m4/nut_check_libgpiod.m4 new file mode 100644 index 0000000000..b946b63dbc --- /dev/null +++ b/m4/nut_check_libgpiod.m4 @@ -0,0 +1,90 @@ +dnl Check for LIBGPIO compiler flags. On success, set nut_have_gpio="yes" +dnl and set LIBGPIO_CFLAGS and LIBGPIO_LIBS. On failure, set +dnl nut_have_gpio="no". This macro can be run multiple times, but will +dnl do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBGPIO], +[ +if test -z "${nut_have_gpio_seen}"; then + nut_have_gpio_seen=yes + NUT_CHECK_PKGCONFIG + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + nut_gpio_lib="" + + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [dnl See which version of the gpiod library (if any) is installed + dnl FIXME : Support detection of cflags/ldflags below by legacy + dnl discovery if pkgconfig is not there + AC_MSG_CHECKING(for libgpiod version via pkg-config (1.0.0 minimum required)) + GPIO_VERSION="`$PKG_CONFIG --silence-errors --modversion libgpiod 2>/dev/null`" + if test "$?" != "0" -o -z "${GPIO_VERSION}"; then + GPIO_VERSION="none" + else + nut_gpio_lib="libgpiod" + fi + AC_MSG_RESULT(${GPIO_VERSION} found) + ], + [GPIO_VERSION="none" + AC_MSG_NOTICE([can not check libgpiod settings via pkg-config]) + ] + ) + + AC_MSG_CHECKING(for libgpiod cflags) + AC_ARG_WITH(gpio-includes, + AS_HELP_STRING([@<:@--with-gpio-includes=CFLAGS@:>@], [include flags for the gpiod library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-gpio-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags libgpiod 2>/dev/null`" || CFLAGS="-I/usr/include -I/usr/local/include"], + [CFLAGS="-I/usr/include -I/usr/local/include"] + )] + ) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for libgpiod ldflags) + AC_ARG_WITH(gpio-libs, + AS_HELP_STRING([@<:@--with-gpio-libs=LIBS@:>@], [linker flags for the gpiod library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-gpio-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [LIBS="`$PKG_CONFIG --silence-errors --libs libgpiod 2>/dev/null`" || LIBS="-lgpiod"], + [LIBS="-lgpiod"] + )] + ) + AC_MSG_RESULT([${LIBS}]) + + dnl check if gpiod is usable + AC_CHECK_HEADERS(gpiod.h, [nut_have_gpio=yes], [nut_have_gpio=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(gpiod_chip_open_by_name gpiod_chip_close, [nut_gpio_lib="libgpiod"], [nut_have_gpio=no]) + + if test "${nut_have_gpio}" = "yes"; then + LIBGPIO_CFLAGS="${CFLAGS}" + LIBGPIO_LIBS="${LIBS}" + else + nut_gpio_lib="" + fi + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" +fi +]) diff --git a/m4/nut_check_libltdl.m4 b/m4/nut_check_libltdl.m4 index 4c5f6e7306..9c67e93eef 100644 --- a/m4/nut_check_libltdl.m4 +++ b/m4/nut_check_libltdl.m4 @@ -1,16 +1,21 @@ dnl Check for LIBLTDL compiler flags. On success, set nut_have_libltdl="yes" dnl and set LIBLTDL_CFLAGS and LIBLTDL_LIBS. On failure, set dnl nut_have_libltdl="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_LIBLTDL], +AC_DEFUN([NUT_CHECK_LIBLTDL], [ if test -z "${nut_have_libltdl_seen}"; then nut_have_libltdl_seen=yes + dnl No NUT_CHECK_PKGCONFIG here: (lib)ltdl.pc was not seen on any OS - dnl save LIBS + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" LIBS="" + CFLAGS="" + dnl For fallback below: + myCFLAGS="" AC_MSG_CHECKING(for libltdl cflags) AC_ARG_WITH(libltdl-includes, @@ -24,7 +29,10 @@ if test -z "${nut_have_libltdl_seen}"; then CFLAGS="${withval}" ;; esac - ], []) + ], [dnl Best-Effort Fallback (LDFLAGS might make more sense for -L..., + dnl but other m4's have it so) to use if probe below fails: + myCFLAGS="-I/usr/local/include -I/usr/include -L/usr/local/lib -L/usr/lib" + ]) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libltdl ldflags) @@ -39,19 +47,38 @@ if test -z "${nut_have_libltdl_seen}"; then LIBS="${withval}" ;; esac - ], []) + ], [])dnl No fallback here - we probe suitable libs below AC_MSG_RESULT([${LIBS}]) - AC_CHECK_HEADERS(ltdl.h, [nut_have_libltdl=yes], [nut_have_libltdl=no], [AC_INCLUDES_DEFAULT]) - AC_SEARCH_LIBS(lt_dlinit, ltdl, [], [nut_have_libltdl=no]) + AC_CHECK_HEADERS(ltdl.h, [nut_have_libltdl=yes], [ + dnl Double-check if we stashed include paths to try above + AS_IF([test -n "$myCFLAGS"], [ + CFLAGS="$myCFLAGS" + AS_UNSET([ac_cv_header_ltdl_h]) + AC_CHECK_HEADERS(ltdl.h, [nut_have_libltdl=yes], [nut_have_libltdl=no], [AC_INCLUDES_DEFAULT]) + ],[nut_have_libltdl=no] + )], [AC_INCLUDES_DEFAULT]) + AS_IF([test x"$nut_have_libltdl" = xyes], [ + dnl ltdl-number may help find it for MingW DLLs naming + AC_SEARCH_LIBS(lt_dlinit, ltdl ltdl-7, [], [ + nut_have_libltdl=no + AS_IF([test -n "$myCFLAGS" -a x"$myCFLAGS" != x"$CFLAGS"], [ + CFLAGS="$myCFLAGS" + dnl No ltdl-7 here, this codepath is unlikely on Windows where that matters: + AC_SEARCH_LIBS(lt_dlinit, ltdl, [nut_have_libltdl=yes], []) + ]) + ]) + ]) - if test "${nut_have_libltdl}" = "yes"; then + AS_IF([test "${nut_have_libltdl}" = "yes"], [ AC_DEFINE(HAVE_LIBLTDL, 1, [Define to enable libltdl support]) - LIBLTDL_CFLAGS="" + LIBLTDL_CFLAGS="${CFLAGS}" LIBLTDL_LIBS="${LIBS}" - fi + ]) + unset myCFLAGS - dnl restore original LIBS + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" fi ]) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index ea08dd3659..dbaae4968e 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -72,6 +72,8 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_new_rtu_usb, [nut_have_libmodbus_usb=yes], [nut_have_libmodbus_usb=no]) + dnl modbus_set_byte_timeout() and modbus_set_response_timeout() dnl in 3.0.x and 3.1.x have different args (since ~2013): the dnl older version used to accept timeout as a struct timeval @@ -180,6 +182,10 @@ modbus_set_byte_timeout(ctx, to_sec, to_usec);]) LIBMODBUS_LIBS="${LIBS}"] ) + AS_IF([test x"${nut_have_libmodbus_usb}" = x"yes"], + [AC_DEFINE([NUT_MODBUS_HAS_USB], 1, [Define to use libmodbus USB backend])] + ) + dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" diff --git a/m4/nut_check_libnetsnmp.m4 b/m4/nut_check_libnetsnmp.m4 index 836d09da9f..389ac2d2ff 100644 --- a/m4/nut_check_libnetsnmp.m4 +++ b/m4/nut_check_libnetsnmp.m4 @@ -100,6 +100,7 @@ if test -z "${nut_have_libnetsnmp_seen}"; then ) AC_MSG_RESULT([${CFLAGS}]) + myLIBS_SOURCE="" AC_MSG_CHECKING(for Net-SNMP libs) AC_ARG_WITH(snmp-libs, AS_HELP_STRING([@<:@--with-snmp-libs=LIBS@:>@], [linker flags for the Net-SNMP library]), @@ -109,21 +110,52 @@ if test -z "${nut_have_libnetsnmp_seen}"; then AC_MSG_ERROR(invalid option --with(out)-snmp-libs - see docs/configure.txt) ;; *) + myLIBS_SOURCE="confarg" LIBS="${withval}" ;; esac ], [AS_IF(["${prefer_NET_SNMP_CONFIG}"], - [LIBS="`${NET_SNMP_CONFIG} --libs 2>/dev/null`"], + [LIBS="`${NET_SNMP_CONFIG} --libs 2>/dev/null`" + myLIBS_SOURCE="netsnmp-config"], [AS_IF([test x"$have_PKG_CONFIG" = xyes], - [LIBS="`$PKG_CONFIG --silence-errors --libs netsnmp 2>/dev/null`"], - [LIBS="-lnetsnmp"])] + [LIBS="`$PKG_CONFIG --silence-errors --libs netsnmp 2>/dev/null`" + myLIBS_SOURCE="pkg-config"], + [LIBS="-lnetsnmp" + myLIBS_SOURCE="default"])] )] ) AC_MSG_RESULT([${LIBS}]) dnl Check if the Net-SNMP library is usable + nut_have_libnetsnmp_static=no AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [nut_have_libnetsnmp=yes], [nut_have_libnetsnmp=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS(init_snmp, [], [nut_have_libnetsnmp=no]) + AC_CHECK_FUNCS(init_snmp, [], [ + dnl Probably is dysfunctional, except one case... + nut_have_libnetsnmp=no + AS_IF([test x"$myLIBS_SOURCE" = x"pkg-config"], [ + AS_CASE(["${target_os}"], + [*mingw*], [ + AC_MSG_NOTICE([mingw builds of net-snmp might provide only a static library - retrying for that]) + LIBS="`$PKG_CONFIG --silence-errors --libs --static netsnmp 2>/dev/null`" + dnl # Some workarouds here, to avoid libtool bailing out like this: + dnl # *** Warning: This system cannot link to static lib archive /usr/x86_64-w64-mingw32/lib//libnetsnmp.la. + dnl # *** I have the capability to make that library automatically link in when + dnl # *** you link to this library. But I can only do this if you have a + dnl # *** shared version of the library, which you do not appear to have. + dnl # In Makefiles be sure to use _LDFLAGS (not _LIBADD) to smuggle linker + dnl # arguments when building "if WITH_SNMP_STATIC" recipe blocks! + dnl # For a practical example, see tools/nut-scanner/Makefile.am. + LIBS="`echo " $LIBS" | sed 's/ -l/ -Wl,-l/g'`" + AS_UNSET([ac_cv_func_init_snmp]) + AC_CHECK_FUNCS(init_snmp, [ + nut_have_libnetsnmp=yes + nut_have_libnetsnmp_static=yes + ]) + ] + ) + ]) + ]) + AS_UNSET([myLIBS_SOURCE]) AS_IF([test "${nut_have_libnetsnmp}" = "yes"], [ LIBNETSNMP_CFLAGS="${CFLAGS}" @@ -164,6 +196,9 @@ oid * pProto = usmAES128PrivProtocol; #include #include oid * pProto = usmDESPrivProtocol; +#ifdef NETSNMP_DISABLE_DES +#error "NETSNMP_DISABLE_DES is defined" +#endif ], [] )], @@ -179,6 +214,9 @@ oid * pProto = usmDESPrivProtocol; #include #include oid * pProto = usmHMAC256SHA384AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -194,6 +232,9 @@ oid * pProto = usmHMAC256SHA384AuthProtocol; #include #include oid * pProto = usmHMAC384SHA512AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -209,6 +250,9 @@ oid * pProto = usmHMAC384SHA512AuthProtocol; #include #include oid * pProto = usmHMAC192SHA256AuthProtocol; +#ifndef HAVE_EVP_SHA224 +#error "HAVE_EVP_SHA224 is NOT defined" +#endif ], [] )], @@ -224,6 +268,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 +286,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 +304,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_libregex.m4 b/m4/nut_check_libregex.m4 new file mode 100644 index 0000000000..86c2bc1552 --- /dev/null +++ b/m4/nut_check_libregex.m4 @@ -0,0 +1,102 @@ +dnl Check for LIBREGEX (and, if found, fill 'nut_usb_lib' with its +dnl approximate version) and its compiler flags. On success, set +dnl nut_have_libusb="yes" and set LIBREGEX_CFLAGS and LIBREGEX_LIBS. On failure, set +dnl nut_have_libusb="no". This macro can be run multiple times, but will +dnl do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBREGEX], +[ +if test -z "${nut_have_libregex_seen}"; then + nut_have_libregex_seen=yes + NUT_CHECK_PKGCONFIG + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + CFLAGS="" + LIBS="" + + dnl Actually did not see it in any systems' pkg-config info... + dnl Part of standard footprint? + LIBREGEX_MODULE="" + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING(for libregex version via pkg-config) + LIBREGEX_VERSION="`$PKG_CONFIG --silence-errors --modversion regex 2>/dev/null`" + if test "$?" != "0" -o -z "${LIBREGEX_VERSION}"; then + LIBREGEX_VERSION="`$PKG_CONFIG --silence-errors --modversion libregex 2>/dev/null`" + if test "$?" != "0" -o -z "${LIBREGEX_VERSION}"; then + LIBREGEX_VERSION="none" + else + LIBREGEX_MODULE="libregex" + fi + else + LIBREGEX_MODULE="regex" + fi + AC_MSG_RESULT(${LIBREGEX_VERSION} found) + ], + [LIBREGEX_VERSION="none" + AC_MSG_NOTICE([can not check libregex settings via pkg-config]) + ] + ) + + AS_IF([test x"$LIBREGEX_VERSION" != xnone && test x"$LIBREGEX_MODULE" != x], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags "${LIBREGEX_MODULE}" 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs "${LIBREGEX_MODULE}" 2>/dev/null`" + REQUIRES="${LIBREGEX_MODULE}" + ], + [CFLAGS="" + LIBS="" + REQUIRES="" + ] + ) + + dnl Check if libregex is usable + AC_LANG_PUSH([C]) + dnl # With USB we can match desired devices by regex + dnl # (and currently have no other use for the library); + dnl # however we may have some general regex helper + dnl # methods built into libcommon (may become useful + dnl # elsewhere) - so need to know if we may and should. + dnl # Maybe already involved in NUT for Windows builds... + nut_have_regex=no + AC_CHECK_HEADER([regex.h], + [nut_have_regex=yes + AC_DEFINE([HAVE_REGEX_H], [1], + [Define to 1 if you have .])]) + AC_CHECK_DECLS([regexec, regcomp], [nut_have_regex=yes], [], +[#ifdef HAVE_REGEX_H +# include +#endif +]) + + AC_SEARCH_LIBS([regcomp, regexec], [], [nut_have_regex=yes], [ + AS_IF([test x"$LIBS" = x], [ + dnl Avoid using cached reply for the absent library name + unset ac_cv_search_regcomp__regexec || true + AC_SEARCH_LIBS([regcomp, regexec], [regex], [ + LIBS="-lregex" + nut_have_regex=yes + ]) + ]) + ]) + + AS_IF([test x"${nut_have_regex}" = xyes], [ + LIBREGEX_CFLAGS="${CFLAGS}" + LIBREGEX_LIBS="${LIBS}" + AC_DEFINE(HAVE_LIBREGEX, 1, + [Define to 1 for build where we can support general regex matching.]) + ], [ + LIBREGEX_CFLAGS="" + LIBREGEX_LIBS="" + AC_DEFINE(HAVE_LIBREGEX, 0, + [Define to 1 for build where we can support general regex matching.]) + ]) + AM_CONDITIONAL(HAVE_LIBREGEX, test x"${nut_have_regex}" = xyes) + + AC_LANG_POP([C]) + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" +fi +]) diff --git a/m4/nut_check_libsystemd.m4 b/m4/nut_check_libsystemd.m4 new file mode 100644 index 0000000000..155add4423 --- /dev/null +++ b/m4/nut_check_libsystemd.m4 @@ -0,0 +1,90 @@ +dnl Check for LIBSYSTEMD compiler flags. On success, set nut_have_libsystemd="yes" +dnl and set LIBSYSTEMD_CFLAGS and LIBSYSTEMD_LIBS. On failure, set +dnl nut_have_libsystemd="no". This macro can be run multiple times, but will +dnl do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBSYSTEMD], +[ +if test -z "${nut_have_libsystemd_seen}"; then + nut_have_libsystemd_seen=yes + NUT_CHECK_PKGCONFIG + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [dnl See which version of the systemd library (if any) is installed + dnl FIXME : Support detection of cflags/ldflags below by legacy + dnl discovery if pkgconfig is not there + AC_MSG_CHECKING(for libsystemd version via pkg-config) + SYSTEMD_VERSION="`$PKG_CONFIG --silence-errors --modversion libsystemd 2>/dev/null`" + if test "$?" != "0" -o -z "${SYSTEMD_VERSION}"; then + SYSTEMD_VERSION="none" + fi + AC_MSG_RESULT(${SYSTEMD_VERSION} found) + ], + [SYSTEMD_VERSION="none" + AC_MSG_NOTICE([can not check libsystemd settings via pkg-config]) + ] + ) + + AC_MSG_CHECKING(for libsystemd cflags) + AC_ARG_WITH(libsystemd-includes, + AS_HELP_STRING([@<:@--with-libsystemd-includes=CFLAGS@:>@], [include flags for the systemd library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-libsystemd-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], [ + dnl Not specifying a default include path here, + dnl headers are referenced by relative directory + dnl and these should be in OS location usually. + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags libsystemd 2>/dev/null`" || CFLAGS=""], + [CFLAGS=""] + )] + ) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for libsystemd ldflags) + AC_ARG_WITH(libsystemd-libs, + AS_HELP_STRING([@<:@--with-libsystemd-libs=LIBS@:>@], [linker flags for the systemd library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-libsystemd-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [LIBS="`$PKG_CONFIG --silence-errors --libs libsystemd 2>/dev/null`" || LIBS="-lsystemd"], + [LIBS="-lsystemd"] + )] + ) + AC_MSG_RESULT([${LIBS}]) + + dnl check if libsystemd is usable + AC_CHECK_HEADERS(systemd/sd-daemon.h, [nut_have_libsystemd=yes], [nut_have_libsystemd=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(sd_notify, [], [nut_have_libsystemd=no]) + + AS_IF([test x"${nut_have_libsystemd}" = x"yes"], [ + dnl Check for additional feature support in library (optional) + AC_CHECK_FUNCS(sd_booted sd_watchdog_enabled sd_notify_barrier) + LIBSYSTEMD_CFLAGS="${CFLAGS}" + LIBSYSTEMD_LIBS="${LIBS}" + ]) + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" +fi +]) diff --git a/m4/nut_check_libusb.m4 b/m4/nut_check_libusb.m4 index 89f7a02ac1..186e7c6e2f 100644 --- a/m4/nut_check_libusb.m4 +++ b/m4/nut_check_libusb.m4 @@ -14,6 +14,9 @@ if test -z "${nut_have_libusb_seen}"; then nut_have_libusb_seen=yes NUT_CHECK_PKGCONFIG + dnl Our USB matching relies on regex abilities + NUT_CHECK_LIBREGEX + dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" @@ -79,6 +82,18 @@ if test -z "${nut_have_libusb_seen}"; then AS_IF([test x"${LIBUSB_1_0_VERSION}" != xnone], [LIBUSB_VERSION="${LIBUSB_1_0_VERSION}" nut_usb_lib="(libusb-1.0)" + dnl ...except on Windows, where we support libusb-0.1(-compat) + dnl better so far (allow manual specification though, to let + dnl someone finally develop the on-par support): + AS_IF([test x"${LIBUSB_0_1_VERSION}" != xnone], [ + AS_CASE(["${target_os}"], + [*mingw*], [ + AC_MSG_NOTICE([mingw builds prefer libusb-0.1(-compat) if available]) + LIBUSB_VERSION="${LIBUSB_0_1_VERSION}" + nut_usb_lib="(libusb-0.1)" + ]) + ] + ) ], [AS_IF([test x"${LIBUSB_0_1_VERSION}" != xnone], [LIBUSB_VERSION="${LIBUSB_0_1_VERSION}" @@ -222,15 +237,39 @@ if test -z "${nut_have_libusb_seen}"; then fi else dnl libusb 0.1, or missing pkg-config : - AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [ + nut_have_libusb=no + dnl Per https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/ + dnl this project (used among alternatives in MSYS2/MinGW builds) + dnl uses a different include filename to avoid conflict with + dnl a WDK header: + AS_CASE(["${target_os}"], + [*mingw*], [ + AC_MSG_NOTICE([try alternate header name for mingw builds with libusb-win32]) + AC_CHECK_HEADERS(lusb0_usb.h, [ + nut_usb_lib="(libusb-0.1)" + nut_have_libusb=yes + ], [], [AC_INCLUDES_DEFAULT]) + ]) + ], + [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(usb_init, [], [ dnl Some systems may just have libusb in their standard dnl paths, but not the pkg-config or libusb-config data - AS_IF([test "${nut_have_libusb}" = "yes" && test "$LIBUSB_VERSION" = "none" && test -z "$LIBS"], + AS_IF([test "${nut_have_libusb}" = "yes" && test "$LIBUSB_VERSION" = "none" && test -z "$LIBS" -o x"$LIBS" = x"-lusb" ], [AC_MSG_CHECKING([if libusb is just present in path]) LIBS="-L/usr/lib -L/usr/local/lib -lusb" + dnl TODO: Detect bitness for trying /mingw32 or /usr/$ARCH as well? + dnl This currently caters to mingw-w64-x86_64-libusb-win32 of MSYS2: + AS_CASE(["${target_os}"], + [*mingw*], [LIBS="-L/mingw64/lib $LIBS"]) unset ac_cv_func_usb_init || true - AC_CHECK_FUNCS(usb_init, [], [nut_have_libusb=no]) + AC_CHECK_FUNCS(usb_init, [], [ + AC_MSG_CHECKING([if libusb0 is just present in path]) + LIBS="$LIBS"0 + unset ac_cv_func_usb_init || true + AC_CHECK_FUNCS(usb_init, [nut_usb_lib="(libusb-0.1)"], [nut_have_libusb=no]) + ]) AC_MSG_RESULT([${nut_have_libusb}]) ], [nut_have_libusb=no] )] @@ -250,6 +289,7 @@ if test -z "${nut_have_libusb_seen}"; then nut_have_libusb=no fi + nut_with_usb_busport=no AS_IF([test "${nut_have_libusb}" = "yes"], [ dnl ---------------------------------------------------------------------- dnl additional USB-related checks @@ -281,9 +321,24 @@ if test -z "${nut_have_libusb_seen}"; then CFLAGS="${CFLAGS} -lpthread" ] ) + + dnl AC_MSG_CHECKING([for libusb bus port support]) + dnl Per https://github.com/networkupstools/nut/issues/2043#issuecomment-1721856494 : + dnl #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) + dnl DEFINE WITH_USB_BUSPORT + dnl #endif + AC_CHECK_FUNCS(libusb_get_port_number, [nut_with_usb_busport=yes]) ]) AC_LANG_POP([C]) + AS_IF([test x"${nut_with_usb_busport}" = xyes], [ + AC_DEFINE(WITH_USB_BUSPORT, 1, + [Define to 1 for libusb versions where we can support "busport" USB matching value.]) + ], [ + AC_DEFINE(WITH_USB_BUSPORT, 0, + [Define to 1 for libusb versions where we can support "busport" USB matching value.]) + ]) + AS_IF([test "${nut_have_libusb}" = "yes"], [ LIBUSB_CFLAGS="${CFLAGS}" LIBUSB_LIBS="${LIBS}" diff --git a/m4/nut_check_pkgconfig.m4 b/m4/nut_check_pkgconfig.m4 index 263be4bed5..fd9a2449ef 100644 --- a/m4/nut_check_pkgconfig.m4 +++ b/m4/nut_check_pkgconfig.m4 @@ -27,7 +27,7 @@ AS_IF([test -z "${nut_have_pkg_config_seen}"], [ AC_PATH_PROG(dummy_PKG_CONFIG, pkg-config) AC_ARG_WITH(pkg-config, - AS_HELP_STRING([@<:@--with-pkg-config=/path/to/gdlib-config@:>@], + AS_HELP_STRING([--with-pkg-config=/path/to/pkg-config], [path to program that reports development package configuration]), [ case "${withval}" in diff --git a/m4/nut_check_python.m4 b/m4/nut_check_python.m4 index 99843d1f71..b44a5cd5bb 100644 --- a/m4/nut_check_python.m4 +++ b/m4/nut_check_python.m4 @@ -1,12 +1,52 @@ dnl Check for python binary program names per language version dnl to embed into scripts and Make rules +AC_DEFUN([NUT_CHECK_PYTHON_DEFAULT], +[ + dnl Check for all present variants and pick the default PYTHON + AC_REQUIRE([NUT_CHECK_PYTHON]) + AC_REQUIRE([NUT_CHECK_PYTHON2]) + AC_REQUIRE([NUT_CHECK_PYTHON3]) + + AS_IF([test x"$PYTHON2" = xno], [PYTHON2=""]) + AS_IF([test x"$PYTHON3" = xno], [PYTHON3=""]) + AS_IF([test x"$PYTHON" = xno], [PYTHON=""]) + AS_IF([test x"$PYTHON" = x], [ + AC_MSG_CHECKING([which python version to use by default]) + dnl Last hit wins (py3) + AS_IF([test x"$PYTHON2" != x], [PYTHON="$PYTHON2"]) + AS_IF([test x"$PYTHON3" != x], [PYTHON="$PYTHON3"]) + AS_IF([test x"$PYTHON" = x], + [AC_MSG_RESULT([none])], + [AC_MSG_RESULT([$PYTHON]) + AC_MSG_WARN([A python program name was not specified during configuration, will default to '$PYTHON' (derived from --with-python2 or --with-python3 setting)]) + ]) + ]) + + AS_IF([test -z "${PYTHON3}" && test x"${nut_with_python3}" = xyes], [ + AC_MSG_ERROR([A python3 interpreter was required but not found or validated]) + ]) + + AS_IF([test -z "${PYTHON2}" && test x"${nut_with_python2}" = xyes], [ + AC_MSG_ERROR([A python2 interpreter was required but not found or validated]) + ]) + + AS_IF([test -z "${PYTHON}" && test x"${nut_with_python}" = xyes], [ + AC_MSG_ERROR([A python interpreter was required but not found or validated]) + ]) +]) + +dnl Note: this checks for default/un-versioned python version +dnl as the --with-python=SHEBANG_PATH setting into the PYTHON +dnl variable; it may be further tweaked by NUT_CHECK_PYTHON_DEFAULT AC_DEFUN([NUT_CHECK_PYTHON], [ AS_IF([test -z "${nut_with_python}"], [ NUT_ARG_WITH([python], [Use a particular program name of the python interpeter], [auto]) PYTHON="" + PYTHON_SITE_PACKAGES="" + PYTHON_VERSION_REPORT="" AS_CASE([${nut_with_python}], [auto|yes|""], [AC_CHECK_PROGS([PYTHON], [python python3 python2], [_python_runtime])], [no], [PYTHON="no"], @@ -32,14 +72,62 @@ AC_DEFUN([NUT_CHECK_PYTHON], AC_MSG_WARN([A python program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) PYTHON="/usr/bin/env ${PYTHON}" ], - [*], [PYTHON="/usr/bin/env ${PYTHON}"] + [*], [ + dnl Note: no "realpath" here, see comment below + myPYTHON="`command -v "${PYTHON}" 2>/dev/null`" && test -n "${myPYTHON}" && test -x "${myPYTHON}" \ + && PYTHON="${myPYTHON}" \ + || PYTHON="/usr/bin/env ${PYTHON}" + unset myPYTHON + ] ) + dnl Note: requesting e.g. "--with-python=python3" is valid, + dnl but would likely use a symlink that changes over time - + dnl and if `env` gets used, can resolve according to PATH + dnl (by default we try to bolt pathname here if resolvable, + dnl but do not unwrap the chain of symlinks like we do for + dnl versioned "--with-python2/3" due to their site-packages). + dnl For some use-cases, this lack of constraints may be + dnl deliberately desired; for others it is a "caveat emptor!" + AS_CASE(["${PYTHON}"], + [*2.*|*3.*], [], + [AC_MSG_WARN([A python program name without a specific version number was requested (may be a symlink prone to change over time): ${PYTHON}])]) + + AS_CASE(["${PYTHON}"], + [/usr/bin/env*], [AC_MSG_WARN([A python program will be resolved from PATH at run-time (PyNUT module may be not found if installed into site-packages of a specific version): ${PYTHON}])]) + + AS_IF([test -n "${PYTHON}" && test "${PYTHON}" != "no"], [ + AS_IF([test x"`$PYTHON -c 'import sys; print (sys.version_info >= (2, 6))'`" = xTrue], + [PYTHON_VERSION_REPORT=" (`$PYTHON -c 'import sys; print (sys.version_info)'`)"], + [AC_MSG_WARN([Version reported by ${PYTHON} was not suitable as python]) + PYTHON=no]) + ]) + + dnl Unfulfilled "yes" is re-tested in NUT_CHECK_PYTHON_DEFAULT + AS_IF([test -z "${PYTHON}" || test "${PYTHON}" = "no"], [ + AS_CASE([${nut_with_python}], + [auto|yes|no|""], [], + [AC_MSG_ERROR([A python interpreter was required but not found or validated: ${nut_with_python}])]) + ]) + AC_MSG_CHECKING([python interpeter to call]) - AC_MSG_RESULT([${PYTHON}]) + AC_MSG_RESULT([${PYTHON}${PYTHON_VERSION_REPORT}]) AC_SUBST([PYTHON], [${PYTHON}]) - AM_CONDITIONAL([HAVE_PYTHON], [test "${PYTHON}" != "no"]) - AS_IF([test -n "${PYTHON}"], [export PYTHON]) + AM_CONDITIONAL([HAVE_PYTHON], [test -n "${PYTHON}" && test "${PYTHON}" != "no"]) + AS_IF([test -n "${PYTHON}" && test "${PYTHON}" != "no"], [ + export PYTHON + AC_CACHE_CHECK([python site-packages location], [nut_cv_PYTHON_SITE_PACKAGES], [ + nut_cv_PYTHON_SITE_PACKAGES="`${PYTHON} -c 'import site; print(site.getsitepackages().pop(0))'`" + AS_CASE(["$nut_cv_PYTHON_SITE_PACKAGES"], + [*:*], [ + dnl Note: on Windows MSYS2 this embeds "C:/msys64/mingw..." into the string [nut#1584] + nut_cv_PYTHON_SITE_PACKAGES="`cd "$nut_cv_PYTHON_SITE_PACKAGES" && pwd`" + ] + ) + ]) + ]) + AC_SUBST([PYTHON_SITE_PACKAGES], [${nut_cv_PYTHON_SITE_PACKAGES}]) + AM_CONDITIONAL([HAVE_PYTHON_SITE_PACKAGES], [test x"${PYTHON_SITE_PACKAGES}" != "x"]) ]) ]) @@ -49,8 +137,45 @@ AC_DEFUN([NUT_CHECK_PYTHON2], NUT_ARG_WITH([python2], [Use a particular program name of the python2 interpeter for code that needs that version and is not compatible with python3], [auto]) PYTHON2="" + PYTHON2_SITE_PACKAGES="" + PYTHON2_VERSION_REPORT="" AS_CASE([${nut_with_python2}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python], [_python2_runtime])], + [auto|yes|""], [ + dnl Cross check --with-python results: + AS_CASE(["${PYTHON_VERSION_REPORT}"], + [*major=2,*], [ + PYTHON2="`${PYTHON} -c 'import sys; print(sys.executable);' 2>/dev/null`" && test -n "${PYTHON2}" || PYTHON2="${PYTHON}" + PYTHON2="`realpath "${PYTHON2}" 2>/dev/null`" && test -n "${PYTHON2}" || { + PYTHON2="${PYTHON}" + PYTHON_CONFIG="`command -v "${PYTHON}-config" 2>/dev/null`" || PYTHON_CONFIG="" + if test -n "${PYTHON_CONFIG}" ; then + mySHEBANG_SCRIPT="`${PYTHON_CONFIG} --config-dir 2>/dev/null`/python-config.py" \ + || mySHEBANG_SCRIPT="${PYTHON_CONFIG}" + if test -f "${mySHEBANG_SCRIPT}" ; then + mySHEBANG="`head -1 "${mySHEBANG_SCRIPT}" | grep -E '^#!'`" || mySHEBANG="" + if test -n "${mySHEBANG}" ; then + PYTHON2="`echo "${mySHEBANG}" | sed 's,^#! *,,'`" \ + && test -n "${PYTHON2}" || PYTHON2="${PYTHON}" + fi + fi + fi + unset mySHEBANG_SCRIPT + unset mySHEBANG + unset PYTHON_CONFIG + } + + dnl Only accept fully qualified names that refer to expected + dnl python version or quietly fall back to search below: + AS_CASE(["${PYTHON2}"], + [/usr/bin/env*], [PYTHON2=""], + [/*py*2.*], [AC_MSG_WARN([A python2 program name was not specified during configuration, will default to '$PYTHON2' (derived from --with-python setting which has a suitable version)])], + [/*py*2*], [AC_MSG_WARN([A python2 program name was not specified during configuration, will default to '$PYTHON2' (derived from --with-python setting which has a suitable version, but without a specific version number - so may be a symlink prone to change over time)])], + [PYTHON2=""]) + ]) + AS_IF([test x"${PYTHON2}" = x], [ + AC_CHECK_PROGS([PYTHON2], [python2 python2.7 python-2.7 python], [_python2_runtime]) + ]) + ], [no], [PYTHON2="no"], [PYTHON2="${nut_with_python2}"] ) @@ -60,7 +185,8 @@ AC_DEFUN([NUT_CHECK_PYTHON2], AS_CASE([${PYTHON2}], [_python2_runtime], [ PYTHON2="/usr/bin/env python2" - AC_MSG_WARN([A python2 program name was not detected during configuration, will default to '$PYTHON2' (scripts will fail if that is not in PATH at run time)])], + AC_MSG_WARN([A python2 program name was not detected during configuration, will default to '$PYTHON2' (scripts will fail if that is not in PATH at run time)]) + ], [no], [], [/*" "*" "*], [ AC_MSG_WARN([A python2 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) @@ -74,14 +200,46 @@ AC_DEFUN([NUT_CHECK_PYTHON2], AC_MSG_WARN([A python2 program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) PYTHON2="/usr/bin/env ${PYTHON2}" ], - [*], [PYTHON2="/usr/bin/env ${PYTHON2}"] + [*], [ + myPYTHON="`command -v "${PYTHON2}" 2>/dev/null`" && test -n "${myPYTHON}" && test -x "${myPYTHON}" \ + && PYTHON2="${myPYTHON}" \ + || PYTHON2="/usr/bin/env ${PYTHON2}" + unset myPYTHON + ] ) + AS_IF([test -n "${PYTHON2}" && test "${PYTHON2}" != "no"], [ + AS_IF([test x"`$PYTHON2 -c 'import sys; print (sys.version_info >= (2, 6) and sys.version_info < (3, 0))'`" = xTrue], + [PYTHON2_VERSION_REPORT=" (`$PYTHON2 -c 'import sys; print (sys.version_info)'`)"], + [AC_MSG_WARN([Version reported by ${PYTHON2} was not suitable as python2]) + PYTHON2=no]) + ]) + + dnl Unfulfilled "yes" is re-tested in NUT_CHECK_PYTHON_DEFAULT + AS_IF([test -z "${PYTHON2}" || test "${PYTHON2}" = "no"], [ + AS_CASE([${nut_with_python2}], + [auto|yes|no|""], [], + [AC_MSG_ERROR([A python2 interpreter was required but not found or validated: ${nut_with_python2}])]) + ]) + AC_MSG_CHECKING([python2 interpeter to call]) - AC_MSG_RESULT([${PYTHON2}]) + AC_MSG_RESULT([${PYTHON2}${PYTHON2_VERSION_REPORT}]) AC_SUBST([PYTHON2], [${PYTHON2}]) - AM_CONDITIONAL([HAVE_PYTHON2], [test "${PYTHON2}" != "no"]) - AS_IF([test -n "${PYTHON2}"], [export PYTHON2]) + AM_CONDITIONAL([HAVE_PYTHON2], [test -n "${PYTHON2}" && test "${PYTHON2}" != "no"]) + AS_IF([test -n "${PYTHON2}" && test "${PYTHON2}" != "no"], [ + export PYTHON2 + AC_CACHE_CHECK([python2 site-packages location], [nut_cv_PYTHON2_SITE_PACKAGES], [ + nut_cv_PYTHON2_SITE_PACKAGES="`${PYTHON2} -c 'import site; print(site.getsitepackages().pop(0))'`" + AS_CASE(["$nut_cv_PYTHON2_SITE_PACKAGES"], + [*:*], [ + dnl Note: on Windows MSYS2 this embeds "C:/msys64/mingw..." into the string [nut#1584] + nut_cv_PYTHON2_SITE_PACKAGES="`cd "$nut_cv_PYTHON2_SITE_PACKAGES" && pwd`" + ] + ) + ]) + ]) + AC_SUBST([PYTHON2_SITE_PACKAGES], [${nut_cv_PYTHON2_SITE_PACKAGES}]) + AM_CONDITIONAL([HAVE_PYTHON2_SITE_PACKAGES], [test x"${PYTHON2_SITE_PACKAGES}" != "x"]) ]) ]) @@ -91,8 +249,45 @@ AC_DEFUN([NUT_CHECK_PYTHON3], NUT_ARG_WITH([python3], [Use a particular program name of the python3 interpeter for code that needs that version and is not compatible with python2], [auto]) PYTHON3="" + PYTHON3_SITE_PACKAGES="" + PYTHON3_VERSION_REPORT="" AS_CASE([${nut_with_python3}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON3], [python3 python], [_python3_runtime])], + [auto|yes|""], [ + dnl Cross check --with-python results: + AS_CASE(["${PYTHON_VERSION_REPORT}"], + [*major=3,*], [ + PYTHON3="`${PYTHON} -c 'import sys; print(sys.executable);' 2>/dev/null`" && test -n "${PYTHON3}" || PYTHON3="${PYTHON}" + PYTHON3="`realpath "${PYTHON3}" 2>/dev/null`" && test -n "${PYTHON3}" || { + PYTHON3="${PYTHON}" + PYTHON_CONFIG="`command -v "${PYTHON}-config" 2>/dev/null`" || PYTHON_CONFIG="" + if test -n "${PYTHON_CONFIG}" ; then + mySHEBANG_SCRIPT="`${PYTHON_CONFIG} --config-dir 2>/dev/null`/python-config.py" \ + || mySHEBANG_SCRIPT="${PYTHON_CONFIG}" + if test -f "${mySHEBANG_SCRIPT}" ; then + mySHEBANG="`head -1 "${mySHEBANG_SCRIPT}" | grep -E '^#!'`" || mySHEBANG="" + if test -n "${mySHEBANG}" ; then + PYTHON3="`echo "${mySHEBANG}" | sed 's,^#! *,,'`" \ + && test -n "${PYTHON3}" || PYTHON3="${PYTHON}" + fi + fi + fi + unset mySHEBANG_SCRIPT + unset mySHEBANG + unset PYTHON_CONFIG + } + + dnl Only accept fully qualified names that refer to expected + dnl python version or quietly fall back to search below: + AS_CASE(["${PYTHON3}"], + [/usr/bin/env*], [PYTHON3=""], + [/*py*3.*], [AC_MSG_WARN([A python3 program name was not specified during configuration, will default to '$PYTHON3' (derived from --with-python setting which has a suitable version)])], + [/*py*3*], [AC_MSG_WARN([A python3 program name was not specified during configuration, will default to '$PYTHON3' (derived from --with-python setting which has a suitable version, but without a specific version number - so may be a symlink prone to change over time)])], + [PYTHON3=""]) + ]) + AS_IF([test x"${PYTHON3}" = x], [ + AC_CHECK_PROGS([PYTHON3], [python3 python3.14 python-3.14 python3.13 python-3.13 python3.12 python-3.12 python3.11 python-3.11 python3.10 python-3.10 python3.9 python-3.9 python3.7 python-3.7 python3.6 python-3.6 python3.5 python-3.5 python], [_python3_runtime]) + ]) + ], [no], [PYTHON3="no"], [PYTHON3="${nut_with_python3}"] ) @@ -102,7 +297,8 @@ AC_DEFUN([NUT_CHECK_PYTHON3], AS_CASE([${PYTHON3}], [_python3_runtime], [ PYTHON3="/usr/bin/env python3" - AC_MSG_WARN([A python3 program name was not detected during configuration, will default to '$PYTHON3' (scripts will fail if that is not in PATH at run time)])], + AC_MSG_WARN([A python3 program name was not detected during configuration, will default to '$PYTHON3' (scripts will fail if that is not in PATH at run time)]) + ], [no], [], [/*" "*" "*], [ AC_MSG_WARN([A python3 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) @@ -116,13 +312,45 @@ AC_DEFUN([NUT_CHECK_PYTHON3], AC_MSG_WARN([A python3 program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) PYTHON3="/usr/bin/env ${PYTHON3}" ], - [*], [PYTHON3="/usr/bin/env ${PYTHON3}"] + [*], [ + myPYTHON="`command -v "${PYTHON3}" 2>/dev/null`" && test -n "${myPYTHON}" && test -x "${myPYTHON}" \ + && PYTHON3="${myPYTHON}" \ + || PYTHON3="/usr/bin/env ${PYTHON3}" + unset myPYTHON + ] ) + AS_IF([test -n "${PYTHON3}" && test "${PYTHON3}" != "no"], [ + AS_IF([test x"`$PYTHON3 -c 'import sys; print (sys.version_info >= (3, 0))'`" = xTrue], + [PYTHON3_VERSION_REPORT=" (`$PYTHON3 -c 'import sys; print (sys.version_info)'`)"], + [AC_MSG_WARN([Version reported by ${PYTHON3} was not suitable as python3]) + PYTHON3=no]) + ]) + + dnl Unfulfilled "yes" is re-tested in NUT_CHECK_PYTHON_DEFAULT + AS_IF([test -z "${PYTHON3}" || test "${PYTHON3}" = "no"], [ + AS_CASE([${nut_with_python3}], + [auto|yes|no|""], [], + [AC_MSG_ERROR([A python3 interpreter was required but not found or validated: ${nut_with_python3}])]) + ]) + AC_MSG_CHECKING([python3 interpeter to call]) - AC_MSG_RESULT([${PYTHON3}]) + AC_MSG_RESULT([${PYTHON3}${PYTHON3_VERSION_REPORT}]) AC_SUBST([PYTHON3], [${PYTHON3}]) - AM_CONDITIONAL([HAVE_PYTHON3], [test "${PYTHON3}" != "no"]) - AS_IF([test -n "${PYTHON3}"], [export PYTHON3]) + AM_CONDITIONAL([HAVE_PYTHON3], [test -n "${PYTHON3}" && test "${PYTHON3}" != "no"]) + AS_IF([test -n "${PYTHON3}" && test "${PYTHON3}" != "no"], [ + export PYTHON3 + AC_CACHE_CHECK([python3 site-packages location], [nut_cv_PYTHON3_SITE_PACKAGES], [ + nut_cv_PYTHON3_SITE_PACKAGES="`${PYTHON3} -c 'import site; print(site.getsitepackages().pop(0))'`" + AS_CASE(["$nut_cv_PYTHON3_SITE_PACKAGES"], + [*:*], [ + dnl Note: on Windows MSYS2 this embeds "C:/msys64/mingw..." into the string [nut#1584] + nut_cv_PYTHON3_SITE_PACKAGES="`cd "$nut_cv_PYTHON3_SITE_PACKAGES" && pwd`" + ] + ) + ]) + ]) + AC_SUBST([PYTHON3_SITE_PACKAGES], [${nut_cv_PYTHON3_SITE_PACKAGES}]) + AM_CONDITIONAL([HAVE_PYTHON3_SITE_PACKAGES], [test x"${PYTHON3_SITE_PACKAGES}" != "x"]) ]) ]) diff --git a/m4/nut_check_socketlib.m4 b/m4/nut_check_socketlib.m4 new file mode 100644 index 0000000000..65df958516 --- /dev/null +++ b/m4/nut_check_socketlib.m4 @@ -0,0 +1,41 @@ +dnl Copyright (C) 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl NUT_CHECK_SOCKETLIB +dnl Determines the library to use for socket functions. +dnl Sets and AC_SUBSTs NETLIBS. + +dnl This code comes from gnulib trunk (4 Nov. 2010). + +AC_DEFUN([NUT_CHECK_SOCKETLIB], +[ + NUT_PREREQ_SYS_H_SOCKET dnl for HAVE_WINSOCK2_H + NETLIBS= + if test $HAVE_WINSOCK2_H = 1; then + dnl Native Windows API (not Cygwin). + AC_CACHE_CHECK([if we need to call WSAStartup in winsock2.h and -lws2_32], + [nut_cv_func_wsastartup], [ + nut_save_LIBS="$LIBS" + LIBS="$LIBS -lws2_32" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_WINSOCK2_H +# include +#endif]], [[ + WORD wVersionRequested = MAKEWORD(1, 1); + WSADATA wsaData; + int err = WSAStartup(wVersionRequested, &wsaData); + WSACleanup ();]])], + nut_cv_func_wsastartup=yes, nut_cv_func_wsastartup=no) + LIBS="$nut_save_LIBS" + ]) + if test "$nut_cv_func_wsastartup" = "yes"; then + AC_DEFINE([WINDOWS_SOCKETS], [1], [Define if WSAStartup is needed.]) + NETLIBS='-lws2_32' + fi + fi + AC_SUBST([NETLIBS]) + + AM_CONDITIONAL([HAVE_WINDOWS_SOCKETS], [test "$nut_cv_func_wsastartup" = "yes"]) +]) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index 4204b3c079..361a77a444 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -2,65 +2,99 @@ dnl detect if current compiler is clang or gcc (or...) AC_DEFUN([NUT_COMPILER_FAMILY], [ + CC_VERSION_FULL="`LANG=C LC_ALL=C $CC --version 2>&1`" || CC_VERSION_FULL="" + CXX_VERSION_FULL="`LANG=C LC_ALL=C $CXX --version 2>&1`" || CXX_VERSION_FULL="" + CPP_VERSION_FULL="`LANG=C LC_ALL=C $CPP --version 2>&1`" || CPP_VERSION_FULL="" + CC_VERSION="" + CXX_VERSION="" + CPP_VERSION="" + AC_CACHE_CHECK([if CC compiler family is GCC], [nut_cv_GCC], - [AS_IF([test -n "$CC"], - [AS_IF([$CC --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [AS_IF([test -n "$CC" && test -n "$CC_VERSION_FULL"], + [AS_IF([echo "${CC_VERSION_FULL}" | grep 'Free Software Foundation' > /dev/null], [nut_cv_GCC=yes],[nut_cv_GCC=no])], [AC_MSG_ERROR([CC is not set])] )]) AC_CACHE_CHECK([if CXX compiler family is GCC], [nut_cv_GXX], - [AS_IF([test -n "$CXX"], - [AS_IF([$CXX --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [AS_IF([test -n "$CXX" && test -n "$CXX_VERSION_FULL"], + [AS_IF([echo "${CXX_VERSION_FULL}" | grep 'Free Software Foundation' > /dev/null], [nut_cv_GXX=yes],[nut_cv_GXX=no])], [AC_MSG_ERROR([CXX is not set])] )]) AC_CACHE_CHECK([if CPP preprocessor family is GCC], [nut_cv_GPP], - [AS_IF([test -n "$CPP"], - [AS_IF([$CPP --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [AS_IF([test -n "$CPP" && test -n "$CPP_VERSION_FULL"], + [AS_IF([echo "${CPP_VERSION_FULL}" | grep 'Free Software Foundation' > /dev/null], [nut_cv_GPP=yes],[nut_cv_GPP=no])], [AC_MSG_ERROR([CPP is not set])] )]) - AS_IF([test "x$GCC" = "x" && test "$nut_cv_GCC" = yes], [GCC=yes]) - AS_IF([test "x$GXX" = "x" && test "$nut_cv_GXX" = yes], [GXX=yes]) - AS_IF([test "x$GPP" = "x" && test "$nut_cv_GPP" = yes], [GPP=yes]) + AS_IF([test "x$GCC" = "x" && test "$nut_cv_GCC" = yes], [GCC=yes + CC_VERSION="`echo "${CC_VERSION_FULL}" | grep -i gcc | head -1`" \ + && test -n "${CC_VERSION}" || CC_VERSION="" + ]) + AS_IF([test "x$GXX" = "x" && test "$nut_cv_GXX" = yes], [GXX=yes + CXX_VERSION="`echo "${CXX_VERSION_FULL}" | grep -i -E 'g++|gcc' | head -1`" \ + && test -n "${CXX_VERSION}" || CXX_VERSION="" + ]) + AS_IF([test "x$GPP" = "x" && test "$nut_cv_GPP" = yes], [GPP=yes + CPP_VERSION="`echo "${CPP_VERSION_FULL}" | grep -i -E 'cpp|gcc' | head -1`" \ + && test -n "${CPP_VERSION}" || CPP_VERSION="" + ]) AC_CACHE_CHECK([if CC compiler family is clang], [nut_cv_CLANGCC], - [AS_IF([test -n "$CC"], - [AS_IF([$CC --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [AS_IF([test -n "$CC" && test -n "$CC_VERSION_FULL"], + [AS_IF([echo "${CC_VERSION_FULL}" | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], [nut_cv_CLANGCC=yes],[nut_cv_CLANGCC=no])], [AC_MSG_ERROR([CC is not set])] )]) AC_CACHE_CHECK([if CXX compiler family is clang], [nut_cv_CLANGXX], - [AS_IF([test -n "$CXX"], - [AS_IF([$CXX --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [AS_IF([test -n "$CXX" && test -n "$CXX_VERSION_FULL"], + [AS_IF([echo "${CXX_VERSION_FULL}" | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], [nut_cv_CLANGXX=yes],[nut_cv_CLANGXX=no])], [AC_MSG_ERROR([CXX is not set])] )]) AC_CACHE_CHECK([if CPP preprocessor family is clang], [nut_cv_CLANGPP], - [AS_IF([test -n "$CPP"], - [AS_IF([$CPP --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [AS_IF([test -n "$CPP" && test -n "$CPP_VERSION_FULL"], + [AS_IF([echo "${CPP_VERSION_FULL}" | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], [nut_cv_CLANGPP=yes],[nut_cv_CLANGPP=no])], [AC_MSG_ERROR([CPP is not set])] )]) - AS_IF([test "x$CLANGCC" = "x" && test "$nut_cv_CLANGCC" = yes], [CLANGCC=yes]) - AS_IF([test "x$CLANGXX" = "x" && test "$nut_cv_CLANGXX" = yes], [CLANGXX=yes]) - AS_IF([test "x$CLANGPP" = "x" && test "$nut_cv_CLANGPP" = yes], [CLANGPP=yes]) + AS_IF([test "x$CLANGCC" = "x" && test "$nut_cv_CLANGCC" = yes], [CLANGCC=yes + CC_VERSION="`echo "${CC_VERSION_FULL}" | grep -v "Dir:" | tr '\n' ';' | sed -e 's, *;,;,g' -e 's,;$,,' -e 's,;,; ,g'`" \ + && test -n "${CC_VERSION}" || CC_VERSION="" + ]) + AS_IF([test "x$CLANGXX" = "x" && test "$nut_cv_CLANGXX" = yes], [CLANGXX=yes + CXX_VERSION="`echo "${CXX_VERSION_FULL}" | grep -v "Dir:" | tr '\n' ';' | sed -e 's, *;,;,g' -e 's,;$,,' -e 's,;,; ,g'`" \ + && test -n "${CXX_VERSION}" || CXX_VERSION="" + ]) + AS_IF([test "x$CLANGPP" = "x" && test "$nut_cv_CLANGPP" = yes], [CLANGPP=yes + CPP_VERSION="`echo "${CPP_VERSION_FULL}" | grep -v "Dir:" | tr '\n' ';' | sed -e 's, *;,;,g' -e 's,;$,,' -e 's,;,; ,g'`" \ + && test -n "${CPP_VERSION}" || CPP_VERSION="" + ]) + + AS_IF([test "x$CC_VERSION" = x], [CC_VERSION="`echo "${CC_VERSION_FULL}" | head -1`"]) + AS_IF([test "x$CXX_VERSION" = x], [CXX_VERSION="`echo "${CXX_VERSION_FULL}" | head -1`"]) + AS_IF([test "x$CPP_VERSION" = x], [CPP_VERSION="`echo "${CPP_VERSION_FULL}" | head -1`"]) ]) 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 +107,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 +115,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 +136,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 +174,26 @@ 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$cross_compiling" != xyes], [ + 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 @@ -160,7 +207,7 @@ dnl # Some distributions and platforms also have problems dnl # building in "strict C" mode, so for the GNU-compatible dnl # compilers we default to the GNU C/C++ dialects. AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], - [AS_CASE(["${CFLAGS}"], [-std=*], [], + [AS_CASE(["${CFLAGS}"], [*"-std="*|*"-ansi"*], [], [AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([-std=gnu99], [AC_MSG_NOTICE([Defaulting C standard support to GNU C99 on a GCC or CLANG compatible compiler]) @@ -173,7 +220,7 @@ dnl # compilers we default to the GNU C/C++ dialects. dnl # Note: this might upset some very old compilers dnl # but then by default we wouldn't build C++ parts AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], - [AS_CASE(["${CXXFLAGS}"], [-std=*], [], + [AS_CASE(["${CXXFLAGS}"], [*"-std="*|*"-ansi"*], [], [AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-std=gnu++11], [AC_MSG_NOTICE([Defaulting C++ standard support to GNU C++11 on a GCC or CLANG compatible compiler]) @@ -199,7 +246,7 @@ dnl # Some distributions and platforms also have problems dnl # building in "strict C" mode, so for the GNU-compatible dnl # compilers we default to the GNU C/C++ dialects. AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], - [AS_CASE(["${CFLAGS}"], [*-std=*], [], + [AS_CASE(["${CFLAGS}"], [*"-std="*|*"-ansi"*], [], [AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([-std=gnu99], [AC_MSG_NOTICE([Defaulting C standard support to GNU C99 on a GCC or CLANG compatible compiler]) @@ -212,7 +259,7 @@ dnl # compilers we default to the GNU C/C++ dialects. dnl # Note: this might upset some very old compilers dnl # but then by default we wouldn't build C++ parts AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], - [AS_CASE(["${CXXFLAGS}"], [*-std=*], [], + [AS_CASE(["${CXXFLAGS}"], [*"-std="*|*"-ansi"*], [], [AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-std=gnu++11], [AC_MSG_NOTICE([Defaulting C++ standard support to GNU C++11 on a GCC or CLANG compatible compiler]) diff --git a/m4/nut_func_getnameinfo_argtypes.m4 b/m4/nut_func_getnameinfo_argtypes.m4 index 8f32c42efe..d71b90184f 100644 --- a/m4/nut_func_getnameinfo_argtypes.m4 +++ b/m4/nut_func_getnameinfo_argtypes.m4 @@ -23,7 +23,7 @@ AC_DEFUN([NUT_FUNC_GETNAMEINFO_ARGTYPES], [ nut_cv_func_getnameinfo_args="unknown" for gni_arg1 in 'const struct sockaddr *' 'struct sockaddr *' 'void *'; do for gni_arg2 in 'socklen_t' 'size_t' 'int'; do - for gni_arg46 in 'socklen_t' 'size_t' 'int' 'unsigned int'; do + for gni_arg46 in 'socklen_t' 'size_t' 'int' 'unsigned int' 'DWORD'; do for gni_arg7 in 'int' 'unsigned int'; do AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([ diff --git a/m4/nut_report_feature.m4 b/m4/nut_report_feature.m4 index 72667ba660..02ff814033 100644 --- a/m4/nut_report_feature.m4 +++ b/m4/nut_report_feature.m4 @@ -4,39 +4,172 @@ dnl for example, "usb" (--with-usb) will give dnl nut_with_usb and WITH_USB (both macros, and dnl AM_CONDITIONAL) +AC_DEFUN([NUT_REPORT_FILE], +[ + dnl arg#1 = description (summary) + dnl arg#2 = value + dnl arg#3 = file tag (e.g. number) + dnl arg#4 = file title (e.g. "NUT Configuration summary:") + AS_IF([test x"${nut_report_feature_flag$3}" = x], [ + nut_report_feature_flag$3="1" + ac_clean_files="${ac_clean_files} config.nut_report_feature.log.$3" + if test x1 = "x$3" ; then + echo "$4" + echo "$4" | sed 's/./=/g' + echo "" + else + echo "" + echo "$4" + echo "$4" | sed 's/./-/g' + echo "" + fi > "config.nut_report_feature.log.$3" + ]) + printf "* %s:\t%s\n" "$1" "$2" >> "config.nut_report_feature.log.$3" +]) + AC_DEFUN([NUT_REPORT], -[ if test -z "${nut_report_feature_flag}"; then - nut_report_feature_flag="1" - ac_clean_files="${ac_clean_files} conf_nut_report_feature" - echo > conf_nut_report_feature - echo "Configuration summary:" >> conf_nut_report_feature - echo "======================" >> conf_nut_report_feature - fi - echo "$1: $2" >> conf_nut_report_feature +[ + dnl arg#1 = description (summary) + dnl arg#2 = value + NUT_REPORT_FILE([$1], [$2], [1], "NUT Configuration summary:") +]) + +AC_DEFUN([NUT_REPORT_PATH], +[ + dnl arg#1 = description (summary) + dnl arg#2 = value + NUT_REPORT_FILE([$1], [$2], [2], "NUT Paths:") +]) + +AC_DEFUN([NUT_REPORT_PATH_INTEGRATIONS], +[ + dnl arg#1 = description (summary) + dnl arg#2 = value + NUT_REPORT_FILE([$1], [$2], [3], "NUT Paths for third-party integrations:") ]) AC_DEFUN([NUT_REPORT_FEATURE], [ - AC_MSG_CHECKING([whether to $1]) - AC_MSG_RESULT([$2 $3]) - NUT_REPORT([$1], [$2 $3]) + dnl arg#1 = summary/config.log description + dnl arg#2 = test flag ("yes" or not) + dnl arg#3 = value + dnl arg#4 = autoconf varname + dnl arg#5 = longer description (autoconf comment) + AC_MSG_CHECKING([whether to $1]) + AC_MSG_RESULT([$2 $3]) + NUT_REPORT([$1], [$2 $3]) - AM_CONDITIONAL([$4], test "$2" = "yes") - if test "$2" = "yes"; then - AC_DEFINE_UNQUOTED($4, 1, $5) - fi + AM_CONDITIONAL([$4], test "$2" = "yes") + AS_IF([test x"$2" = x"yes"], [ + AC_DEFINE_UNQUOTED($4, 1, $5) + ]) ]) -AC_DEFUN([NUT_PRINT_FEATURE_REPORT], +AC_DEFUN([NUT_REPORT_SETTING], +[ + dnl arg#1 = summary/config.log description + dnl arg#2 = autoconf varname + dnl arg#3 = value + dnl arg#4 = longer description (autoconf comment) + AC_MSG_CHECKING([setting for $1]) + AC_MSG_RESULT([$3]) + NUT_REPORT([$1], [$3]) + + dnl Note: unlike features, settings do not imply an AutoMake toggle + AC_DEFINE_UNQUOTED($2, $3, $4) +]) + +AC_DEFUN([NUT_REPORT_SETTING_PATH], [ - cat conf_nut_report_feature + dnl arg#1 = summary/config.log description + dnl arg#2 = autoconf varname + dnl arg#3 = value + dnl arg#4 = longer description (autoconf comment) + AC_MSG_CHECKING([setting for $1]) + AC_MSG_RESULT([$3]) + NUT_REPORT_PATH([$1], [$3]) - echo "------------------" - echo "Compiler settings:" - printf 'CC \t:%s\n' "$CC" - printf 'CFLAGS \t:%s\n' "$CFLAGS" - printf 'CXX \t:%s\n' "$CXX" - printf 'CXXFLAGS\t:%s\n' "$CXXFLAGS" - printf 'CPP \t:%s\n' "$CPP" - printf 'CPPFLAGS\t:%s\n' "$CPPFLAGS" + dnl Note: unlike features, settings do not imply an AutoMake toggle + AC_DEFINE_UNQUOTED($2, $3, $4) +]) + +AC_DEFUN([NUT_REPORT_SETTING_PATH_INTEGRATIONS], +[ + dnl arg#1 = summary/config.log description + dnl arg#2 = autoconf varname + dnl arg#3 = value + dnl arg#4 = longer description (autoconf comment) + AC_MSG_CHECKING([setting for $1]) + AC_MSG_RESULT([$3]) + NUT_REPORT_PATH_INTEGRATIONS([$1], [$3]) + + dnl Note: unlike features, settings do not imply an AutoMake toggle + AC_DEFINE_UNQUOTED($2, $3, $4) +]) + +AC_DEFUN([NUT_REPORT_TARGET], +[ + dnl arg#1 = autoconf varname + dnl arg#2 = value + dnl arg#3 = summary/config.log/autoconf description + AC_MSG_CHECKING([$3]) + AC_MSG_RESULT([$2]) + dnl FIXME: value here is already quoted by caller (for AC_DEFINE_UNQUOTED + dnl with multi-token strings). Then quotes are added in NUT_REPORT_FILE() + dnl and turn it into multiple single-token strings. So we neuter that here: + NUT_REPORT_FILE([$3], ["$2"], [8], "NUT Build/Target system info:") + + dnl Note: unlike features, target info does not imply an AutoMake toggle + AC_DEFINE_UNQUOTED($1, $2, $3) +]) + +AC_DEFUN([NUT_REPORT_COMPILERS], +[ + (echo "" + echo "NUT Compiler settings:" + echo "----------------------" + echo "" + if test x"${nut_with_debuginfo_C}" = x"yes" -o x"${nut_with_debuginfo_CXX}" = x"yes" ; then + printf 'NOTE: Settings for ' + if test x"${nut_with_debuginfo_C}" = x"yes" ; then + printf 'C ' + fi + if test x"${nut_with_debuginfo_C}${nut_with_debuginfo_CXX}" = x"yesyes" ; then + printf 'and ' + fi + if test x"${nut_with_debuginfo_CXX}" = x"yes" ; then + printf 'C++ ' + fi + printf 'compiler' + if test x"${nut_with_debuginfo_C}${nut_with_debuginfo_CXX}" = x"yesyes" ; then + printf 's' + fi + printf ' are adjusted for debugging (and minimal optimizations)\n\n' + fi + printf '* CC \t: %s\n' "$CC" + printf '* CFLAGS \t: %s\n' "$CFLAGS" + printf '* CXX \t: %s\n' "$CXX" + printf '* CXXFLAGS\t: %s\n' "$CXXFLAGS" + printf '* CPP \t: %s\n' "$CPP" + printf '* CPPFLAGS\t: %s\n' "$CPPFLAGS" + printf '* CONFIG_FLAGS\t: %s\n' "$CONFIG_FLAGS" + ) > config.nut_report_feature.log.9 + ac_clean_files="${ac_clean_files} config.nut_report_feature.log.9" +]) + +AC_DEFUN([NUT_PRINT_FEATURE_REPORT], +[ + dnl By (legacy) default we remove this report file + dnl For CI we want to publish its artifact + dnl Manageable by "--enable-keep_nut_report_feature" + echo "" + AS_IF([test x"${nut_enable_keep_nut_report_feature-}" = xyes], + [AC_MSG_NOTICE([Will keep config.nut_report_feature.log]) + cat config.nut_report_feature.log.* > config.nut_report_feature.log + cat config.nut_report_feature.log + ], + [dnl Remove if exists from old builds + ac_clean_files="${ac_clean_files} config.nut_report_feature.log" + cat config.nut_report_feature.log.* + ]) ]) diff --git a/m4/nut_type_socklen_t.m4 b/m4/nut_type_socklen_t.m4 index 205a5f953a..d22dda3791 100644 --- a/m4/nut_type_socklen_t.m4 +++ b/m4/nut_type_socklen_t.m4 @@ -1,13 +1,30 @@ dnl Check for socklen_t: historically on BSD it is an int, and in dnl POSIX 1g it is a type of its own, but some platforms use different -dnl types for the argument to getsockopt, getpeername, etc. So we -dnl have to test to find something that will work. +dnl types for the argument to getsockopt, getpeername, etc.: +dnl HP-UX 10.20, IRIX 6.5, Interix 3.5, BeOS. +dnl So we have to test to find something that will work. -dnl This code gets around. This instance came from rsync 2.5.6. +dnl Copyright (C) 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Albert Chin, Windows fixes from Simon Josefsson. + +dnl On mingw32, socklen_t is in ws2tcpip.h ('int'), so we try to find +dnl it there first. That file is included by gnulib sys_socket.in.h, which +dnl all users of this module should include. Cygwin must not include +dnl ws2tcpip.h. + +dnl This code gets around. This instance came from gnulib trunk (21 Oct. 2010). AC_DEFUN([NUT_TYPE_SOCKLEN_T], [ + NUT_PREREQ_SYS_H_SOCKET AC_REQUIRE([NUT_CHECK_HEADER_WS2TCPIP])dnl + dnl # TOTHINK: Are and mutually exclusive? + dnl # Some projects test for both, based on ifdef results; we declare + dnl # those in NUT_PREREQ_SYS_H_SOCKET however as either one or another HEADERS_SOCKLEN_T=' #undef inline #ifdef HAVE_WINDOWS_H @@ -44,26 +61,28 @@ AC_DEFUN([NUT_TYPE_SOCKLEN_T], AC_CHECK_TYPE([socklen_t], ,[ AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([nut_cv_socklen_t_equiv], - [ - # Systems have either "struct sockaddr *" or + [# Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername AC_LANG_PUSH([C]) nut_cv_socklen_t_equiv= for arg1 in "int" "SOCKET"; do for arg2 in "struct sockaddr" void; do - for arg3 in int size_t unsigned long "unsigned long"; do + for arg3 in int size_t unsigned long "unsigned long" "unsigned int" "long int" "unsigned long int"; do AC_COMPILE_IFELSE([AC_LANG_PROGRAM([${HEADERS_SOCKLEN_T} GNICALLLINK int GNICALLCONV getpeername ($arg1, $arg2 *, $arg3 *); ],[ $arg3 len; - getpeername(0,0,&len); + getpeername(0, 0, &len); ])], [ nut_cv_socklen_t_equiv="$arg3" break ]) + if test x"$nut_cv_socklen_t_equiv" != "x" ; then break ; fi done + if test x"$nut_cv_socklen_t_equiv" != "x" ; then break ; fi done + if test x"$nut_cv_socklen_t_equiv" != "x" ; then break ; fi done AC_LANG_POP([C]) @@ -76,3 +95,35 @@ AC_DEFUN([NUT_TYPE_SOCKLEN_T], [type to use in place of socklen_t if not defined])], [${HEADERS_SOCKLEN_T}]) ]) + +AC_DEFUN([NUT_PREREQ_SYS_H_SOCKET], +[ + dnl Check prerequisites of the replacement. + AC_CHECK_HEADERS_ONCE([sys/socket.h]) + if test $ac_cv_header_sys_socket_h = yes; then + HAVE_SYS_SOCKET_H=1 + HAVE_WS2TCPIP_H=0 + HAVE_WINSOCK2_H=0 + else + HAVE_SYS_SOCKET_H=0 + dnl We cannot use AC_CHECK_HEADERS_ONCE here, because that would make + dnl the check for those headers unconditional; yet cygwin reports + dnl that the headers are present but cannot be compiled (since on + dnl cygwin, all socket information should come from sys/socket.h). + AC_CHECK_HEADERS([ws2tcpip.h]) + if test $ac_cv_header_ws2tcpip_h = yes; then + HAVE_WS2TCPIP_H=1 + else + HAVE_WS2TCPIP_H=0 + fi + AC_CHECK_HEADERS([winsock2.h]) + if test "$ac_cv_header_winsock2_h" = yes; then + HAVE_WINSOCK2_H=1 + else + HAVE_WINSOCK2_H=0 + fi + fi + AC_SUBST([HAVE_WINSOCK2_H]) + AC_SUBST([HAVE_SYS_SOCKET_H]) + AC_SUBST([HAVE_WS2TCPIP_H]) +]) diff --git a/scripts/Aix/nut-aix.spec.in b/scripts/Aix/nut-aix.spec.in index 37e4934745..1302160dcf 100644 --- a/scripts/Aix/nut-aix.spec.in +++ b/scripts/Aix/nut-aix.spec.in @@ -17,8 +17,8 @@ Release: 1 Group: Applications/System License: GPLv2+ Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -Url: http://www.networkupstools.org/ -Source: http://www.networkupstools.org/source/@TREE_VERSION@/%{name}-%{version}.tar.gz +Url: https://www.networkupstools.org/ +Source: https://www.networkupstools.org/source/@TREE_VERSION@/%{name}-%{version}.tar.gz Source1: nut.init #Source2: ups.sysconfig #Source3: nut-client.tmpfiles diff --git a/scripts/Aix/nut.init.in b/scripts/Aix/nut.init.in index 018777efe6..f8bd09444b 100755 --- a/scripts/Aix/nut.init.in +++ b/scripts/Aix/nut.init.in @@ -34,12 +34,15 @@ CLIENT="no" NUT_DIR="@prefix@" NUT_SBIN_DIR="${NUT_DIR}/sbin" NUT_LIB_DIR="${NUT_DIR}/lib" -NUT_RUN_DIR="@PIDPATH@/nut" +NUT_RUN_DIR="@ALTPIDPATH@" CONFIG="@CONFPATH@/nut.conf" NUTUSER="@RUN_AS_USER@" NUTGROUP="@RUN_AS_GROUP@" NUT_VAR_LOCK="/var/locks/ups" +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + if [ -f "$CONFIG" ] ; then . "$CONFIG" diff --git a/scripts/DMF/Makefile.am b/scripts/DMF/Makefile.am index b85e3f7162..45f63510f5 100644 --- a/scripts/DMF/Makefile.am +++ b/scripts/DMF/Makefile.am @@ -37,6 +37,17 @@ clean-local: check: check-local +# Included dependencies +$(abs_top_builddir)/common/libcommon.la: $(top_builddir)/common/libcommon.la +$(abs_top_builddir)/drivers/dstate.o: $(top_builddir)/drivers/dstate.o + +$(top_builddir)/common/libcommon.la \ +$(top_builddir)/common/libcommonstr.la \ +$(top_builddir)/drivers/dstate.o \ +$(top_builddir)/common/libnutwincompat.la \ +$(top_builddir)/common/libnutdmfsnmp.la: + +cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + # A way to to relative symlinks, or hardlinks, or copies... depends on OS. LN_S_R = @LN_S_R@ -f @@ -61,7 +72,8 @@ DMFNUTSCAN_SUBDIR = dmfnutscan.d # This generated file defines LEGACY_NUT_C_MIBS and LEGACY_NUT_DMFS lists # based on existing C sources for ../../drivers/*-mib.c -DISTCLEANFILES += legacy-mibfiles-list.mk +DISTCLEANFILES += \ + $(builddir)/legacy-mibfiles-list.mk # This file is only to be maintainer-cleaned: it is generated by autogen.sh MAINTAINERCLEANFILES += legacy-mibfiles-list.mk.in # Use "include" with macro to avoid errors in automake: the file is not @@ -76,16 +88,13 @@ include $(LEGACY_MIBFILES_LIST) EXTRA_DIST += legacy-mibfiles-list.mk.in # Support development experiments for lookup with functions -testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: $(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf - @true - -$(abs_builddir)/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: $(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf - @true +testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf #$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: DEBUG=yes #$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: DMFTOOLS_CFLAGS += -DWITH_SNMP_LKP_FUN_DUMMY=1 -DWITH_SNMP_LKP_FUN=1 #$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: DMFTOOLS_CPPFLAGS += -DWITH_SNMP_LKP_FUN_DUMMY=1 -DWITH_SNMP_LKP_FUN=1 -$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf : $(abs_top_srcdir)/drivers/eaton-pdu-marlin-mib.c $(DMFGEN_DEPS) +$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf: $(abs_top_srcdir)/drivers/eaton-pdu-marlin-mib.c $(DMFGEN_DEPS) @DMFFILE="$(abs_top_builddir)/scripts/DMF/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf"; \ CMIBFILE="$(abs_top_srcdir)/drivers/eaton-pdu-marlin-mib.c"; \ DEBUG=yes; \ @@ -131,13 +140,22 @@ install-data-hook: install_dmfsnmp_data install_dmfnutscan_data uninstall-hook: -rm -f $(INSTALL_NUT_DMFNUTSCAN_SYMLINK) $(INSTALL_NUT_DMF_SYMLINKS) -endif +endif WITH_DMF EXTRA_DIST += dmfsnmp.xsd dmfnutscan.xsd DISTCLEANFILES += $(LEGACY_NUT_DMF_SYMLINKS) $(DMFNUTSCAN_SYMLINK) -DISTCLEANDIRS += $(DMFSNMP_SUBDIR) $(DMFNUTSCAN_SUBDIR) -CLEANFILES += $(DMFSNMP_SUBDIR)/.uptodate $(DMFNUTSCAN_SUBDIR)/.uptodate $(DMFSNMP_SUBDIR)/.validated $(DMFNUTSCAN_SUBDIR)/.validated \ - $(DMFSNMP_RES_SUBDIR)/.uptodate $(DMFNUTSCAN_RES_SUBDIR)/.uptodate $(DMFSNMP_RES_SUBDIR)/.validated $(DMFNUTSCAN_RES_SUBDIR)/.validated +DISTCLEANDIRS += \ + $(builddir)/$(DMFSNMP_SUBDIR) \ + $(builddir)/$(DMFNUTSCAN_SUBDIR) +CLEANFILES += \ + $(builddir)/$(DMFSNMP_SUBDIR)/.uptodate \ + $(builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate \ + $(builddir)/$(DMFSNMP_SUBDIR)/.validated \ + $(builddir)/$(DMFNUTSCAN_SUBDIR)/.validated \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate \ + $(builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/.validated \ + $(builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.validated # This does not rely on automake semantics, so this Makefile.am can be directly # used and changed during development without reconfiguring all of NUT sources @@ -160,9 +178,16 @@ alist.c : $(top_srcdir)/common/alist.c @test -s "$@" || ln -s -f "$(top_srcdir)/common/alist.c" "$@" -CLEANFILES += $(noinst_PROGRAMS) $(DMFSNMP_RES_SUBDIR)/*.o \ - $(DMFSNMP_RES_SUBDIR)/*.dmf.tmp $(DMFSNMP_RES_SUBDIR)/*.json.tmp \ - $(DMFSNMP_RES_SUBDIR)/*_TEST.c $(DMFSNMP_RES_SUBDIR)/*_TEST.exe +CLEANFILES += $(noinst_PROGRAMS) \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/*.o \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/*.dmf.tmp \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/*.json.tmp \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/*_TEST.c \ + $(builddir)/$(DMFSNMP_RES_SUBDIR)/*_TEST.exe \ + $(builddir)/*.dmf.tmp \ + $(builddir)/*.json.tmp \ + $(builddir)/*_TEST.c \ + $(builddir)/*_TEST.exe # A bit of magic for the simpler "automake if" below # which can not process "if (A || B)" statements @@ -173,17 +198,17 @@ ADD_TGT_DMF = if WITH_DMF TGT_CHECK_LOCAL += run-dmf-test -endif +endif WITH_DMF if WITH_VALIDATE_DMF_SNMP ADD_TGT_CHECK_LOCAL += dmf-validate ADD_TGT_DMF += dmf-validate -else +else !WITH_VALIDATE_DMF_SNMP if WITH_VALIDATE_DMF_NUTSCAN ADD_TGT_CHECK_LOCAL += dmf-validate ADD_TGT_DMF += dmf-validate -endif -endif +endif WITH_VALIDATE_DMF_NUTSCAN +endif !WITH_VALIDATE_DMF_SNMP check-local: $(TGT_CHECK_LOCAL) $(ADD_TGT_CHECK_LOCAL) dmf: $(TGT_DMF) $(ADD_TGT_DMF) @@ -193,7 +218,7 @@ check-experimental: progs check-local run-dmf-lua-test run-dmf-fun-test check-all: check-experimental check-lua: run-dmf-lua-test check-fun: run-dmf-fun-test -endif +endif WITH_DMF # Recipes to build DMF files with the sources and tools we have are defined # via $(DMFGEN_CMD) as used in legacy-mibfiles-list.mk for example. @@ -224,15 +249,24 @@ if WITH_REGENERATE_DMF_SNMP # (we can not use $< in this context); this approach is a hack that # relies on known/assumed fixed input filename location and relation # to output filenames - which we sort of guarantee in other places. +# NOTE about MKDIR_P passing here and below: on some platforms it may be +# defined via expanded relative path to `$(top_builddir)/install-sh` and +# be invalid for callers whose current directory is not $(builddir). +# Take care to not use it inside contexts shifted by `cd somewhere`. DMFGEN_CMD = ( \ - echo " DMFIFY '$$CMIBFILE' => '$@' (PWD = `pwd`)" >&2 ; \ + if test x"$(V)" = x1 ; then echo "[D] abs_top_builddir='$(abs_top_builddir)' abs_top_srcdir='$(abs_top_srcdir)' top_builddir='$(top_builddir)' top_srcdir='$(top_srcdir)' builddir='$(builddir)' srcdir='$(srcdir)' PWD='`pwd`'" >&2 ; fi ; \ + if test x"$${ABS_OUTDIR-}" = x ; then ABS_OUTDIR="`pwd`" ; fi ; export ABS_OUTDIR ; \ + echo " DMFIFY '$$CMIBFILE' => '$@' (PWD='`pwd`', actual ABS_OUTDIR='$${ABS_OUTDIR}')" >&2 ; \ test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" || exit ; \ if test -f "$(DMFGEN_SANITY_VALIDATED)" ; then \ + test -d "$${ABS_OUTDIR}" -a -w "$${ABS_OUTDIR}" || $(MKDIR_P) "$${ABS_OUTDIR}" || exit ; \ ( cd "$(@D)" && \ LANG=C LC_ALL=C TZ=UTC \ CFLAGS="$(DMFTOOLS_CFLAGS) $(AM_CFLAGS)" \ LDFLAGSx="$(abs_top_builddir)/common/libcommon.la $(abs_top_builddir)/drivers/dstate.o" \ CPPFLAGS="$(DMFTOOLS_CPPFLAGS) $(CPPFLAGS) $(AM_CPPFLAGS)" \ + ABS_BUILDDIR="$(abs_builddir)" \ + DEBUG="$(V)" \ $(abs_srcdir)/dmfify-mib.sh --skip-sanity-check "$$CMIBFILE" "$@" \ ) || exit; \ fi ; \ @@ -247,14 +281,15 @@ if WITH_REGENERATE_DMF_SNMP $(DMFGEN_SANITY_VALIDATED): dmfgen-sanity dmfgen-sanity: $(srcdir)/dmfify-mib.sh - @if $(abs_srcdir)/dmfify-mib.sh --sanity-check ; then \ + @DEBUG="$(V)"; export DEBUG; \ + if $(abs_srcdir)/dmfify-mib.sh --sanity-check ; then \ echo "Sanity-checked the low-level ability to generate DMF files - OK" >&2; \ test -f "$(DMFGEN_SANITY_VALIDATED)" || touch $(DMFGEN_SANITY_VALIDATED); \ - else \ + else \ echo "SKIP: NOT REBUILDING DMF files due to absence of prerequisites like Python or pycparser" >&2; \ rm -f $(DMFGEN_SANITY_VALIDATED); \ - fi -else + fi +else !WITH_REGENERATE_DMF_SNMP DMFGEN_CMD = ( \ echo "SKIP: Generation of $@ from $$CMIBFILE not enabled by the configure script (--with-dmfsnmp-regenerate option)"; \ rm -f $(DMFGEN_SANITY_VALIDATED); \ @@ -262,28 +297,38 @@ else echo "ERROR: Distributed file $@ is missing" >&2; exit 2; \ fi; \ ) -endif +endif !WITH_REGENERATE_DMF_SNMP # For regenerated files, we link from `pwd` (e.g. .../_build/scripts/DMF during # distcheck) and fail if it did not go well (generation failed); but when the # caller explicitly does not regenerate DMFs from Cs, we use pre-generated ones. if WITH_REGENERATE_DMF_SNMP DMFFILE_DIR= -else +else !WITH_REGENERATE_DMF_SNMP DMFFILE_DIR=$(abs_srcdir)/ -endif +endif !WITH_REGENERATE_DMF_SNMP +# NOTE: "sleep" below lets some funny make programs catch up, hopefully +# (if sub-make of a DMF reported completion but file is not here yet, +# or if the initial reaction was to a file in srcdir/distdir) DMFLNK_CMD = ( \ case "$$DMFFILE" in /*) DMFFILE_DIR="" ;; *) DMFFILE_DIR="$(DMFFILE_DIR)" ;; esac; \ - echo " LN '$$DMFFILE_DIR$$DMFFILE' => '$$DMFLINK' (PWD = `pwd`)" >&2 ; \ - [ "$(V)" = 1 ] && echo "BEFORE:" && ls -la "$$DMFFILE_DIR$$DMFFILE" "$$DMFLINK" || true; \ + if [ x"$(V)" = x1 ] ; then \ + echo " LN '$$DMFFILE_DIR$$DMFFILE' => '$$DMFLINK' (PWD = '`pwd`', IN='$?', IN_ALL='$^', OUT='$@')" >&2 ; \ + else \ + echo " LN '$$DMFFILE_DIR$$DMFFILE' => '$$DMFLINK'" >&2 ; \ + fi ; \ + test -f "$$DMFFILE_DIR$$DMFFILE" || { echo "`date`: 1. File '$$DMFFILE_DIR$$DMFFILE' not found yet, sleeping a bit" >&2; sleep 5 ; sync; } ; \ + test -f "$$DMFFILE_DIR$$DMFFILE" || { echo "`date`: 2. File '$$DMFFILE_DIR$$DMFFILE' not found yet, sleeping a bit" >&2; sleep 10 ; sync; } ; \ + test -f "$$DMFFILE_DIR$$DMFFILE" || { echo "`date`: 3. File '$$DMFFILE_DIR$$DMFFILE' not found yet, sleeping a bit" >&2; sleep 15 ; sync; } ; \ + if [ x"$(V)" = x1 ] ; then echo "BEFORE:" ; ls -la "$$DMFFILE_DIR$$DMFFILE" "$$DMFLINK" || true; fi ; \ DMFLINK_DIR="`dirname "$$DMFLINK"`"; \ test -d "$$DMFLINK_DIR" -a -w "$$DMFLINK_DIR" || $(MKDIR_P) "$$DMFLINK_DIR" || exit ; \ test -f "$$DMFFILE_DIR$$DMFFILE" || { echo "File '$$DMFFILE_DIR$$DMFFILE' not found" >&2; exit 22; } ; \ - [ "$(V)" = 1 ] && echo "$(LN_S_R) '$$DMFFILE_DIR$$DMFFILE' '$$DMFLINK' || exit"; \ + if [ x"$(V)" = x1 ] ; then echo "$(LN_S_R) '$$DMFFILE_DIR$$DMFFILE' '$$DMFLINK' || exit"; fi ; \ rm -f "$$DMFLINK" || true ; \ $(LN_S_R) "$$DMFFILE_DIR$$DMFFILE" "$$DMFLINK" || exit ; \ - [ "$(V)" = 1 ] && echo "AFTER:"; ls -la "$$DMFFILE_DIR$$DMFFILE" "$$DMFLINK" || true; \ + if [ x"$(V)" = x1 ] ; then echo "AFTER:" && ls -la "$$DMFFILE_DIR$$DMFFILE" "$$DMFLINK" || true; fi ; \ ) # We can not touch read-only source dirs (e.g. during distcheck) so make sure @@ -295,7 +340,8 @@ DMFLNK_CMD = ( \ # so we copy stuff below just in case (if needed). .PHONY: $(DMFSNMP_SUBDIR)/.uptodate $(DMFNUTSCAN_SUBDIR)/.uptodate $(DMFSNMP_SUBDIR)/.validated $(DMFNUTSCAN_SUBDIR)/.validated \ $(DMFSNMP_RES_SUBDIR)/.uptodate $(DMFNUTSCAN_RES_SUBDIR)/.uptodate $(DMFSNMP_RES_SUBDIR)/.validated $(DMFNUTSCAN_RES_SUBDIR)/.validated -$(DMFSNMP_RES_SUBDIR)/.uptodate: $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate +$(DMFSNMP_RES_SUBDIR)/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate: $(dmfsnmpres_DMFS) @echo "DMFSNMP_RES_SUBDIR content is now up to date, made of: $(dmfsnmpres_DMFS)" >&2 @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @@ -311,6 +357,7 @@ $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate: $(dmfsnmpres_DMFS) *) N="$(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/$$B"; \ if [ -s "$$F" ] && [ ! -s "$$N" ] ; then \ echo " CP $$F => $$N" >&2 ; \ + $(MKDIR_P) "$(abs_builddir)/$(DMFSNMP_RES_SUBDIR)" && \ cp -f "$$F" "$$N" || exit ; \ fi ;; \ esac ; \ @@ -318,7 +365,8 @@ $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate: $(dmfsnmpres_DMFS) fi touch "$@" -$(DMFSNMP_SUBDIR)/.uptodate: $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate +$(DMFSNMP_SUBDIR)/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate: $(LEGACY_NUT_DMF_SYMLINKS) touch "$@" @@ -329,12 +377,14 @@ $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate: $(LEGACY_NUT_DMF_SYMLINKS) # TODO: The proper solution (sort, uniq) should probably be in the C code! # NOTE: Sorting is a problem now that some MIBs must be last (e.g. ietf) - # so we add them in desired order explicitly... -DMFNUTSCAN_DEPS = $(DMFSNMP_RES_SUBDIR)/.uptodate +DMFNUTSCAN_DEPS = $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate EXTRA_DIST += $(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf +$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_top_builddir)/scripts/DMF/$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf if WITH_REGENERATE_DMF_NUTSCAN DMFNUTSCAN_DEPS += dmf-reindex $(top_builddir)/Makefile $(abs_builddir)/Makefile -$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: $(DMFNUTSCAN_DEPS) +$(abs_top_builddir)/scripts/DMF/$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: $(DMFNUTSCAN_DEPS) @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" $(DMF_REINDEX) -Z "$(abs_builddir)/$(DMFSNMP_RES_SUBDIR)" > "$@.tmp" ( LANG=C; LC_ALL=C; TZ=UTC; export LANG LC_ALL TZ; \ @@ -351,35 +401,38 @@ $(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: $(DMFNUTSCAN_DEPS) rm -f "$@.tmp"* ; exit 1; } ; exit 0 rm -f "$@.tmp"* test -s "$@" -else -$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: $(DMFNUTSCAN_DEPS) +else !WITH_REGENERATE_DMF_NUTSCAN +$(abs_top_builddir)/scripts/DMF/$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf: $(DMFNUTSCAN_DEPS) @echo "SKIP: Generation of $@ not enabled by the configure script (--with-dmfnutscan-regenerate option)" - @if test ! -s "$@" -a ! -s "$(abs_srcdir)/$@"; then echo "ERROR: Distributed file $@ is missing" >&2; exit 2; fi; true + @if test ! -s "$@" -a ! -s "$(abs_srcdir)/$(DMFNUTSCAN_RES_SUBDIR)/$(@F)"; then echo "ERROR: Distributed file $@ is missing" >&2; exit 2; fi; true @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @if [ "`cd $(abs_builddir) && pwd`" != "`cd $(abs_srcdir) && pwd`" ]; then \ F="$(abs_srcdir)/$(DMFNUTSCAN_RES_SUBDIR)/$(@F)"; \ N="$(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/$(@F)"; \ if [ -s "$$F" ] && [ ! -s "$$N" ] ; then \ echo " CP $$F => $$N" >&2 && \ + $(MKDIR_P) "$(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)" && \ cp -f "$$F" "$$N" || { ls -la "$$F" "$$N" ; exit 1; } ; \ fi; \ fi test -s "$@" -endif +endif !WITH_REGENERATE_DMF_NUTSCAN -$(DMFNUTSCAN_SYMLINK): $(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf +$(DMFNUTSCAN_SYMLINK): $(abs_top_builddir)/scripts/DMF/$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf @DMFFILE="$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf"; DMFLINK="$@"; $(DMFLNK_CMD) $(INSTALL_NUT_DMFNUTSCAN_SYMLINK): $(INSTALL_NUT_DMFNUTSCAN_FILE) @DMFFILE="$(INSTALL_NUT_DMFNUTSCAN_FILE)"; DMFLINK="$@"; $(DMFLNK_CMD) -$(DMFNUTSCAN_RES_SUBDIR)/.uptodate: $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate -$(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate: $(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf +$(DMFNUTSCAN_RES_SUBDIR)/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate +$(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate: $(abs_top_builddir)/scripts/DMF/$(DMFNUTSCAN_RES_SUBDIR)/dmfnutscan-snmp.dmf @echo "DMFNUTSCAN_RES_SUBDIR is now up to date" >&2 @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" touch "$@" -$(DMFNUTSCAN_SUBDIR)/.uptodate: $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate +$(DMFNUTSCAN_SUBDIR)/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate: $(DMFNUTSCAN_SYMLINK) @echo "DMFNUTSCAN_SUBDIR is now up to date" >&2 @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @@ -387,10 +440,14 @@ $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate: $(DMFNUTSCAN_SYMLINK) # Validation requires xmllint, which we have if we succeed with asciidoc usage # TODO: Maybe per-DMF *.dmf.validated files would be better? -$(DMFSNMP_RES_SUBDIR)/.validated: $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.validated -$(DMFSNMP_SUBDIR)/.validated: $(abs_builddir)/$(DMFSNMP_SUBDIR)/.validated -$(DMFNUTSCAN_RES_SUBDIR)/.validated: $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.validated -$(DMFNUTSCAN_SUBDIR)/.validated: $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.validated +$(DMFSNMP_RES_SUBDIR)/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.validated +$(DMFSNMP_SUBDIR)/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFSNMP_SUBDIR)/.validated +$(DMFNUTSCAN_RES_SUBDIR)/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.validated +$(DMFNUTSCAN_SUBDIR)/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.validated # The DMFLINT_XSD and DMFLINT_OPTION are fed by caller if WITH_VALIDATE_DMF_SNMP DMFLINT_CMD = ( \ @@ -401,13 +458,13 @@ if WITH_VALIDATE_DMF_SNMP done; \ touch "$(@F)"; \ ) -else +else !WITH_VALIDATE_DMF_SNMP DMFLINT_CMD = ( \ test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" || exit ; \ echo "SKIP: Validation of $@ not enabled by the configure script ($$DMFLINT_OPTION option)" ; \ echo "VALIDATION-SKIPPED" > "$@" ; \ ) -endif +endif !WITH_VALIDATE_DMF_SNMP $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.validated: $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate @DMFLINT_XSD="$(abs_srcdir)/dmfsnmp.xsd" ; DMFLINT_OPTION="--with-dmfsnmp-validate"; $(DMFLINT_CMD) @@ -423,31 +480,39 @@ $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.validated: $(abs_builddir)/$(DMFNUTSCAN_SU # Make sure all generated DMFs are up to date vs. C sources and recipes -dmf-uptodate: $(DMFSNMP_RES_SUBDIR)/.uptodate $(DMFNUTSCAN_RES_SUBDIR)/.uptodate $(DMFSNMP_SUBDIR)/.uptodate $(DMFNUTSCAN_SUBDIR)/.uptodate +dmf-uptodate: \ + $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.uptodate \ + $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.uptodate \ + $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate \ + $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.uptodate # Make sure all present DMFs are valid vs. XSD schema -dmf-validate: dmf-uptodate $(DMFSNMP_RES_SUBDIR)/.validated $(DMFNUTSCAN_RES_SUBDIR)/.validated $(DMFSNMP_SUBDIR)/.validated $(DMFNUTSCAN_SUBDIR)/.validated +dmf-validate: dmf-uptodate \ + $(abs_builddir)/$(DMFSNMP_RES_SUBDIR)/.validated \ + $(abs_builddir)/$(DMFNUTSCAN_RES_SUBDIR)/.validated \ + $(abs_builddir)/$(DMFSNMP_SUBDIR)/.validated \ + $(abs_builddir)/$(DMFNUTSCAN_SUBDIR)/.validated CFLAGS_LUA = $(LUA_INCLUDE) LDFLAGS_LUA = $(LUA_LIB) if WITH_DMF_LUA CFLAGS_DMF_LUA = -DWITH_DMF_LUA=1 -DWITH_DMF_FUNCTIONS=1 $(CFLAGS_LUA) LDFLAGS_DMF_LUA = $(LDFLAGS_LUA) -else +else !WITH_DMF_LUA # Note: absence of LUA does not require to set -DWITH_DMF_FUNCTIONS=0 CFLAGS_DMF_LUA = -DWITH_DMF_LUA=0 LDFLAGS_DMF_LUA = -endif +endif !WITH_DMF_LUA if WITH_LIBLTDL LIBS_dmfsnmp_ltdlXneon = $(LIBLTDL_LIBS) CFLAGS_dmfsnmp_ltdlXneon = $(LIBNEON_CFLAGS) $(LIBLTDL_CFLAGS) -DWITH_LIBLTDL=1 -else +else !WITH_LIBLTDL if WITH_NEON LIBS_dmfsnmp_ltdlXneon = $(LIBNEON_LIBS) CFLAGS_dmfsnmp_ltdlXneon = $(LIBNEON_CFLAGS) -DWITH_LIBLTDL=0 -endif -endif +endif WITH_NEON +endif !WITH_LIBLTDL progs: $(noinst_PROGRAMS) @@ -463,17 +528,27 @@ DMFTESTS_COMMON_CFLAGS = \ -DDEFAULT_DMFSNMP_DIR_OVERRIDE="\"$(abs_builddir)/$(DMFSNMP_SUBDIR)\"" # Some GCCisms... TODO: A configure flag variable like WITH_GCC +# Note that _FORTIFY_SOURCE requires non-trivial optimization +# to *BE* enabled (unlike the comments above about pre-processing +# needed for pycparser to understand the original code); otherwise +# a warning can appear (which is fatal when NUT is configured to +# reject any warnings generally, or the line below is uncommented), +# e.g.: +# /usr/include/features.h:397:4: error: #warning _FORTIFY_SOURCE +# requires compiling with optimization (-O) [-Werror=cpp] DMFTESTS_COMMON_CFLAGS += \ - -ggdb -std=c99 -Wall -pedantic -Wc++-compat -D_FORTIFY_SOURCE=2 -fno-stack-check -fno-omit-frame-pointer -fstack-protector -ggdb3 -O0 + -D_FORTIFY_SOURCE=2 -O2 + +# Overlaps with warning levels NUT offers nowardays; should be +# used via configure flags (some compilers in NUT CI farm do not +# know the flags below): +#DMFTESTS_COMMON_CFLAGS += \ +# -ggdb -std=c99 -Wall -pedantic -Wc++-compat -fno-stack-check -fno-omit-frame-pointer -fstack-protector -ggdb3 # For fans of suicide on obscure platforms ;) #DMFTESTS_COMMON_CFLAGS += -Werror if WITH_DMF -# Included dependencies -$(top_builddir)/common/libnutdmfsnmp.la : - cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) - noinst_PROGRAMS += dmf-test dmf_test_SOURCES = $(DMFTEST_SRC) @@ -488,11 +563,15 @@ dmf_test_CFLAGS = $(DMFTESTS_COMMON_CFLAGS) $(CFLAGS_DMF_LUA) # $(LIBNETSNMP_CFLAGS) $(LIBNEON_CFLAGS) -DWITH_DMFMIB=1 -DWITH_SNMP=1 -DWITH_NEON=1 dmf_test_LDFLAGS = $(LDFLAGS_DMF_LUA) +if HAVE_WINDOWS + dmf_test_LDADD += $(top_builddir)/common/libnutwincompat.la +endif HAVE_WINDOWS + if WITH_LIBXML2 dmf_test_LDADD += $(LIBXML2_LIBS) dmf_test_CFLAGS += $(LIBXML2_CFLAGS) -endif -endif +endif WITH_LIBXML2 +endif WITH_DMF # This seems to cause issues regardless of "if", so # devs desiring to hack should just uncomment this part @@ -505,31 +584,31 @@ endif #dmf_reindex_CFLAGS = $(DMFTESTS_COMMON_CFLAGS) $(CFLAGS_DMF_LUA) #dmf_reindex_LDFLAGS = $(LDFLAGS_DMF_LUA) #else -CLEANFILES += dmf-reindex +CLEANFILES += dmf-reindex$(EXEEXT) DMF_REINDEX = $(abs_top_builddir)/tools/nut-scanner/nut-scanner-reindex-dmfsnmp dmf-reindex: - cd $(abs_top_builddir)/tools/nut-scanner/ && $(MAKE) nut-scanner-reindex-dmfsnmp + +cd $(abs_top_builddir)/tools/nut-scanner/ && $(MAKE) nut-scanner-reindex-dmfsnmp $(LN_S_R) $(abs_top_builddir)/tools/nut-scanner/nut-scanner-reindex-dmfsnmp dmf-reindex #endif if WITH_VALGRIND DMF_TEST_RUNNER = $(VALGRIND) --leak-check=full --track-origins=yes --show-leak-kinds=all -v -else +else !WITH_VALGRIND DMF_TEST_RUNNER = -endif +endif !WITH_VALGRIND # Note: DMF dirs are currently hardcoded (above) so no big need to "cd" anywhere # But some data should be prepopulated there before we run leak tests etc. if WITH_DMF -run-dmf-test: dmf-test $(DMFSNMP_SUBDIR)/.uptodate - cd $(DMFSNMP_SUBDIR) && ls -la && $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-test +run-dmf-test: dmf-test $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate + cd $(DMFSNMP_SUBDIR) && ls -la && DEBUG="$(V)" $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-test -run-dmf-reindex: dmf-reindex $(DMFSNMP_SUBDIR)/.uptodate - cd $(DMFSNMP_SUBDIR) && ls -la && $(DMF_TEST_RUNNER) $(DMF_REINDEX) -else +run-dmf-reindex: dmf-reindex $(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate + cd $(DMFSNMP_SUBDIR) && ls -la && DEBUG="$(V)" $(DMF_TEST_RUNNER) $(DMF_REINDEX) +else !WITH_DMF run-dmf-test run-dmf-reindex: @echo " SKIP DMF support was not enabled or detected during project configuration, can not run test programs" >&2 -endif +endif !WITH_DMF if WITH_DMF if WITH_LUA @@ -550,7 +629,7 @@ dmf_lua_test_LDFLAGS = $(LDFLAGS_LUA) if WITH_LIBXML2 dmf_lua_test_LDADD += $(LIBXML2_LIBS) dmf_lua_test_CFLAGS += $(LIBXML2_CFLAGS) -endif +endif WITH_LIBXML2 noinst_PROGRAMS += dmf-fun-test # Note: This uses CFLAGS_LUA not CFLAGS_DMF_LUA so the test bits can build @@ -568,29 +647,33 @@ dmf_fun_test_LDFLAGS = $(LDFLAGS_LUA) if WITH_LIBXML2 dmf_fun_test_LDADD += $(LIBXML2_LIBS) dmf_fun_test_CFLAGS += $(LIBXML2_CFLAGS) -endif +endif WITH_LIBXML2 -testdata-lua/.validated: $(abs_builddir)/testdata-lua/.validated -testdata-fun/.validated: $(abs_builddir)/testdata-fun/.validated +testdata-lua/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/testdata-lua/.validated +testdata-fun/.validated: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/testdata-fun/.validated LUA_EXAMPLE_DMFS = $(abs_srcdir)/testdata-lua/lua-eaton-mib.dmf $(abs_srcdir)/testdata-lua/lua-mge-mib.dmf # LUA_EXAMPLE_DMFS += $(abs_srcdir)/testdata-lua/lua-example-mib.dmf FUN_EXAMPLE_DMFS = $(abs_srcdir)/testdata-fun/eaton-pdu-marlin-mib_WITH_SNMP_LKP_FUN_DUMMY.dmf -testdata-lua/.uptodate: $(abs_builddir)/testdata-lua/.uptodate +testdata-lua/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/testdata-lua/.uptodate $(abs_builddir)/testdata-lua/.uptodate: $(LUA_EXAMPLE_DMFS) @echo "testdata-lua is now up to date" >&2 @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" touch "$@" -testdata-fun/.uptodate: $(abs_builddir)/testdata-fun/.uptodate +testdata-fun/.uptodate: + +$(MAKE) $(AM_MAKEFLAGS) $(abs_builddir)/testdata-fun/.uptodate $(abs_builddir)/testdata-fun/.uptodate: $(FUN_EXAMPLE_DMFS) @echo "testdata-fun is now up to date" >&2 @test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" touch "$@" if WITH_VALIDATE_DMF_SNMP -$(abs_builddir)/testdata-lua/.validated: testdata-lua/.uptodate +$(abs_builddir)/testdata-lua/.validated: $(abs_builddir)/testdata-lua/.uptodate test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @cd "$(@D)" && for F in $(LUA_EXAMPLE_DMFS) ; do \ echo " XMLLINT-LUA $$F"; \ @@ -598,29 +681,29 @@ $(abs_builddir)/testdata-lua/.validated: testdata-lua/.uptodate done touch "$@" -$(abs_builddir)/testdata-fun/.validated: testdata-fun/.uptodate +$(abs_builddir)/testdata-fun/.validated: $(abs_builddir)/testdata-fun/.uptodate test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @cd "$(@D)" && for F in $(FUN_EXAMPLE_DMFS) ; do \ echo " XMLLINT-LUA $$F"; \ $(XMLLINT) --noout --schema $(abs_srcdir)/dmfsnmp.xsd "$$F" || exit; \ done touch "$@" -else +else !WITH_VALIDATE_DMF_SNMP $(abs_builddir)/testdata-lua/.validated $(abs_builddir)/testdata-fun/.validated: test -d "$(@D)" -a -w "$(@D)" || $(MKDIR_P) "$(@D)" @echo "SKIP: Validation of $@ not enabled by the configure script (--with-dmfsnmp-validate option)" @echo "VALIDATION-SKIPPED" > "$@" -endif +endif !WITH_VALIDATE_DMF_SNMP CLEANFILES += $(abs_builddir)/testdata-lua/.validated testdata-lua/.validated CLEANFILES += $(abs_builddir)/testdata-fun/.validated testdata-fun/.validated -#$(DMFSNMP_SUBDIR)/.uptodate -run-dmf-lua-test: dmf-lua-test testdata-lua/.validated - cd $(abs_top_builddir)/scripts/DMF/testdata-lua && $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-lua-test +#$(abs_builddir)/$(DMFSNMP_SUBDIR)/.uptodate ... +run-dmf-lua-test: dmf-lua-test $(abs_builddir)/testdata-lua/.validated + cd $(abs_top_builddir)/scripts/DMF/testdata-lua && DEBUG="$(V)" $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-lua-test -run-dmf-fun-test: dmf-fun-test testdata-fun/.validated - cd $(abs_top_builddir)/scripts/DMF/testdata-fun && $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-fun-test -else +run-dmf-fun-test: dmf-fun-test $(abs_builddir)/testdata-fun/.validated + cd $(abs_top_builddir)/scripts/DMF/testdata-fun && DEBUG="$(V)" $(DMF_TEST_RUNNER) $(abs_builddir)/dmf-fun-test +else !WITH_LUA dmf-lua-test: @echo " SKIP LUA support was not enabled or detected during project configuration, can not build dmf-lua-test program" >&2 @@ -632,8 +715,8 @@ dmf-fun-test: run-dmf-fun-test: @echo " SKIP LUA support was not enabled or detected during project configuration, can not run-dmf-fun-test" >&2 -endif -else +endif !WITH_LUA +else !WITH_DMF dmf-lua-test: @echo " SKIP DMF support was not enabled or detected during project configuration, can not build dmf-lua-test program" >&2 @@ -645,7 +728,9 @@ dmf-fun-test: run-dmf-fun-test: @echo " SKIP DMF support was not enabled or detected during project configuration, can not run test programs" >&2 -endif +endif !WITH_DMF + +dummy: MAINTAINERCLEANFILES += Makefile.in .dirstamp diff --git a/scripts/DMF/dmf-test.c b/scripts/DMF/dmf-test.c index aac075cf79..26037f4462 100644 --- a/scripts/DMF/dmf-test.c +++ b/scripts/DMF/dmf-test.c @@ -25,14 +25,30 @@ #include "config.h" /* must be the first header */ +#ifndef HAVE_SETENV +/* Avoid our override from proto.h, not used in this test program anyway */ +# define HAVE_SETENV 0 +#endif + +#ifdef WIN32 +# include "wincompat.h" +#endif + #include #include #include +#include + +#ifndef DEBUG +# define DEBUG 0 +#endif /* For experiments in development of DMF+lookup function support, * uncomment this line; for currently stable codebase keep it off... */ -//#define WITH_SNMP_LKP_FUN 1 +#if DEBUG +# define WITH_SNMP_LKP_FUN 1 +#endif #include "dmf.h" @@ -47,18 +63,58 @@ #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_BUGREPORT + +#include "eaton-pdu-marlin-helpers.h" +#include "eaton-pdu-marlin-helpers.c" +#include "snmp-ups-helpers.c" #include "eaton-pdu-marlin-mib.c" -// Replicate what drivers/main.c exports +/* Replicate what drivers/main.c exports */ int do_synchronous = 0; int -main () +main (void) { + char *s; + int result, iterator = 0; + mibdmf_parser_t *dmp = NULL; + alist_t *element = NULL, **aux = NULL; +#if DEBUG > 1 + /* See below, currently a disabled code block */ + mib2nut_info_t *m2n = NULL; +#endif +#if WITH_DMF_FUNCTIONS && WITH_DMF_LUA + mib2nut_info_t **mib2nut = NULL; +#endif + nut_debug_level = 10; - int result; - mibdmf_parser_t * dmp = mibdmf_parser_new(); +/* Older CLANG (e.g. clang-3.4) sees short strings in str{n}cmp() + * arguments as arrays and claims out-of-bounds accesses + */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Warray-bounds" +#endif + if ((s = getenv("DEBUG")) && !strcmp(s, "0")) { + /* Handle "make V=1" or "V=0" */ + nut_debug_level = 0; + } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic pop +#endif + + /* Where would we load libs from? */ + upsdebugx_report_search_paths(1, 1); + + dmp = mibdmf_parser_new(); if (!dmp) { fprintf(stderr,"FATAL: Can not allocate the DMF parsing structures\n"); return ENOMEM; @@ -72,54 +128,63 @@ main () #endif #ifdef DEFAULT_DMFSNMP_DIR - printf("=== DMF-Test: Parsing data from %s...\n\n", DEFAULT_DMFSNMP_DIR); + if (nut_debug_level) + printf("=== DMF-Test: Parsing data from %s...\n\n", DEFAULT_DMFSNMP_DIR); result = mibdmf_parse_dir(DEFAULT_DMFSNMP_DIR, dmp); #else - printf("=== DMF-Test: Parsing data from %s...\n\n", "./"); + if (nut_debug_level) + printf("=== DMF-Test: Parsing data from %s...\n\n", "./"); result = mibdmf_parse_dir("./", dmp); #endif if (result != 0) { - printf("=== DMF-Test: Error parsing data: %i\n\n", result); + fprintf(stderr, "=== DMF-Test: Error parsing data: %i\n\n", result); mibdmf_parser_destroy(&dmp); return result; } - /*Debugging - *mib2nut_info_t *m2n = get_mib2nut_table(); - *print_mib2nut_memory_struct(m2n + 6); - *print_mib2nut_memory_struct(&pxgx_ups); */ - alist_t **aux = mibdmf_get_initial_list_ptr(dmp); - alist_t *element; - int iterator = 0; +#if DEBUG > 1 + /* FIXME: Modernize to new API and changed static table names + * (eaton_pxg_ups?), maybe update Makefile.am for symlinks etc. */ + m2n = get_mib2nut_table(dmp); + print_mib2nut_memory_struct(m2n + 6); + print_mib2nut_memory_struct(&pxgx_ups); +#endif - /* printf("=== DMF-Test: Loaded C structures (sample for 'eaton_epdu'):\n\n"); */ + aux = mibdmf_get_initial_list_ptr(dmp); + +#if DEBUG + if (nut_debug_level) + printf("=== DMF-Test: Loaded C structures (sample for 'eaton_epdu'):\n\n"); +#endif if(aux){ while(!(element = alist_get_element_by_name(aux[iterator], "eaton_marlin"))&&(iterator < mibdmf_get_list_size(dmp))) iterator++; if(element) { - printf("=== DMF-Test: Found an eaton_marlin element; iterator == %i \n\n", iterator); - print_mib2nut_memory_struct((mib2nut_info_t *) element->values[0]); + if (nut_debug_level) { + printf("=== DMF-Test: Found an eaton_marlin element; iterator == %i \n\n", iterator); + print_mib2nut_memory_struct((mib2nut_info_t *) element->values[0]); + } result = 0; } else { - printf("=== DMF-Test: Error, did not find an eaton_marlin element; iterator == %i\n\n", iterator); + fprintf(stderr, "=== DMF-Test: Error, did not find an eaton_marlin element; iterator == %i\n\n", iterator); result = 1; } - /* Aid debugging, uncomment and rebuild and re-run */ - /* - printf("\n\n"); - printf("=== DMF-Test: Displaying original (non-DMF) C structures (sample for 'eaton_epdu'):\n\n"); - print_mib2nut_memory_struct(&eaton_marlin); - printf("=== DMF-Test: End of original (non-DMF) C structures (sample for 'eaton_epdu'):\n\n"); - */ - /* End debugging */ +#if DEBUG + if (nut_debug_level) { + printf("\n\n"); + printf("=== DMF-Test: Displaying original (non-DMF) C structures (sample for 'eaton_epdu'):\n\n"); + print_mib2nut_memory_struct(&eaton_marlin); + printf("=== DMF-Test: End of original (non-DMF) C structures (sample for 'eaton_epdu'):\n\n"); + } +#endif #if WITH_DMF_FUNCTIONS #if WITH_DMF_LUA - // Array of pointers to singular instances of mib2nut_info_t - mib2nut_info_t **mib2nut = *(mibdmf_get_mib2nut_table_ptr)(dmp); + /* Array of pointers to singular instances of mib2nut_info_t */ + mib2nut = *(mibdmf_get_mib2nut_table_ptr)(dmp); if ( mib2nut == NULL ) { upsdebugx(1,"FATAL: Could not access the mib2nut index table"); result = 1; @@ -129,13 +194,16 @@ main () * being function-interpreted. Normally we'd search and match that... */ for (i = 0; mib2nut[i] != NULL; i++) { if (strcmp("mge", mib2nut[i]->mib_name)==0) { - printf("=== DMF-Test: Found an mge MIB mapping, dumping snmp_info; iterator == %i \n\n", i); - print_snmp_memory_struct(&(mib2nut[i]->snmp_info[0])); + if (nut_debug_level) { + printf("=== DMF-Test: Found an mge MIB mapping, dumping snmp_info; iterator == %i \n\n", i); + print_snmp_memory_struct(&(mib2nut[i]->snmp_info[0])); + } if (is_sentinel__snmp_info_t( &(mib2nut[i]->snmp_info[0]) )) { - printf("=== DMF-Test: FAILURE : an mge MIB mapping begins with a sentinel, proceeding for now but will fail the test later! \n\n"); + fprintf(stderr, "=== DMF-Test: FAILURE : an mge MIB mapping begins with a sentinel, proceeding for now but will fail the test later! \n\n"); result = 2; } else { - print_snmp_memory_struct(&(mib2nut[i]->snmp_info[1])); + if (nut_debug_level) + print_snmp_memory_struct(&(mib2nut[i]->snmp_info[1])); } break; } @@ -144,10 +212,11 @@ main () if (strcmp("eaton_epdu", mib2nut[i]->mib_name)==0) { printf("=== DMF-Test: Found an eaton_epdu MIB mapping, dumping snmp_info (note: the gateway routines manipulate driver state (snmp-ups) and do not make sense here, not linked, return error as expected); iterator == %i \n\n", i); if (is_sentinel__snmp_info_t( &(mib2nut[i]->snmp_info[0]) )) { - printf("=== DMF-Test: FAILURE : an eaton_epdu MIB mapping begins with a sentinel, proceeding for now but will fail the test later! \n\n"); + fprintf(stderr, "=== DMF-Test: FAILURE : an eaton_epdu MIB mapping begins with a sentinel, proceeding for now but will fail the test later! \n\n"); result = 2; } else { - print_snmp_memory_struct(&(mib2nut[i]->snmp_info[1])); + if (nut_debug_level) + print_snmp_memory_struct(&(mib2nut[i]->snmp_info[1])); } break; } @@ -156,14 +225,16 @@ main () #endif #endif - printf("=== DMF-Test: Freeing data...\n\n"); + if (nut_debug_level) + printf("=== DMF-Test: Freeing data...\n\n"); mibdmf_parser_destroy(&dmp); - printf("=== DMF-Test: All done (code %d)\n\n", result); + if (nut_debug_level) + printf("=== DMF-Test: All done (code %d)\n\n", result); return result; } - printf("=== DMF-Test: Error, no DMF data loaded\n"); + fprintf(stderr, "=== DMF-Test: Error, no DMF data loaded\n"); mibdmf_parser_destroy(&dmp); return -1; } diff --git a/scripts/DMF/dmfify-mib.sh b/scripts/DMF/dmfify-mib.sh index 7f27b079a5..6d1c3ce232 100755 --- a/scripts/DMF/dmfify-mib.sh +++ b/scripts/DMF/dmfify-mib.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # This wrapper around Python scripts uses them to generate XML DMF files # from existing NUT *-mib.c sources with legacy mapping structures. @@ -6,6 +6,7 @@ # # Copyright (C) 2016 Michal Vyskocil # Copyright (C) 2016 - 2021 Jim Klimov +# Copyright (C) 2022 - 2024 Jim Klimov # # A bashism, important for us here @@ -18,6 +19,43 @@ XSD_DMFSNMP_XMLNS='http://www.networkupstools.org/dmf/snmp/snmp-ups' # Where to look for python scripts - same dir as this shell script _SCRIPT_DIR="`cd $(dirname "$0") && pwd`" || \ _SCRIPT_DIR="./" # Fallback can fail +[ -n "${ABS_OUTDIR-}" ] || ABS_OUTDIR="`pwd`" || exit +[ -n "${ABS_BUILDDIR-}" ] || ABS_BUILDDIR="${ABS_OUTDIR}" + +# Integrate with "make V=1" +[ -n "${DEBUG-}" ] || DEBUG="${V-}" + +logmsg_info() { + if [ x"${DEBUG-}" = x1 -o x"${DEBUG-}" = xyes -o x"${DEBUG-}" = x ] ; then + echo "INFO: $*" >&2 + fi + return 0 +} + +logmsg_debug() { + if [ x"${DEBUG-}" = x1 -o x"${DEBUG-}" = xyes ] ; then + echo "DEBUG-INFO: $*" >&2 + fi + return 0 +} + +logmsg_error() { +# if [ x"${DEBUG-}" = x1 -o x"${DEBUG-}" = xyes ] ; then + echo "ERROR: $*" >&2 +# fi + return 0 +} + +die() { + code=$? + if [ "$1" -ge 0 ] 2>/dev/null ; then + code="$1" + shift + fi + + logmsg_error "$@" + exit $code +} if [ -n "${PYTHON-}" ] ; then # May be a name/path of binary, or one with args - check both @@ -44,7 +82,7 @@ fi # "/usr/bin/env python" value into a real path; ignoring args to python # itself (if any). [ -n "${PYTHON}" ] && PYTHON="`command -v $PYTHON | tail -1`" && [ -x "$PYTHON" ] \ -|| { echo "ERROR: Can not find Python 2.7+: '$PYTHON'" >&2; exit 2; } +|| die 2 "Can not find Python 2.7+: '$PYTHON'" # The pycparser uses GCC-compatible flags [ -n "${CC-}" ] || CC="`command -v gcc`" @@ -75,7 +113,7 @@ if [ -n "${CC-}" ] ; then *) CC="`command -v "$CC"`" ;; esac fi -[ -n "${CC}" ] && [ -x "$CC" ] || { echo "ERROR: Can not find (G)CC: '$CC'" >&2; exit 2; } +[ -n "${CC}" ] && [ -x "$CC" ] || die 2 "Can not find (G)CC: '$CC'" export CC CFLAGS CC_ENV [ -n "${CPP-}" ] || CPP="`command -v cpp`" @@ -106,7 +144,7 @@ if [ -n "${CPP-}" ] ; then *) CPP="`command -v "$CPP"`" ;; esac fi -[ -n "${CPP}" ] && [ -x "$CPP" ] || { echo "ERROR: Can not find a C preprocessor: '$CPP'" >&2; exit 2; } +[ -n "${CPP}" ] && [ -x "$CPP" ] || die 2 "Can not find a C preprocessor: '$CPP'" export CPP CPPFLAGS CPP_ENV if [ "$1" == "--skip-sanity-check" ]; then @@ -115,14 +153,14 @@ else # Here we only check basic prerequisites (a module provided with Python 2.7 # and an extra module that end-user is expected to pre-install per README). # Other included modules will be checked when scripts are executed. - echo "INFO: Validating some basics about your Python installation" >&2 + logmsg_info "Validating some basics about your Python installation" for PYMOD in argparse pycparser json; do "${PYTHON}" -c "import $PYMOD; print ($PYMOD);" || \ - { echo "ERROR: Can not use Python module '$PYMOD'" >&2; exit 2; } + die 2 "Can not use Python module '$PYMOD'" done - eval $CPP_ENV "$CPP" $CPPFLAGS --help > /dev/null || { echo "ERROR: Can not find a C preprocessor: '$CPP'" >&2; exit 2; } - eval $CC_ENV "$CC" $CFLAGS --help > /dev/null || { echo "ERROR: Can not find a C compiler: '$CC'" >&2; exit 2; } + eval $CPP_ENV "$CPP" $CPPFLAGS --help > /dev/null || die 2 "Can not find a C preprocessor: '$CPP'" + eval $CC_ENV "$CC" $CFLAGS --help > /dev/null || die 2 "Can not find a C compiler: '$CC'" if [ "$1" = "--sanity-check" ]; then # We are alive by now, so checks above have succeeded @@ -135,7 +173,7 @@ dmfify_c_file() { # Optional second one names the output file (and temporary files) # Optional third (and beyond) name additional files to look into # (e.g. the snmp-ups-helpers.c for shared tables and routines) - #echo "dmfify_c_file: '$1' '$2' '$3' ..." >&2 + #logmsg_debug "dmfify_c_file: '$1' '$2' '$3' ..." >&2 local cmib="$1" local mib="$2" shift @@ -150,11 +188,11 @@ dmfify_c_file() { fi [ -n "${cmib}" ] && [ -s "${cmib}" ] || \ - { echo "ERROR: dmfify_c_file() can not process argument '${cmib}'!" >&2 + { logmsg_error "dmfify_c_file() can not process argument '${cmib}'!" return 2; } - echo "INFO: Parsing '${cmib}' into '${mib}.dmf'; do not worry if 'missing setvar' warnings pop up..." >&2 - if [ $# -gt 0 ]; then echo "INFO: Additionally parsing resources from: $*" >&2; fi + logmsg_info "Parsing '${cmib}' into '${mib}.dmf'; do not worry if 'missing setvar' warnings pop up..." + if [ $# -gt 0 ]; then logmsg_info "Additionally parsing resources from: $*" ; fi # Code below assumes that the *.py.in only template the shebang line JNAME="" @@ -165,26 +203,31 @@ dmfify_c_file() { for F in "${_SCRIPT_DIR}"/xmlify-mib.py.in "${_SCRIPT_DIR}"/xmlify-mib.py ; do [ -s "$F" ] && XNAME="$F" && break done - ( "${PYTHON}" "${JNAME}" --test "${cmib}" "$@" > "${mib}.json.tmp" && \ - "${PYTHON}" "${XNAME}" < "${mib}.json.tmp" > "${mib}.dmf.tmp" ) \ - && [ -s "${mib}.dmf.tmp" ] \ + logmsg_debug "PWD: `pwd`" + ( "${PYTHON}" "${JNAME}" --test "${cmib}" "$@" > "${ABS_BUILDDIR}/${mib}.json.tmp" && \ + "${PYTHON}" "${XNAME}" < "${ABS_BUILDDIR}/${mib}.json.tmp" > "${ABS_BUILDDIR}/${mib}.dmf.tmp" ) \ + && [ -s "${ABS_BUILDDIR}/${mib}.dmf.tmp" ] \ || { ERRCODE=$? if [ $# -gt 0 ]; then - echo "ERROR: Could not parse '${cmib}' + $* into '${mib}.dmf'" >&2 + logmsg_error "Could not parse '${cmib}' + $* into '${ABS_BUILDDIR}/${mib}.dmf'" else - echo "ERROR: Could not parse '${cmib}' into '${mib}.dmf'" >&2 + logmsg_error "Could not parse '${cmib}' into '${ABS_BUILDDIR}/${mib}.dmf'" fi - echo " You can inspect a copy of the intermediate result in '${mib}.json.tmp', '${mib}.dmf.tmp' and '${mib}_TEST.c'" >&2 + logmsg_error "You can inspect a copy of the intermediate result in '${mib}.json.tmp', '${mib}.dmf.tmp' and '${mib}_TEST.c' located in '${ABS_BUILDDIR}/', '${_SCRIPT_DIR}' and/or '`pwd`'" return $ERRCODE; } - sed 's,^,\,' < "${mib}.dmf.tmp" > "${mib}.dmf" \ + sed 's,^,\,' < "${ABS_BUILDDIR}/${mib}.dmf.tmp" > "${ABS_BUILDDIR}/${mib}.dmf" \ || { ERRCODE=$? - echo "ERROR: Could not fix headers of '${mib}.dmf'" >&2 - echo " You can inspect a copy of the intermediate result in '${mib}.json.tmp', '${mib}.dmf.tmp' and '${mib}_TEST.c'" >&2 + logmsg_error "Could not fix headers of '${ABS_BUILDDIR}/${mib}.dmf'" + logmsg_error "You can inspect a copy of the intermediate result in '${mib}.json.tmp', '${mib}.dmf.tmp' and '${mib}_TEST.c located in '${ABS_BUILDDIR}/', '${_SCRIPT_DIR}' and/or '`pwd`''" return $ERRCODE; } -# mv -f "${mib}.dmf.tmp" "${mib}.dmf" \ -# && rm -f "${mib}_TEST"{.c,.exe} "${mib}.json.tmp" +# mv -f "${ABS_BUILDDIR}/${mib}.dmf.tmp" "${ABS_BUILDDIR}/${mib}.dmf" \ +# && rm -f "${ABS_BUILDDIR}/${mib}_TEST"{.c,.exe} "${ABS_BUILDDIR}/${mib}.json.tmp" + + if [ -n "${ABS_OUTDIR-}" -a "${ABS_OUTDIR-}" != "${ABS_BUILDDIR}" ] ; then + mv -f "${ABS_BUILDDIR}/${mib}.dmf" "${ABS_OUTDIR}/" || return + fi } list_shared_sources() { @@ -196,12 +239,26 @@ list_shared_sources() { # somehow? Support a nested loop and separate storage var # to find many such files? SNAME="" - for F in ../../../drivers/snmp-ups-helpers.c ../../drivers/snmp-ups-helpers.c \ + + for F in \ + ../../../drivers/snmp-ups-helpers.c \ + ../../drivers/snmp-ups-helpers.c \ "${_SCRIPT_DIR}"/../../../drivers/snmp-ups-helpers.c \ "${_SCRIPT_DIR}"/../../drivers/snmp-ups-helpers.c \ ; do [ -s "$F" ] && SNAME="$F" && break done + + for F in \ + ../../../drivers/eaton-pdu-marlin-helpers.c \ + ../../drivers/eaton-pdu-marlin-helpers.c \ + "${_SCRIPT_DIR}"/../../../drivers/eaton-pdu-marlin-helpers.c \ + "${_SCRIPT_DIR}"/../../drivers/eaton-pdu-marlin-helpers.c \ + ; do + [ -s "$F" ] && SNAME="$SNAME $F" && break + done + + # echo the return value (string) echo "$SNAME" } @@ -219,19 +276,19 @@ dmfify_NUT_drivers() { SNAME="`list_shared_sources`" for cmib in ../../../drivers/*-mib.c ../../drivers/*-mib.c; do [ -s "${cmib}" ] || \ - { echo "ERROR: File not found or is empty: '${cmib}'" >&2; continue; } + { logmsg_error "File not found or is empty: '${cmib}'" ; continue; } # Note: helper sources must be arg 3+ below; # arg2 may be empty but must then be present dmfify_c_file "${cmib}" "" $SNAME || return i=$(($i+1)) done - [ "$i" = 0 ] && echo "ERROR: No files processed" >&2 && return 2 - echo "INFO: Processed $i files OK" >&2 + [ "$i" = 0 ] && logmsg_error "No files processed" && return 2 + logmsg_info "Processed $i files OK" return 0 } if [[ "$#" -gt 0 ]]; then - echo "INFO: Got some arguments, assuming they are NUT filenames for parsing" >&2 + logmsg_info "Got some arguments, assuming they are NUT filenames for parsing" SNAME="`list_shared_sources`" # Note: helper sources must be arg 3+ below; # arg2 may be empty but must then be present @@ -248,8 +305,8 @@ if [[ "$#" -gt 0 ]]; then shift done else - echo "INFO: No arguments provided, will try to parse all NUT drivers" >&2 + logmsg_info "No arguments provided, will try to parse all NUT drivers" dmfify_NUT_drivers || exit fi -echo "OK - All done" >&2 +logmsg_info "OK - All done" diff --git a/scripts/DMF/dmfnutscan/dmfnutscan-snmp.dmf b/scripts/DMF/dmfnutscan/dmfnutscan-snmp.dmf index 13b436d66f..9c8fed4342 100644 --- a/scripts/DMF/dmfnutscan/dmfnutscan-snmp.dmf +++ b/scripts/DMF/dmfnutscan/dmfnutscan-snmp.dmf @@ -1,6 +1,8 @@ + + @@ -10,6 +12,7 @@ + @@ -18,6 +21,7 @@ + @@ -27,7 +31,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/apc-ats-mib.dmf b/scripts/DMF/dmfsnmp/apc-ats-mib.dmf index 6cfdcf557a..47e2d2ac3e 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-epdu-mib.dmf b/scripts/DMF/dmfsnmp/apc-epdu-mib.dmf new file mode 100644 index 0000000000..9f63582ec3 --- /dev/null +++ b/scripts/DMF/dmfsnmp/apc-epdu-mib.dmf @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/DMF/dmfsnmp/apc-mib.dmf b/scripts/DMF/dmfsnmp/apc-mib.dmf index 9f6cf29968..6be83c3da9 100644 --- a/scripts/DMF/dmfsnmp/apc-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-mib.dmf @@ -4,6 +4,8 @@ + + @@ -13,6 +15,9 @@ + + + @@ -27,6 +32,22 @@ + + + + + + + + + + + + + + + + @@ -56,6 +77,9 @@ + + + @@ -182,6 +206,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/apc-pdu-mib.dmf b/scripts/DMF/dmfsnmp/apc-pdu-mib.dmf index 06a4938c33..ab314c5879 100644 --- a/scripts/DMF/dmfsnmp/apc-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-pdu-mib.dmf @@ -33,8 +33,8 @@ - - - + + + diff --git a/scripts/DMF/dmfsnmp/baytech-mib.dmf b/scripts/DMF/dmfsnmp/baytech-mib.dmf index 3fe64450cd..02b3fc1f24 100644 --- a/scripts/DMF/dmfsnmp/baytech-mib.dmf +++ b/scripts/DMF/dmfsnmp/baytech-mib.dmf @@ -5,11 +5,18 @@ + + + + + + + @@ -34,6 +41,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/bestpower-mib.dmf b/scripts/DMF/dmfsnmp/bestpower-mib.dmf index 933b6b3db4..d9594274e0 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..3d2e88c34e 100644 --- a/scripts/DMF/dmfsnmp/compaq-mib.dmf +++ b/scripts/DMF/dmfsnmp/compaq-mib.dmf @@ -54,17 +54,20 @@ + + + - - - - - + + + + + @@ -82,36 +85,32 @@ - + - - - - - - - - - - + + + + + + + + + - - + - - - - - - + + + + @@ -129,6 +128,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf index 72105a2395..3028a742dd 100644 --- a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf +++ b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf @@ -19,17 +19,42 @@ + + + + + - + + + + + + + + + + + + + + + + + + + + + @@ -38,6 +63,8 @@ + + @@ -47,24 +74,29 @@ + + - - - - - - - - - + + + + + + + + + + + - + + diff --git a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf index ea9f592c07..7331d26088 100644 --- a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf +++ b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf @@ -14,12 +14,15 @@ - + + + + @@ -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..eb0ebf921f 100644 --- a/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf @@ -1,5 +1,12 @@ + + + + + + + @@ -79,6 +86,9 @@ + + + @@ -111,6 +121,8 @@ + + @@ -142,6 +154,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf index 750afcc3ac..c5450f6ce6 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..0c6172ef4d 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf @@ -11,7 +11,7 @@ - + @@ -145,13 +145,16 @@ + + + - + @@ -338,7 +341,7 @@ - + @@ -365,11 +368,11 @@ - - + + @@ -377,6 +380,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-nlogic-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-nlogic-mib.dmf new file mode 100644 index 0000000000..9070945286 --- /dev/null +++ b/scripts/DMF/dmfsnmp/eaton-pdu-nlogic-mib.dmf @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf index b1ebff4b12..33bdb62141 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/powerware-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ups-pwnm2-mib.dmf similarity index 94% rename from scripts/DMF/dmfsnmp/powerware-mib.dmf rename to scripts/DMF/dmfsnmp/eaton-ups-pwnm2-mib.dmf index d5fc6286b8..69ed2aa6a1 100644 --- a/scripts/DMF/dmfsnmp/powerware-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ups-pwnm2-mib.dmf @@ -49,6 +49,25 @@ + + + + + + + + + + + + + + + + + + + @@ -68,13 +87,8 @@ - - - - - - - + + @@ -168,6 +182,9 @@ + + + @@ -180,6 +197,7 @@ + @@ -302,7 +320,6 @@ - - + diff --git a/scripts/DMF/dmfsnmp/eaton-ups-pxg-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ups-pxg-mib.dmf new file mode 100644 index 0000000000..2d9e5338ce --- /dev/null +++ b/scripts/DMF/dmfsnmp/eaton-ups-pxg-mib.dmf @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf b/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf index 197bf126ea..6ee782d53a 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..f71ef289b0 100644 --- a/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf @@ -3,7 +3,7 @@ - + @@ -116,6 +116,9 @@ + + + @@ -277,6 +280,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/hpe-pdu3-cis-mib.dmf b/scripts/DMF/dmfsnmp/hpe-pdu3-cis-mib.dmf new file mode 100644 index 0000000000..1e6e648545 --- /dev/null +++ b/scripts/DMF/dmfsnmp/hpe-pdu3-cis-mib.dmf @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/DMF/dmfsnmp/huawei-mib.dmf b/scripts/DMF/dmfsnmp/huawei-mib.dmf index b615b388b9..95eb9062d9 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..91d3636c1f 100644 --- a/scripts/DMF/dmfsnmp/ietf-mib.dmf +++ b/scripts/DMF/dmfsnmp/ietf-mib.dmf @@ -46,6 +46,9 @@ + + + @@ -54,7 +57,7 @@ - + @@ -65,11 +68,11 @@ - + - + @@ -80,11 +83,11 @@ - + - + @@ -92,8 +95,8 @@ - - + + @@ -119,12 +122,12 @@ - - + + - - + + @@ -133,7 +136,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/mge-mib.dmf b/scripts/DMF/dmfsnmp/mge-mib.dmf index 1f9e525c42..7681a61d84 100644 --- a/scripts/DMF/dmfsnmp/mge-mib.dmf +++ b/scripts/DMF/dmfsnmp/mge-mib.dmf @@ -72,7 +72,10 @@ - + + + + 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/raritan-pdu-mib.dmf b/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf index a5416f7b62..1132401e05 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..2b9a3fd142 100644 --- a/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf +++ b/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf @@ -24,7 +24,15 @@ - + + + + + + + + + @@ -35,6 +43,9 @@ + + + @@ -63,6 +74,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/xppc-mib.dmf b/scripts/DMF/dmfsnmp/xppc-mib.dmf index a03973eceb..6b039e011b 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/DMF/gen-legacy-mibfiles-list.sh b/scripts/DMF/gen-legacy-mibfiles-list.sh index d7d20e244c..8ce00141ea 100755 --- a/scripts/DMF/gen-legacy-mibfiles-list.sh +++ b/scripts/DMF/gen-legacy-mibfiles-list.sh @@ -9,7 +9,7 @@ # Be portable - no bash etc... plain minimal shell. Tested with bash, dash, # busybox sh and ksh for good measure. # -# Copyright (C) 2016 Jim Klimov +# Copyright (C) 2016-2024 Jim Klimov # # Relative to here we look for old sources @@ -54,7 +54,7 @@ print_makefile_LEGACY_NUT_DMF_RULES() { echo 'LEGACY_NUT_DMF_SYMLINKS =' echo 'INSTALL_NUT_DMF_SYMLINKS =' - printf '\n# NOTE: DMFSNMP_SUBDIR, DMFSNMP_RES_SUBDIR, DMFGEN_DEPS and DMFGEN_CMD,\n# and dmfsnmpdir and dmfsnmpresdir (for install) are defined in Makefile.am\n\n' + printf '\n# NOTE: DMFSNMP_SUBDIR, DMFSNMP_RES_SUBDIR, DMFGEN_DEPS and DMFGEN_CMD,\n# and dmfsnmpdir and dmfsnmpresdir (for install) are defined in Makefile.am\n# The ABS_OUTDIR in recipes below allows certain "make" implementations\n# to not try overwriting a SOURCE (e.g. DISTed and read-only) DMF file.\n\n' for CMIBBASE in `sort_LEGACY_NUT_C_MIBS` ; do case "$CMIBBASE" in @@ -62,7 +62,8 @@ print_makefile_LEGACY_NUT_DMF_RULES() { *) CMIBFILE='$(abs_top_srcdir)/drivers/'"$CMIBBASE" ;; esac DMFBASE="`basename "$CMIBBASE" .c`".dmf - DMFFILE='$(DMFSNMP_RES_SUBDIR)/'"$DMFBASE" + DMFSUBDIR='$(DMFSNMP_RES_SUBDIR)/' + DMFFILE="${DMFSUBDIR}${DMFBASE}" case "$DMFBASE" in ietf-mib.dmf) L="S90_${DMFBASE}" ;; S*|K*) ;; @@ -71,7 +72,7 @@ print_makefile_LEGACY_NUT_DMF_RULES() { DMFLINK='$(DMFSNMP_SUBDIR)/'"$L" printf 'LEGACY_NUT_C_MIBS +=\t%s\n' "$CMIBFILE" printf 'LEGACY_NUT_DMFS +=\t%s\n' "$DMFFILE" - printf '%s : %s $(DMFGEN_DEPS)\n\t@DMFFILE="%s"; CMIBFILE="%s"; $(DMFGEN_CMD)\n\n' "$DMFFILE" "$CMIBFILE" "$DMFFILE" "$CMIBFILE" + printf '%s : %s $(DMFGEN_DEPS)\n\t@DMFFILE="%s"; CMIBFILE="%s"; ABS_OUTDIR="$(abs_builddir)/%s"; $(DMFGEN_CMD)\n\n' "$DMFFILE" "$CMIBFILE" "$DMFFILE" "$CMIBFILE" "$DMFSUBDIR" printf 'LEGACY_NUT_DMF_SYMLINKS += %s\n' "$DMFLINK" printf '%s : %s\n\t@DMFFILE="%s"; DMFLINK="%s"; $(DMFLNK_CMD)\n\n' "$DMFLINK" "$DMFFILE" "$DMFFILE" "$DMFLINK" INSTFILE='$(DESTDIR)$(dmfsnmpresdir)/'"$DMFBASE" diff --git a/scripts/DMF/jsonify-mib.py.in b/scripts/DMF/jsonify-mib.py.in index 5258f13563..6ecc2649af 100644 --- a/scripts/DMF/jsonify-mib.py.in +++ b/scripts/DMF/jsonify-mib.py.in @@ -7,6 +7,7 @@ # # Copyright (C) 2016 Michal Vyskocil # Copyright (C) 2016 - 2021 Jim Klimov +# Copyright (C) 2022 - 2024 Jim Klimov # from __future__ import print_function @@ -32,17 +33,23 @@ Michal Vyskocil Jim Klimov """ +# Can be used for temporary file locations, if defined: +ABS_BUILDDIR = os.environ.get("ABS_BUILDDIR", None) + + # TODO: # - read the file to find and prints the comments def warn (msg): - print ("W: %s" % msg, file=sys.stderr) + if os.environ.get("DEBUG", "") in ["yes", "1"]: + print ("W: %s" % msg, file=sys.stderr) def info (msg): - print ("I: %s" % msg, file=sys.stderr) + if os.environ.get("DEBUG", "") in ["yes", "1", ""]: + print ("I: %s" % msg, file=sys.stderr) def debug (msg): - if os.environ.get("DEBUG") == "yes": + if os.environ.get("DEBUG", "") in ["yes", "1"]: print ("D: %s" % msg, file=sys.stderr) def f2f(node): @@ -346,8 +353,14 @@ class Visitor(c_ast.NodeVisitor): if node.type.type.type.names == ['snmp_info_t']: self._mappings ["SNMP-INFO"][node.name] = self._visit_snmp_info_t (node) elif node.type.type.type.names == ['info_lkp_t']: - self._mappings ["INFO"][node.type.type.declname] = \ - self._visit_info_lkp_t (node) + # Anticipate several sources (helpers and all), so if one + # of them lacks some mapping info - do not delete what we + # got from others :) + tmp = self._visit_info_lkp_t (node) + if self._mappings ["INFO"].get(node.type.type.declname) is None: + self._mappings ["INFO"][node.type.type.declname] = tmp + else: + self._mappings ["INFO"][node.type.type.declname].extend(tmp) elif node.type.type.type.names == ['alarms_info_t']: self._mappings ["ALARMS-INFO"][node.type.type.declname] = \ self._visit_alarms_info_t (node) @@ -445,7 +458,11 @@ static mib2nut_info_t %(name)s_TEST = { %(mib_name)s, %(mib_version)s, %(oid_pwr def s_json2c (fout, MIB_name, js): print (""" +#include "config.h" + #include +#include +#include #include "main.h" #include "snmp-ups.h" @@ -465,17 +482,21 @@ int input_phases, output_phases, bypass_phases; // Replicate what drivers/main.c exports int do_synchronous = 0; +int do_debug = 1; + static inline bool streq (const char* x, const char* y) { if (!x && !y) return true; if (!x || !y) { - fprintf(stderr, "\\nDEBUG: strEQ(): One compared string (but not both) is NULL:\\n\\t%%s\\n\\t%%s\\n\\n", x ? x : "" , y ? y : ""); + if (do_debug) + fprintf(stderr, "\\nDEBUG: strEQ(): One compared string (but not both) is NULL:\\n\\t%%s\\n\\t%%s\\n\\n", x ? x : "" , y ? y : ""); return false; } int cmp = strcmp (x, y); if (cmp != 0) { - fprintf(stderr, "\\nDEBUG: strEQ(): Strings not equal (%%i):\\n\\t%%s\\n\\t%%s\\n\\n", cmp, x, y); + if (do_debug) + fprintf(stderr, "\\nDEBUG: strEQ(): Strings not equal (%%i):\\n\\t%%s\\n\\t%%s\\n\\n", cmp, x, y); } return cmp == 0; } @@ -483,7 +504,8 @@ static inline bool streq (const char* x, const char* y) static inline bool strneq (const char* x, const char* y) { if (!x && !y) { - fprintf(stderr, "\\nDEBUG: strNEQ(): Both compared strings are NULL\\n"); + if (do_debug) + fprintf(stderr, "\\nDEBUG: strNEQ(): Both compared strings are NULL\\n"); return false; } if (!x || !y) { @@ -491,7 +513,8 @@ static inline bool strneq (const char* x, const char* y) } int cmp = strcmp (x, y); if (cmp == 0) { - fprintf(stderr, "\\nDEBUG: strNEQ(): Strings are equal (%%i):\\n\\t%%s\\n\\t%%s\\n\\n", cmp, x, y); + if (do_debug) + fprintf(stderr, "\\nDEBUG: strNEQ(): Strings are equal (%%i):\\n\\t%%s\\n\\t%%s\\n\\n", cmp, x, y); } return cmp != 0; } @@ -507,24 +530,52 @@ static inline bool strneq (const char* x, const char* y) # generate test function print (""" int main () { - size_t i = 0;""", file=fout) + size_t i = 0; + char *s; + +/* Older CLANG (e.g. clang-3.4) sees short strings in str{n}cmp() + * arguments as arrays and claims out-of-bounds accesses + */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Warray-bounds" +#endif + if ((s = getenv("DEBUG")) && !strcmp(s, "0")) + do_debug = 0; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic pop +#endif + +""", file=fout) for key in js["INFO"]: print (""" - fprintf (stderr, "Test %(k)s: "); + if (do_debug) + fprintf (stderr, "Test %(k)s: "); for (i = 0; %(k)s_TEST [i].oid_value != 0 && %(k)s_TEST [i].info_value != NULL; i++) { - fprintf (stderr, "[%%zi] ", i); + if (do_debug) + fprintf (stderr, "[%%zi] ", i); assert (%(k)s [i].oid_value == %(k)s_TEST [i].oid_value); assert (%(k)s [i].info_value && %(k)s_TEST [i].info_value); assert (streq (%(k)s [i].info_value, %(k)s_TEST [i].info_value)); } - fprintf (stderr, "OK\\n");""" % {'k' : key}, file=fout) + if (do_debug) + fprintf (stderr, "OK\\n");""" % {'k' : key}, file=fout) for key in js ["SNMP-INFO"].keys (): print (""" - fprintf (stderr, "Test %(k)s: "); + if (do_debug) + fprintf (stderr, "Test %(k)s: "); for (i = 0; %(k)s_TEST [i].info_type != NULL; i++) { - fprintf (stderr, "[%%zi] ", i); + if (do_debug) + fprintf (stderr, "[%%zi] ", i); assert (streq (%(k)s [i].info_type, %(k)s_TEST [i].info_type)); assert (%(k)s [i].info_flags == %(k)s_TEST [i].info_flags); assert (%(k)s [i].info_len == %(k)s_TEST [i].info_len); @@ -532,17 +583,21 @@ int main () { assert (streq (%(k)s [i].dfl, %(k)s_TEST [i].dfl)); assert (%(k)s [i].flags == %(k)s_TEST [i].flags); if (%(k)s [i].oid2info != %(k)s_TEST [i].oid2info) { - fprintf (stderr, "%(k)s[%%zi].oid2info =<%%p>\\n", i, %(k)s[i].oid2info); - fprintf (stderr, "%(k)s_TEST[%%zi].oid2info=<%%p>\\n", i, %(k)s_TEST[i].oid2info); + if (do_debug) + fprintf (stderr, "%(k)s[%%zi].oid2info =<%%p>\\n", i, %(k)s[i].oid2info); + if (do_debug) + fprintf (stderr, "%(k)s_TEST[%%zi].oid2info=<%%p>\\n", i, %(k)s_TEST[i].oid2info); return 1; } // assert (%(k)s [i].setvar == %(k)s_TEST [i].setvar); } - fprintf (stderr, "OK\\n");""" % {'k' : key}, file=fout) + if (do_debug) + fprintf (stderr, "OK\\n");""" % {'k' : key}, file=fout) for key in js ["MIB2NUT"].keys (): print (""" - fprintf (stderr, "Test %(k)s\\n"); + if (do_debug) + fprintf (stderr, "Test %(k)s\\n"); assert (streq (%(k)s_TEST.mib_name, %(k)s.mib_name)); assert (streq (%(k)s_TEST.mib_version, %(k)s.mib_version)); assert (streq (%(k)s_TEST.oid_pwr_status, %(k)s.oid_pwr_status)); @@ -638,6 +693,9 @@ def test_compile(MIB_name, source = None): if source is None: test_file = MIB_name + "_TEST.c" obj_file = MIB_name + "_TEST.o" + if ABS_BUILDDIR is not None: + test_file = os.path.sep.join([ABS_BUILDDIR, test_file]) + obj_file = os.path.sep.join([ABS_BUILDDIR, obj_file]) info ("CALL test_compile(): generate back %s from JSON, to compile below" % (test_file)) with open (test_file, "wt") as fout: s_json2c (fout, MIB_name, v._mappings) @@ -645,6 +703,9 @@ def test_compile(MIB_name, source = None): # Take the additional shared source as is to link it in test_file = source obj_file = MIB_name + "_" + os.path.splitext (os.path.basename (test_file)) [0] + "_TEST.o" + if ABS_BUILDDIR is not None: + # Use as is? # test_file = os.path.sep.join([ABS_BUILDDIR, test_file]) + obj_file = os.path.sep.join([ABS_BUILDDIR, obj_file]) try: gcc = os.environ["CC"] @@ -692,6 +753,10 @@ if args.test: test_file = os.path.basename (args.source) MIB_name = os.path.splitext (test_file) [0] prog_file = MIB_name + "_TEST.exe" + if ABS_BUILDDIR is not None: + prog_file = os.path.sep.join([ABS_BUILDDIR, prog_file]) + else: + prog_file = os.path.sep.join([".", prog_file]) obj_files = [] # First file is generated from our JSON: @@ -737,7 +802,7 @@ if args.test: sys.exit (retcode.returncode) info ("SELFTEST ./" + prog_file) try: - subprocess.check_call ("./%s" % prog_file) + subprocess.check_call (prog_file) except subprocess.CalledProcessError as retcode: warn ("SELFTEST FAILED with code %s" % retcode.returncode) sys.exit (retcode.returncode) diff --git a/scripts/DMF/nut_cpp b/scripts/DMF/nut_cpp index ca914e4f95..46b803c7d3 100755 --- a/scripts/DMF/nut_cpp +++ b/scripts/DMF/nut_cpp @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # The pipeline to convert legacy *-mib.c files with structures into new DMF # markup involves parsing the C code. The pycparser (Python module) used @@ -9,6 +9,7 @@ # # Copyright (C) 2016 Michal Vyskocil # Copyright (C) 2016 - 2021 Jim Klimov +# Copyright (C) 2022 - 2024 Jim Klimov # [ -n "${CPP-}" ] || CPP="cpp" @@ -21,50 +22,59 @@ set -o pipefail #set -exv -echo "PREPROCESS: ( cd `pwd` && $CPP $CFLAGS $CPPFLAGS $* )" >&2 +# Hush when "make V=0" +if [ x"${DEBUG-}" = x -o x"${DEBUG-}" = x1 -o x"${V-}" = x1 ] ; then + echo "PREPROCESS: ( cd `pwd` && $CPP $CFLAGS $CPPFLAGS $* )" >&2 +fi do_filter() { +if [ "${DEBUG_NUT_CPP_DISABLE_FILTER-}" = "true" ] ; then + cat + return +fi + +# NOTE: "Directives not supported yet" in practice means #include <...> +# ones which were hidden by ifdef's but now pop up in pre-processed text +# somehow... So we filter those out too. + +# \t in sed is not always portable, better use raw char value +TABCHAR="`printf '\t'`" +SPACETAB="[ $TABCHAR]" + # tee temp-cpp-orig.tmp | \ sed \ - -e '/^[ \t]*$/d' \ - -e 's/^[ \t]*;/;/g' | \ + -e '/^'"${SPACETAB}"'*$/d' \ + -e '/^'"${SPACETAB}"'*#'"${SPACETAB}"'*include/d' \ + -e 's/^'"${SPACETAB}"'*;/;/g' | \ perl -0pe 's/\n([^#].*)\n;/\n$1;\n/g' | \ sed \ - -e 's/ __asm(.*)$/;/' \ - -e 's/__asm__ (.*);/;/' \ - -e 's/__attribute__ ((visibility("default"))) //' \ - -e 's/__attribute__((noreturn))//' \ - -e 's/__attribute__((unused))//' \ - -e 's/__attribute__((aligned(8)));/;/' \ - -e 's/__attribute__((availability(.*)));/;/' \ - -e 's/__attribute__((availability(.*)))//' \ - -e 's/__attribute__((format.*)));$/;/' \ - -e 's/__inline__/inline/' \ - -e 's/__signed__/signed/' \ - -e 's/__unsigned__/unsigned/' \ + -e 's/__asm_*'"${SPACETAB}"'*(.*)'"${SPACETAB}"'*;/;/g' \ + -e 's/__attribute__'"${SPACETAB}"'*((['"${SPACETAB}"'*__[A-Za-z0-9_-]*__'"${SPACETAB}"'*([^()]*)))'"${SPACETAB}"'*/ /g' \ + -e 's/__attribute__'"${SPACETAB}"'*((__aligned__([^()]*([^()]*))))'"${SPACETAB}"'*/ /g' \ + -e 's/__attribute__'"${SPACETAB}"'*((aligned([0-9]*)))'"${SPACETAB}"'*/ /g' \ + -e 's/__attribute__'"${SPACETAB}"'*(([^()]*))'"${SPACETAB}"'*/ /g' \ + -e 's/__inline__/inline/g' \ + -e 's/__signed__/signed/g' \ + -e 's/__unsigned__/unsigned/g' \ -e 's/__THROW;$/;/' \ - -e 's/__END_DECLS//' \ - -e 's/__BEGIN_DECLS//' \ - -e 's/void (\*signal(int, void (\*)(int)))(int);//' \ - -e 's/__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__));/;/' \ - -e 's/__attribute__ ((__mode__ (__[A-Z][A-Z]__)));/;/' \ - -e 's/__attribute__ ((__mode__ (__word__)));/;/' \ - -e 's/__attribute__ ((__nothrow__ , __leaf__));/;/' \ - -e 's/__attribute__ ((__nonnull__ ([0-9])));/;/' \ - -e 's/__attribute__ ((__nothrow__ , __leaf__)) //' \ - -e 's/__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;/;/' \ - -e 's/__attribute__ ((__const__))//' \ - -e 's/__attribute__.*;/;/' \ - -e 's/static __inline/static/' \ - -e 's/__extern_inline/extern/' \ - -e 's/extern __inline/extern/' \ - -e 's/_Float[0-9]*x*/float/' \ + -e 's/__END_DECLS//g' \ + -e 's/__BEGIN_DECLS//g' \ + -e 's/void'"${SPACETAB}"'(\*signal(int, void (\*)(int)))(int);//g' \ + -e 's/static'"${SPACETAB}"'__inline/static/g' \ + -e 's/static'"${SPACETAB}"'*;/;/g' \ + -e 's/__extern_inline/extern/g' \ + -e 's/extern'"${SPACETAB}"'__inline/extern/g' \ + -e 's/_Float[0-9]*x*/float/g' \ -e 's/__restrict//g' \ - -e 's/__extension__//' \ - -e 's/[ \t]_Noreturn[ \t]/ /' \ - -e 's/__NORETURN_*//' \ - -e 's/__builtin_va_list/int/' + -e 's/_*_Nullable//g' \ + -e 's/_*_Nonnull//g' \ + -e 's/_Thread_local//g' \ + -e 's/__extension__//g' \ + -e 's/'"${SPACETAB}"'_Noreturn'"${SPACETAB}"'/ /g' \ + -e 's/^_Noreturn'"${SPACETAB}"'/ /g' \ + -e 's/__NORETURN_*//g' \ + -e 's/__builtin_va_list/int/g' } # // end of do_filter() # Repeat same filter a few times to strip appearing blank lines and trailing semicolons @@ -76,7 +86,10 @@ $CPP $CFLAGS $CPPFLAGS "${@}" | \ RET=$? if [ "$RET" = 0 ]; then - echo "INFO: CPP+SED passed OK" >&2 + # Hush when "make V=0" + if [ x"${DEBUG-}" = x -o x"${DEBUG-}" = x1 -o x"${V-}" = x1 ] ; then + echo "INFO: CPP+SED passed OK" >&2 + fi else echo "ERROR: CPP+SED returned code $RET" >&2 # Invalidate the C code so pycparser detects the problem early diff --git a/scripts/DMF/xmlify-mib.py.in b/scripts/DMF/xmlify-mib.py.in index 6a6450977b..2ea703d5cc 100644 --- a/scripts/DMF/xmlify-mib.py.in +++ b/scripts/DMF/xmlify-mib.py.in @@ -8,6 +8,7 @@ # Copyright (C) 2016 Carlos Dominguez # Copyright (C) 2016 - 2021 Jim Klimov # Copyright (C) 2019 Arnaud Quette +# Copyright (C) 2022 - 2024 Jim Klimov # from __future__ import print_function @@ -93,10 +94,11 @@ def die (msg): sys.exit (1) def warn (msg): - print ("W: " + msg, file=sys.stderr) + if os.environ.get("DEBUG", "") in ["yes", "1"]: + print ("W: " + msg, file=sys.stderr) def debug (msg): - if os.environ.get("DEBUG") == "yes": + if os.environ.get("DEBUG", "") in ["yes", "1"]: print ("D: " + msg, file=sys.stderr) def mkElement (_element, **attrs): diff --git a/scripts/HP-UX/makedepot.sh b/scripts/HP-UX/makedepot.sh index 698bccf44b..82a47db2c0 100755 --- a/scripts/HP-UX/makedepot.sh +++ b/scripts/HP-UX/makedepot.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash -set -x +set -x CUR_DIR=$(pwd) TOP_DIR=$CUR_DIR/../.. @@ -26,5 +26,4 @@ cd $CUR_DIR swpackage -s nut.psf -d $CUR_DIR/nut.depot; \ #tar cvf nut.depot.tar nut.depot #gzip nut.depot.tar -echo "Execution completed" - +echo "Execution completed" diff --git a/scripts/HP-UX/nut-drvctl.sh b/scripts/HP-UX/nut-drvctl.sh index 9f7286e11b..f153909f15 100755 --- a/scripts/HP-UX/nut-drvctl.sh +++ b/scripts/HP-UX/nut-drvctl.sh @@ -24,6 +24,9 @@ umask 022 PATH=/usr/sbin:/usr/bin:/sbin export PATH +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + WHAT='NUT UPS driver (Network UPS Tools -- http://www.exploits.org/nut)' WHAT_PATH=/opt/nut/bin/upsdrvctl WHAT_CONFIG=/etc/rc.config.d/nut-drvctl diff --git a/scripts/HP-UX/nut-upsd.sh b/scripts/HP-UX/nut-upsd.sh index 454cfe8732..d5dcd36a3a 100755 --- a/scripts/HP-UX/nut-upsd.sh +++ b/scripts/HP-UX/nut-upsd.sh @@ -24,6 +24,9 @@ umask 022 PATH=/usr/sbin:/usr/bin:/sbin export PATH +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + WHAT='NUT UPS daemon (Network UPS Tools -- http://www.exploits.org/nut)' WHAT_PATH=/opt/nut/sbin/upsd WHAT_CONFIG=/etc/rc.config.d/nut-upsd diff --git a/scripts/HP-UX/nut-upsmon.sh b/scripts/HP-UX/nut-upsmon.sh index 2c7fc95bef..88746431cb 100755 --- a/scripts/HP-UX/nut-upsmon.sh +++ b/scripts/HP-UX/nut-upsmon.sh @@ -24,6 +24,9 @@ umask 022 PATH=/usr/sbin:/usr/bin:/sbin export PATH +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + WHAT='NUT UPS monitor (Network UPS Tools -- http://www.exploits.org/nut)' WHAT_PATH=/opt/nut/sbin/upsmon WHAT_CONFIG=/etc/rc.config.d/nut-upsmon diff --git a/scripts/HP-UX/postinstall.in b/scripts/HP-UX/postinstall.in index 8111b6a43c..8650d2d81d 100644 --- a/scripts/HP-UX/postinstall.in +++ b/scripts/HP-UX/postinstall.in @@ -2,7 +2,7 @@ # directory definitions NUT_DIR="@prefix@" -INSTALLPATH=$NUT_DIR/script +INSTALLPATH="$NUT_DIR/script" CONFIGPATH=/etc/rc.config.d SCRIPTPATH=/sbin/init.d LINKPATH=/sbin/rc3.d @@ -36,13 +36,18 @@ for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf; do fi done +# make sure that /var/run exists (for privileged processes) +if [ ! -d @PIDPATH@ ] ; then + mkdir -p @PIDPATH@ +fi + # make sure that /var/run/nut exists and has the correct ownerships -if [ ! -d @PIDPATH@/nut ] ; then - mkdir -p @PIDPATH@/nut +if [ ! -d @ALTPIDPATH@ ] ; then + mkdir -p @ALTPIDPATH@ fi -if [ -d @PIDPATH@/nut ] ; then - chown root:nut @PIDPATH@/nut - chmod 770 @PIDPATH@/nut +if [ -d @ALTPIDPATH@ ] ; then + chown root:nut @ALTPIDPATH@ + chmod 770 @ALTPIDPATH@ fi # make sure that /var/state/ups exists and has the correct ownerships diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 82c401ab03..40143a59d3 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,4 +1,7 @@ -EXTRA_DIST = README \ +# Network UPS Tools: scripts (root) + +EXTRA_DIST = \ + README.adoc \ avahi/nut.service.in \ HP-UX/nut-drvctl \ HP-UX/nut-drvctl.sh \ @@ -11,25 +14,50 @@ EXTRA_DIST = README \ misc/osd-notify \ perl/Nut.pm \ RedHat/halt.patch \ - RedHat/README \ - RedHat/ups \ - RedHat/upsd \ + RedHat/README.adoc \ + RedHat/ups.in \ RedHat/upsd.in \ - RedHat/upsmon \ RedHat/upsmon.in \ Solaris8/S99upsmon \ subdriver/gen-usbhid-subdriver.sh \ subdriver/gen-snmp-subdriver.sh \ - ufw/README \ + upower/95-upower-hid.hwdb \ upower/95-upower-hid.rules \ + usb_resetter/README.adoc \ + usb_resetter/nut-driver.service \ Windows/halt.c \ Windows/Makefile -SUBDIRS = augeas devd hotplug python systemd udev ufw Solaris upsdrvsvcctl +SUBDIRS = augeas devd hotplug installer python systemd udev ufw Solaris Windows upsdrvsvcctl + if WITH_SNMP if WITH_NEON SUBDIRS += DMF endif endif +SPELLCHECK_SRC = README.adoc RedHat/README.adoc usb_resetter/README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked RedHat/*-spellchecked usb_resetter/*-spellchecked + MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/README b/scripts/README deleted file mode 100644 index 177b417b4d..0000000000 --- a/scripts/README +++ /dev/null @@ -1,13 +0,0 @@ -These directories hold various scripts: -- example startup and shutdown scripts for various operating systems and -distributions, -- hotplug and udev integration for on the fly privileges settings (Linux only), -- UPower (previously DeviceKit-power) rules file, -- Python Client module and application, -- Perl client module, -- Augeas support lenses and modules for NUT, -- scripts to generate DMF data files from existing drivers/*-mib.c structures, -- systemd support files. - -They have either been contributed by users of the software, or by the NUT Team -itself. diff --git a/scripts/README.adoc b/scripts/README.adoc new file mode 100644 index 0000000000..34870a883a --- /dev/null +++ b/scripts/README.adoc @@ -0,0 +1,27 @@ +NUT contributed and integration scripts +======================================= + +These directories hold various scripts and resources needed for systems +integration, and other non-core functionality, which were contributed +over time by the NUT Team, side projects and/or general community members +(users of the software), including: + +- example startup and shutdown scripts for various operating systems and + distributions, +- `hotplug` and `udev` integration for on the fly privileges settings + (Linux only), +- `UPower` (previously `DeviceKit-power`) rules file, +- Python Client module and application, +- Perl client module, +- Augeas support lenses and modules for NUT, +- scripts to generate DMF data files from existing `drivers/*-mib.c` + structures (mapping and lookup tables, etc.), +- support to run NUT components as service unit instances: + * shared `nut-driver-enumerator` and `upsdrvsvcctl` logic, + * `systemd` support files, + * SMF (Solaris/illumos Service Management Framework) support files, +- init-scripts and/or packaging elements for several Unix operating systems, +- Windows build helpers and information, +- software-driven USB reset suggestions and helpers (for stuck devices), +- `logrotate` integration, +- and many others. diff --git a/scripts/RedHat/.gitignore b/scripts/RedHat/.gitignore new file mode 100644 index 0000000000..63cffeda23 --- /dev/null +++ b/scripts/RedHat/.gitignore @@ -0,0 +1,5 @@ +# Generated from .in templates: +/ups +/upsd +/upsmon + diff --git a/scripts/RedHat/README b/scripts/RedHat/README deleted file mode 100644 index 4ce4fbe61b..0000000000 --- a/scripts/RedHat/README +++ /dev/null @@ -1,4 +0,0 @@ -Install ups in /etc/rc.d/init.d - -/etc/sysconfig/ups is used only to store the POWERDOWNFLAG variable - diff --git a/scripts/RedHat/README.adoc b/scripts/RedHat/README.adoc new file mode 100644 index 0000000000..8de3b7be46 --- /dev/null +++ b/scripts/RedHat/README.adoc @@ -0,0 +1,6 @@ +NUT integration files for older RedHat based Linux distributions +================================================================ + +Install `ups` in `/etc/rc.d/init.d` + +The `/etc/sysconfig/ups` is used only to store the `POWERDOWNFLAG` variable diff --git a/scripts/RedHat/ups b/scripts/RedHat/ups deleted file mode 100644 index c448a09484..0000000000 --- a/scripts/RedHat/ups +++ /dev/null @@ -1,3 +0,0 @@ -# POWERDOWNFLAG *must* match that in upsmon.conf -POWERDOWNFLAG=/etc/killpower -NUTUSER=nutmon diff --git a/scripts/RedHat/ups.in b/scripts/RedHat/ups.in new file mode 100644 index 0000000000..ceed0377ec --- /dev/null +++ b/scripts/RedHat/ups.in @@ -0,0 +1,3 @@ +# POWERDOWNFLAG *must* match that in upsmon.conf +POWERDOWNFLAG="@POWERDOWNFLAG@" +NUTUSER="@RUN_AS_USER@" diff --git a/scripts/RedHat/upsd b/scripts/RedHat/upsd deleted file mode 100644 index c682cb7b65..0000000000 --- a/scripts/RedHat/upsd +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/sh -# -# chkconfig: 2345 30 90 -# -# 2003-01-31 Antonino Albanese -# Removed all old statements -# start programs as user nut -# new style starting and stopping upsd -# added reload option -# use of /etc/sysconfig/ups for POWERDOWNFLAG variable retrieving -# -# 2002-02-07 Nigel Metheringham -# made ups.conf pre-eminant, added new upsdrvctl functions, targeted for RH7.2, should -# work OK on RH 6.x, 7.x -# 2001-10-24 Peter Bieringer -# enhancements for new style drivers and controls, tested on a RHL 7.1.93 system -# -# description: NUT upsd and its drivers directly monitor a ups and \ -# make information from it available to other programs -# processname: upsd -# config: /etc/ups/upsd.conf -# config: /etc/ups/ups.conf - -PATH=/sbin:/bin:/usr/sbin:/usr/bin -export PATH - -# Source function library. -. /etc/rc.d/init.d/functions - -# POWERDOWNFLAG *must* match that in upsmon.conf -# Loading POWERDOWNFLAG from /etc/sysconfig/ups -DRIVERPATH=/sbin -if [ -f /etc/sysconfig/ups ]; then - . /etc/sysconfig/ups -else - POWERDOWNFLAG=/etc/killpower - NUTUSER=nutmon -fi -UPSDCONF=/etc/ups/upsd.conf -UPSCONF=/etc/ups/ups.conf - -# if there are no config file, bail out -[ -f $UPSDCONF ] && [ -f $UPSCONF ] || exit 0 - -runcmd() { - echo -n "$1 " - shift - if [ "$BOOTUP" = "color" ]; then - $* && echo_success || echo_failure - else - $* - fi - echo -} - -# See how we are called. -case "$1" in - start) - # new style drivers uses 'upsdrvctl' - echo -n "NUT Starting UPS model drivers: " - # starting ase nut user - daemon --user $NUTUSER `which upsdrvctl` start - echo - if [ $? -eq 0 ]; then - echo -n "NUT Starting UPS daemon: " - # starting ase nut user - daemon upsd -u $NUTUSER - echo - touch /var/lock/subsys/upsd - fi - ;; - - stop) - # new style upsd stop - action "NUT Stopping UPS daemon" \ - upsd -c stop - # new style drivers uses 'upsdrvctl' - action "NUT Stopping UPS model drivers" \ - upsdrvctl stop - - rm -f /var/lock/subsys/upsd - ;; - - powerdown) - # new style drivers - runcmd "NUT powerdown of attached UPS(es)" upsdrvctl shutdown - ;; - - restart) - $0 stop - $0 start - ;; - - reload) - # reloading upsd config files - action "NUT Reloading config files" \ - upsd -c reload - ;; - - status) - # new style drivers - action "NUT: checking UPS model drivers" upsdrvctl status - - status upsd - ;; - *) - echo "Usage: upsd {start|stop|powerdown|restart|reload|status}" - exit 1 -esac - diff --git a/scripts/RedHat/upsd.in b/scripts/RedHat/upsd.in index d2aeca92ed..c5063209b8 100644 --- a/scripts/RedHat/upsd.in +++ b/scripts/RedHat/upsd.in @@ -2,6 +2,9 @@ # # chkconfig: 2345 30 90 # +# 2022-01-04 Jim Klimov +# Updated .in template, double-quoted variable expansions +# # 2003-01-31 Antonino Albanese # Removed all old statements # start programs as user nut @@ -18,10 +21,10 @@ # description: NUT upsd and its drivers directly monitor a ups and \ # make information from it available to other programs # processname: upsd -# config: @sysconfdir@/upsd.conf -# config: @sysconfdir@/ups.conf +# config: @CONFPATH@/upsd.conf +# config: @CONFPATH@/ups.conf -PATH=/sbin:/bin:/usr/sbin:/usr/bin +PATH="/sbin:/bin:/usr/sbin:/usr/bin" export PATH # Source function library. @@ -29,18 +32,29 @@ export PATH # POWERDOWNFLAG *must* match that in upsmon.conf # Loading POWERDOWNFLAG from /etc/sysconfig/ups -DRIVERPATH=@DRVPATH@ +DRIVERPATH="@DRVPATH@" +NUT_SBINDIR="@SBINDIR@" if [ -f /etc/sysconfig/ups ]; then . /etc/sysconfig/ups else - POWERDOWNFLAG=/etc/killpower - NUTUSER=nutmon + POWERDOWNFLAG="@POWERDOWNFLAG@" + NUTUSER="@RUN_AS_USER@" +fi +UPSDCONF="@CONFPATH@/upsd.conf" +UPSCONF="@CONFPATH@/ups.conf" + +if [ -n "$DRIVERPATH" -a -d "$DRIVERPATH" ]; then + PATH="$DRIVERPATH:$PATH" +fi +if [ -n "$NUT_SBINDIR" -a -d "$NUT_SBINDIR" ]; then + PATH="$NUT_SBINDIR:$PATH" fi -UPSDCONF=@sysconfdir@/upsd.conf -UPSCONF=@sysconfdir@/ups.conf # if there are no config file, bail out -[ -f $UPSDCONF ] && [ -f $UPSCONF ] || exit 0 +[ -f "$UPSDCONF" ] && [ -f "$UPSCONF" ] || exit 0 + +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY runcmd() { echo -n "$1 " @@ -58,13 +72,13 @@ case "$1" in start) # new style drivers uses 'upsdrvctl' echo -n "NUT Starting UPS model drivers: " - # starting ase nut user - daemon --user $NUTUSER `which upsdrvctl` start + # starting all drivers as nut user + daemon --user "$NUTUSER" "`which upsdrvctl`" start echo if [ $? -eq 0 ]; then echo -n "NUT Starting UPS daemon: " - # starting ase nut user - daemon upsd -u $NUTUSER + # starting as nut user + daemon upsd -u "$NUTUSER" echo touch /var/lock/subsys/upsd fi @@ -107,4 +121,3 @@ case "$1" in echo "Usage: upsd {start|stop|powerdown|restart|reload|status}" exit 1 esac - diff --git a/scripts/RedHat/upsmon b/scripts/RedHat/upsmon deleted file mode 100644 index b6d77f73bc..0000000000 --- a/scripts/RedHat/upsmon +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# -# chkconfig: 2345 31 89 -# -# 2003-01-31 Antonino Albanese -# start program as user nut -# new style stopping upsmon -# added reload option -# -# description: upsmon talks to upsd and notifies of ups status changes \ -# also shutting systems down if required. -# processname: upsmon -# config: /etc/ups/upsmon.conf - -PATH=/sbin:/bin:/usr/sbin:/usr/bin -export PATH - -# Source function library. -. /etc/rc.d/init.d/functions - -if [ -f /etc/sysconfig/ups ]; then - . /etc/sysconfig/ups -else - POWERDOWNFLAG=/etc/killpower - NUTUSER=nutmon -fi - -# See how we are called. -case "$1" in - start) - action "NUT Starting UPS monitor" upsmon -u $NUTUSER - touch /var/lock/subsys/upsmon - ;; - stop) - action "NUT Stopping UPS monitor: " \ - upsmon -c stop - - rm -f /var/lock/subsys/upsmon - ;; - restart) - $0 stop - $0 start - ;; - reload) - action "NUT Reloading UPS monitor: " \ - upsmon -c reload - - ;; - status) - status upsmon - ;; - *) - echo "Usage: upsmon {start|stop|restart|reload|status}" - exit 1 -esac - diff --git a/scripts/RedHat/upsmon.in b/scripts/RedHat/upsmon.in index af8a7fc806..9cee760500 100644 --- a/scripts/RedHat/upsmon.in +++ b/scripts/RedHat/upsmon.in @@ -2,6 +2,9 @@ # # chkconfig: 2345 31 89 # +# 2022-01-04 Jim Klimov +# Updated .in template, double-quoted variable expansions +# # 2003-01-31 Antonino Albanese # start program as user nut # new style stopping upsmon @@ -10,7 +13,7 @@ # description: upsmon talks to upsd and notifies of ups status changes \ # also shutting systems down if required. # processname: upsmon -# config: @sysconfdir@/upsmon.conf +# config: @CONFPATH@/upsmon.conf PATH=/sbin:/bin:/usr/sbin:/usr/bin export PATH @@ -18,17 +21,24 @@ export PATH # Source function library. . /etc/rc.d/init.d/functions +NUT_SBINDIR="@SBINDIR@" if [ -f /etc/sysconfig/ups ]; then . /etc/sysconfig/ups else - POWERDOWNFLAG=/etc/killpower - NUTUSER=nutmon + POWERDOWNFLAG="@POWERDOWNFLAG@" + NUTUSER="@RUN_AS_USER@" +fi +if [ -n "$NUT_SBINDIR" -a -d "$NUT_SBINDIR" ]; then + PATH="$NUT_SBINDIR:$PATH" fi +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + # See how we are called. case "$1" in start) - action "NUT Starting UPS monitor" upsmon -u $NUTUSER + action "NUT Starting UPS monitor" upsmon -u "$NUTUSER" touch /var/lock/subsys/upsmon ;; stop) @@ -53,4 +63,3 @@ case "$1" in echo "Usage: upsmon {start|stop|restart|reload|status}" exit 1 esac - diff --git a/scripts/Solaris/Makefile.am b/scripts/Solaris/Makefile.am index 1544c797d3..e142a02f4d 100644 --- a/scripts/Solaris/Makefile.am +++ b/scripts/Solaris/Makefile.am @@ -1,4 +1,6 @@ -EXTRA_DIST = makelocal.sh precheck.py.in preproto.pl.in README +# Network UPS Tools: scripts/Solaris + +EXTRA_DIST = makelocal.sh precheck.py.in preproto.pl.in README.adoc PROTOTYPE_DIR = $(DESTDIR)@prefix@ SOLARIS_CHECK_TARGETS = PYTHON = @PYTHON@ @@ -29,8 +31,12 @@ sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl SOLARIS_CHECK_TARGETS += check-local-solaris-smf endif +if WITH_SOLARIS_INIT solarisinitscriptdir = @datadir@/solaris-init -solarisinitscript_SCRIPTS = nut +solarisinitscript_SCRIPTS = nut reset-ups-usb-solaris.sh.sample +endif + +EXTRA_DIST += reset-ups-usb-solaris.sh.sample SOLARIS_PACKAGE_TARGETS = @@ -54,7 +60,7 @@ SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS = preinstall postinstall preremove postremov SOLARIS_PACKAGE_SVR4_INSTALLDATA = pkginfo package-solaris-svr4: $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) if test -n "@auglensdir@" && test -d "$(DESTDIR)@auglensdir@" ; then \ - mkdir -p "$(DESTDIR)@datadir@/augeas-lenses" && \ + $(MKDIR_P) "$(DESTDIR)@datadir@/augeas-lenses" && \ cd "$(DESTDIR)@auglensdir@" && \ ( cp -prf ./ "$(DESTDIR)@datadir@/augeas-lenses/" || cp -rf ./ "$(DESTDIR)@datadir@/augeas-lenses/" ) ; fi cd $(PROTOTYPE_DIR) && find . -print | pkgproto > prototype1 @@ -85,4 +91,28 @@ check-local-solaris-smf: $(SOLARIS_SMF_MANIFESTS) /usr/sbin/svccfg validate "$$F" || RES=$$? ; \ done; exit $$RES +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked + MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/Solaris/README b/scripts/Solaris/README deleted file mode 100644 index adb305efdc..0000000000 --- a/scripts/Solaris/README +++ /dev/null @@ -1,38 +0,0 @@ -This directory contains init-scripts and SMF manifests and methods -for integration of NUT services with Solaris and descendant OSes. - -This also includes the nut-driver-enumerator.sh (service and implementation -method) and upsdrvsvcctl (tool) to manage NUT drivers as service instances, -which are stored in ../upsdrvsvcctl/ subdirectory (portable codebase shared -with Linux systemd). - -The default implementation (runs once) can be enabled with: - - svcadm enable nut-driver-enumerator:default - -Note that at the moment there is no out-of-the-box integration for triggering -a restart/refresh of the nut-driver-enumerator SMF service at the very instant -when the `ups.conf` file is modified, like there is with systemd path unit type. -Due to this, the systems administrator is expected to either invoke -`svcadm refresh nut-driver-enumerator` after changing the NUT configuration -or wait until the daemonized mode, if enabled, picks up the change (should do -so within a minute by default). However, a DTrace script or a tool like -https://github.com/emcrisostomo/fswatch wrapped into a service might be used -for equivalent effect. - -Alternatively, but somewhat more expensively, the same `nut-driver-enumerator.sh` -script can be executed in a loop as the payload of the SMF service to keep -inspecting the configuration and apply changes to the running system. It is -not a common use-case to keep changing device setups, so this solution is not -enforced by default ;) although a service variant is provided... - -Note that only one of these can be enabled at the same time: - - svcadm disable nut-driver-enumerator:default - svcadm enable nut-driver-enumerator:daemon - -Init-script solution contributed by numerous authors -SMF solution contributed by Jim Klimov - -For special notes about USB-connected device monitoring with NUT under Solaris -and related operating systems, see docs/solaris-usb.txt diff --git a/scripts/Solaris/README.adoc b/scripts/Solaris/README.adoc new file mode 100644 index 0000000000..ffb84adcd5 --- /dev/null +++ b/scripts/Solaris/README.adoc @@ -0,0 +1,69 @@ +Solaris/illumos helper scripts and resources +============================================ + +Overview +-------- + +This directory contains init-scripts and SMF manifests and methods for better +integration of NUT services with Solaris and descendant operating systems, +covering SVR4 packaging with init scripts and/or SMF services (as called from +`make package` in the source root). + +NOTE: IPS (`pkg(5)`) packaging is not currently provided here, but is a concern +of particular distributions. See for example the OpenIndiana userland recipe at +https://github.com/OpenIndiana/oi-userland/tree/oi/hipster/components/sysutils/nut + +This also includes the `nut-driver-enumerator.sh` (service and implementation +method) and `upsdrvsvcctl` (tool) to manage NUT drivers as service instances, +which are stored in `../upsdrvsvcctl/` subdirectory (portable codebase shared +with Linux systemd). + +SMF integration +--------------- + +The default implementation (which runs once per invocation) can be enabled +with: +---- +:; svcadm enable nut-driver-enumerator:default +---- + +Note that at the moment there is no out-of-the-box integration for triggering +a restart/refresh of the `nut-driver-enumerator` SMF service at the very instant +when the `ups.conf` file is modified, like there is with systemd path unit type. +Due to this, the systems administrator is expected to either invoke +`svcadm refresh nut-driver-enumerator` after changing the NUT configuration, +or wait until the daemonized mode, if enabled, picks up the change (should do +so within a minute by default). However, a DTrace script or a tool like +https://github.com/emcrisostomo/fswatch wrapped into a service might be used +for equivalent effect. + +Alternatively, but in a potentially more computationally expensive fashion, the +same `nut-driver-enumerator.sh` script can be executed in a loop as the payload +of the SMF service to keep inspecting the configuration regularly and so apply +changes to the running system. It is not a common use-case to keep changing +device setups, so this solution is not enforced by default ;) although an SMF +service variant for this is provided... + +Note that only one of these implementations can be enabled at the same time: +---- +:; svcadm disable nut-driver-enumerator:default +:; svcadm enable nut-driver-enumerator:daemon +---- + +Other +----- + +For special notes about USB-connected device monitoring with NUT under Solaris +and related operating systems, see also `docs/solaris-usb.txt`. The example +`reset-ups-usb-solaris.sh.sample` script can be used to reset USB connections +if a driver (or UPS controller) gets stuck -- but it needs to be adapted to +*your* system first. + +Credits +------- + +* Init-script solution and SVR4 packaging contributed by numerous authors + over the years + +* SMF solution and OpenIndiana packaging contributed by Jim Klimov + diff --git a/scripts/Solaris/nut-driver-enumerator.xml.in b/scripts/Solaris/nut-driver-enumerator.xml.in index 7359049f88..f935b8c066 100644 --- a/scripts/Solaris/nut-driver-enumerator.xml.in +++ b/scripts/Solaris/nut-driver-enumerator.xml.in @@ -9,7 +9,7 @@ - + @@ -128,7 +128,7 @@ - + + + - + + + + + + + + diff --git a/scripts/Solaris/nut.in b/scripts/Solaris/nut.in index 1c39603c37..12e82a48dc 100755 --- a/scripts/Solaris/nut.in +++ b/scripts/Solaris/nut.in @@ -7,6 +7,10 @@ NUT_SBIN_DIR="${NUT_DIR}/sbin" NUT_LIB_DIR="${NUT_DIR}/lib" CONFIG="@CONFPATH@/nut.conf" +# We anticipate some tighter integration with SMF later: +#NUT_QUIET_INIT_UPSNOTIFY=true +#export NUT_QUIET_INIT_UPSNOTIFY + if [ -f "$CONFIG" ] ; then . "$CONFIG" fi diff --git a/scripts/Solaris/nut.xml.in b/scripts/Solaris/nut.xml.in index b06b2b151c..79b6f96e66 100644 --- a/scripts/Solaris/nut.xml.in +++ b/scripts/Solaris/nut.xml.in @@ -53,12 +53,14 @@ exec='/usr/sbin/svcadm disable -ts svc:/system/power/nut-monitor:default || /bin/true ; /usr/sbin/svcadm disable -ts svc:/system/power/nut-server:default || /bin/true ; @SBINDIR@/upsdrvsvcctl stop || /bin/true ; /bin/true' timeout_seconds='120' /> - + diff --git a/scripts/Solaris/pkginfo.in b/scripts/Solaris/pkginfo.in index 39667f0a79..2cbbb3fb42 100644 --- a/scripts/Solaris/pkginfo.in +++ b/scripts/Solaris/pkginfo.in @@ -3,7 +3,7 @@ NAME="Network UPS Tools" ARCH="@target_cpu@" VERSION="@PACKAGE_VERSION@" CATEGORY="application" -VENDOR="http://www.networkupstools.org" +VENDOR="https://www.networkupstools.org" EMAIL=" " PSTAMP=" " DESCRIPTION="Network UPS Tools (NUT) is a client/server monitoring system that allows computers to share uninterruptible power supply (UPS) and power distribution unit (PDU) hardware. Clients access the hardware through the server, and are notified whenever the power status changes." diff --git a/scripts/Solaris/postinstall.in b/scripts/Solaris/postinstall.in index a6068b490d..96dc63951c 100755 --- a/scripts/Solaris/postinstall.in +++ b/scripts/Solaris/postinstall.in @@ -35,13 +35,18 @@ for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf nut fi done +# make sure that /var/run exists (for privileged processes) +if [ ! -d "@PIDPATH@" ] ; then + mkdir -p "@PIDPATH@" +fi + # make sure that /var/run/nut exists and has the correct ownerships -if [ ! -d "@PIDPATH@/nut" ] ; then - mkdir -p "@PIDPATH@/nut" +if [ ! -d "@ALTPIDPATH@" ] ; then + mkdir -p "@ALTPIDPATH@" fi -if [ -d "@PIDPATH@/nut" ] ; then - chown "root:@RUN_AS_GROUP@" "@PIDPATH@/nut" - chmod 770 "@PIDPATH@/nut" +if [ -d "@ALTPIDPATH@" ] ; then + chown "root:@RUN_AS_GROUP@" "@ALTPIDPATH@" + chmod 770 "@ALTPIDPATH@" fi # make sure that /var/state/ups exists and has the correct ownerships diff --git a/scripts/Solaris/postremove.in b/scripts/Solaris/postremove.in index dce3f44e84..e1d1babeeb 100755 --- a/scripts/Solaris/postremove.in +++ b/scripts/Solaris/postremove.in @@ -16,4 +16,8 @@ rm -f /etc/rc3.d/K10nut # Remove /var/run/nut -rm -rf "@PIDPATH@/nut" +rm -rf "@ALTPIDPATH@" + +# Remove /var/state/ups + +rm -rf "@STATEPATH@" diff --git a/scripts/Solaris/reset-ups-usb-solaris.sh.sample b/scripts/Solaris/reset-ups-usb-solaris.sh.sample new file mode 100755 index 0000000000..f776023f30 --- /dev/null +++ b/scripts/Solaris/reset-ups-usb-solaris.sh.sample @@ -0,0 +1,71 @@ +#!/bin/sh + +# Copyright (C) 2020,2022 by Jim Klimov +# Licensed according to GPLv2+ for the NUT project + +# If your USB connection on Solaris/illumos platform gets lost regularly +# and might benefit from a harsh reconnection, consider customizing this +# script (or its envvar args) to your deployment and adding to crontab: +# 0,5,10,15,20,25,30,35,40,45,50,55 * * * * MODE=optional /etc/nut/reset-ups-usb-solaris.sh + +# Comment this away in your deployment after customizing defaults; +# see NUT source docs/solaris-usb.txt for details: +echo "WARNING: Script $0 was not yet tailored to this deployment!" >&2 ; exit + +# TODO: Parse CLI args? +[ -n "$MODE" ] || MODE='always' +# Defaults below come from documentation example: +[ -n "$DEVICE" ] || DEVICE='innotech' +[ -n "$CFGADM_APID" ] || CFGADM_APID="usb10/1" +# Can specify '-' to not reload OS driver: +[ -n "$UGEN_DRV_ID" ] || UGEN_DRV_ID='"usb665,5161.2"' + +if [ "$MODE" = optional ]; then + if upsc "$DEVICE" 2>&1 | grep -i 'Data stale' ; then : ; else exit 0 ; fi +fi + +# Sanity-checks +command -v svcs && command -v svcadm || { echo "ERROR: This system does not have SMF tools?" >&2 ; exit 1; } +command -v cfgadm || { echo "ERROR: This system does not have cfgadm?" >&2 ; exit 1; } +# upsc and upsdrvctl below are rather informational +command -v upsc && command -v upsdrvctl || echo "WARNING: This system does not have NUT tools?" >&2 + +date +svcs -p "$DEVICE" ; upsc "$DEVICE" + +DO_SVC=false +if [ "`svcs -Hostate "$DEVICE"`" = "online" ]; then + DO_SVC=true + svcadm disable -ts "$DEVICE" +fi +upsdrvctl stop "$DEVICE" || true + +echo "Soft-resetting connection of '${CFGADM_APID}':" +cfgadm -lv "${CFGADM_APID}" + +cfgadm -c disconnect -y "${CFGADM_APID}" +if [ "$UGEN_DRV_ID" != '-' ] ; then + rem_drv ugen ; sleep 3 +fi +cfgadm -c configure -y "${CFGADM_APID}"; sleep 3 +if [ "$UGEN_DRV_ID" != '-' ] ; then + add_drv -i "$UGEN_DRV_ID" -m '* 0666 root sys' ugen +fi + +sleep 3 +cfgadm -lv "${CFGADM_APID}" + +if $DO_SVC ; then svcadm enable "$DEVICE" ; fi +svcadm clear "$DEVICE" 2>/dev/null + +dmesg | tail -n 20 +date +svcs -p "$DEVICE" ; upsc "$DEVICE" || { \ + COUNT=60 + while [ "$COUNT" -gt 0 ] ; do + COUNT="`expr $COUNT - 1`" + if upsc "$DEVICE" 2>&1 | grep -Ei '^ups\.status:' >/dev/null ; then break ; fi + sleep 1 + done + svcs -p "$DEVICE" ; upsc "$DEVICE" +} diff --git a/scripts/Solaris/svc-nut-monitor.in b/scripts/Solaris/svc-nut-monitor.in index a7dd0dc91d..9e4129df1f 100755 --- a/scripts/Solaris/svc-nut-monitor.in +++ b/scripts/Solaris/svc-nut-monitor.in @@ -16,11 +16,15 @@ prefix="@prefix@" NUT_DIR="@prefix@" NUT_SBIN_DIR="${NUT_DIR}/sbin" NUT_LIB_DIR="${NUT_DIR}/lib" -NUT_RUN_DIR="@PIDPATH@/nut" +NUT_RUN_DIR="@ALTPIDPATH@" CONFIG="@CONFPATH@/nut.conf" NUTUSER="@RUN_AS_USER@" NUTGROUP="@RUN_AS_GROUP@" +# We anticipate some tighter integration with SMF later: +#NUT_QUIET_INIT_UPSNOTIFY=true +#export NUT_QUIET_INIT_UPSNOTIFY + if [ -f "$CONFIG" ] ; then . "$CONFIG" fi @@ -32,6 +36,7 @@ ups_start () { fi # Default rights inspired by NUT scripts/Solaris/postinstall.in + mkdir -p "@PIDPATH@" # (for privileged processes) mkdir -p "$NUT_RUN_DIR" && \ chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ chmod 770 "$NUT_RUN_DIR" \ diff --git a/scripts/Solaris/svc-nut-server.in b/scripts/Solaris/svc-nut-server.in index 5fee9c1802..867c75fd0c 100755 --- a/scripts/Solaris/svc-nut-server.in +++ b/scripts/Solaris/svc-nut-server.in @@ -16,21 +16,40 @@ prefix="@prefix@" NUT_DIR="@prefix@" NUT_SBIN_DIR="$NUT_DIR/sbin" NUT_LIB_DIR="${NUT_DIR}/lib" -NUT_RUN_DIR="@PIDPATH@/nut" + +NUT_RUN_DIR="`svcprop -p nut/NUT_RUN_DIR $SMF_FMRI`" \ +&& [ -n "$NUT_RUN_DIR" ] \ +|| NUT_RUN_DIR="@ALTPIDPATH@" + CONFIG="@CONFPATH@/nut.conf" -NUTUSER="@RUN_AS_USER@" -NUTGROUP="@RUN_AS_GROUP@" + +NUTUSER="`svcprop -p nut/NUTUSER $SMF_FMRI`" \ +&& [ -n "$NUTUSER" ] \ +|| NUTUSER="@RUN_AS_USER@" + +NUTGROUP="`svcprop -p nut/NUTGROUP $SMF_FMRI`" \ +&& [ -n "$NUTGROUP" ] \ +|| NUTGROUP="@RUN_AS_GROUP@" + +# We anticipate some tighter integration with SMF later: +#NUT_QUIET_INIT_UPSNOTIFY=true +#export NUT_QUIET_INIT_UPSNOTIFY if [ -f "$CONFIG" ] ; then . "$CONFIG" fi -ups_start () { +create_run_dir () { # Default rights inspired by NUT scripts/Solaris/postinstall.in + #mkdir -p "@PIDPATH@" # (for privileged processes - not upsd) mkdir -p "$NUT_RUN_DIR" && \ chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ chmod 770 "$NUT_RUN_DIR" \ || exit $SMF_EXIT_ERR_FATAL +} + +ups_start () { + create_run_dir if [ "$MODE" = "none" ];then echo "No NUT mode set, not starting anything" >&2 @@ -53,6 +72,11 @@ case "$1" in LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" -c reload ;; +"create_run_dir") + # Mostly provided for the sake of nut-driver service + create_run_dir + ;; + *) echo "" echo "Usage: '$0' {start}" diff --git a/scripts/Solaris8/S99upsmon b/scripts/Solaris8/S99upsmon index 06ba753f4a..5030c9f798 100755 --- a/scripts/Solaris8/S99upsmon +++ b/scripts/Solaris8/S99upsmon @@ -11,6 +11,9 @@ export PATH UPSDPATH=/usr/local/ups/sbin +NUT_QUIET_INIT_UPSNOTIFY=true +export NUT_QUIET_INIT_UPSNOTIFY + # See how we are called. case "$1" in 'start') @@ -19,7 +22,7 @@ case "$1" in $UPSDPATH/upsmon >/dev/console 2>&1 touch /var/lock/subsys/upsmon fi - ;; + ;; 'stop') echo "NUT Stopping UPS monitor " /usr/bin/pkill -x upsmon diff --git a/scripts/Windows/.gitignore b/scripts/Windows/.gitignore new file mode 100644 index 0000000000..b620eb4b48 --- /dev/null +++ b/scripts/Windows/.gitignore @@ -0,0 +1,7 @@ +.wininit.c.swp +*.exe +winevent.h +winevent.rc +MSG00409.bin +/nut_build/ +/nut_install/ diff --git a/scripts/Windows/DriverInstaller/README.adoc b/scripts/Windows/DriverInstaller/README.adoc new file mode 100644 index 0000000000..4e60a1ff93 --- /dev/null +++ b/scripts/Windows/DriverInstaller/README.adoc @@ -0,0 +1,11 @@ +To easily compile `wdi-simple.exe`, unzip a copy of `libwdi` on your disk. + +Then set it up to be able to build following +http://sourceforge.net/apps/mediawiki/libwdi/index.php?title=Install + +NOTE: Modern development may be at https://github.com/pbatard/libwdi + +Then copy `wdi-simple.c` and `nutscan-usb.h` into the "examples" +subdirectory of libwdi directory. + +Finally run `ddk_build.cmd` from the root of the `libwdi` directory. diff --git a/scripts/Windows/DriverInstaller/wdi-simple.c b/scripts/Windows/DriverInstaller/wdi-simple.c new file mode 100644 index 0000000000..31d50b5e67 --- /dev/null +++ b/scripts/Windows/DriverInstaller/wdi-simple.c @@ -0,0 +1,130 @@ +/* + wdi-simple.c: Console Driver Installer for NUT USB devices + Copyright (c) 2010 Pete Batard + Copyright (c) 2011 Frederic Bohe + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#ifdef _MSC_VER +#include "getopt/getopt.h" +#else +#include +#endif +#include "libwdi.h" +#include "nutscan-usb.h" + +#define oprintf(...) do {if (!opt_silent) printf(__VA_ARGS__);} while(0) + +#define DESC "NUT USB driver" +#define INF_NAME "usb_device.inf" +#define DEFAULT_DIR "usb_driver" + + +int __cdecl main(int argc, char** argv) +{ + char desc[128] = DESC; + struct wdi_device_info *ldev, *ldev_start, dev = {NULL, 0, 0, false, 0, DESC, NULL, NULL, NULL}; + struct wdi_options_create_list ocl = { 0 }; + struct wdi_options_prepare_driver opd = { 0 }; + struct wdi_options_install_driver oid = { 0 }; + int c, r; + int opt_silent = 0, opt_extract = 0, log_level = WDI_LOG_LEVEL_WARNING; +/* + int opt_silent = 0, opt_extract = 0, log_level = WDI_LOG_LEVEL_DEBUG; +*/ + char *inf_name = INF_NAME; + char *ext_dir = DEFAULT_DIR; + bool matching_device_found; + int index = 0; + + ocl.list_all = true; + ocl.list_hubs = true; + ocl.trim_whitespaces = true; + opd.driver_type = WDI_LIBUSB0; +/* + opd.driver_type = WDI_WINUSB; +*/ + + wdi_set_log_level(log_level); + + oprintf("NUT UPS driver installer.\n"); + oprintf("-------------------------\n\n"); + oprintf("Searching for known UPS...\n"); + + /* Try to match against a plugged device */ + matching_device_found = false; + if (wdi_create_list(&ldev, &ocl) == WDI_SUCCESS) { + r = WDI_SUCCESS; + ldev_start = ldev; + while( usb_device_table[index].vendorID != 0xFFFF || usb_device_table[index].productID != 0xFFFF) { + dev.next = NULL; + dev.vid = usb_device_table[index].vendorID; + dev.pid = usb_device_table[index].productID; + dev.is_composite = false; + dev.mi = 0; + dev.desc = desc; + dev.driver = NULL; + dev.device_id = NULL; + dev.hardware_id = NULL; +/* + oprintf("NUT device : vid : %0X - pid : %0X\n",dev.vid, dev.pid); +*/ + + for (ldev = ldev_start; (ldev != NULL) && (r == WDI_SUCCESS); ldev = ldev->next) { +/* + oprintf("trying vid : %0X - pid : %0X\n",ldev->vid, ldev->pid); +*/ + if ( (ldev->vid == dev.vid) && (ldev->pid == dev.pid) && (ldev->mi == dev.mi) ) { + oprintf("Found UPS : vendor ID = %0X - Product ID = %0X\n",ldev->vid, ldev->pid, ldev->mi); + dev.hardware_id = ldev->hardware_id; + dev.device_id = ldev->device_id; + matching_device_found = true; + + oprintf("Extracting driver files...\n"); + r = wdi_prepare_driver(&dev, ext_dir, inf_name, &opd); + oprintf(" %s\n", wdi_strerror(r)); + if ((r != WDI_SUCCESS) || (opt_extract)) + return r; + + oprintf(" %s: ", dev.hardware_id); + fflush(stdout); + oprintf("Installing driver\n"); + r = wdi_install_driver(&dev, ext_dir, inf_name, &oid); + oprintf("%s\n", wdi_strerror(r)); + if( r == WDI_SUCCESS ) { + oprintf("You should now unplug and re-plug your device to finish driver's installation.\nHit enter when it's done.\n"); + } + else { + oprintf("An error occured while installing driver.\nTry installing libUSB manually.\nHit enter to continue\n"); + } + getc(stdin); + } + } + index++; + } + } + + /* No plugged USB device matches */ + if (!matching_device_found) { + oprintf("No known UPS device found.\nTry installing libUSB manually.\nHit enter to continue\n"); + getc(stdin); + } + + return r; +} diff --git a/scripts/Windows/Installer/.gitignore b/scripts/Windows/Installer/.gitignore new file mode 100644 index 0000000000..c3ce80fccc --- /dev/null +++ b/scripts/Windows/Installer/.gitignore @@ -0,0 +1,5 @@ +NUT-Installer.msi +NUT-Installer.wixobj +NUT-Installer.wixpdb +NUT-Installer.xml +log.txt diff --git a/scripts/Windows/Installer/BuildInstaller.bat b/scripts/Windows/Installer/BuildInstaller.bat new file mode 100644 index 0000000000..d05fdcedf4 --- /dev/null +++ b/scripts/Windows/Installer/BuildInstaller.bat @@ -0,0 +1,35 @@ +::This script can be used to Create and Build NUT installer using WiX. + +@echo off + +SET BATDIR=%~dp0 +cd /d %BATDIR% + +SET MSYS_BIN_DIR=c:\mingw\msys\1.0\bin\ +SET MINGW_BIN_DIR=c:\mingw\bin\ +SET NUT-XML-FILE=NUT-Installer.xml +SET wixobjName=NUT-Installer.wixobj +SET msiPackageName=NUT-Installer.msi + +%MSYS_BIN_DIR%unix2dos.exe ../../../conf/upssched.conf.sample + +echo copy DLL files from MSYS +copy /Y %MSYS_BIN_DIR%msys-1.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-crypto-1.0.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-ssl-1.0.0.dll .\ImageFiles\Others +copy /Y %MSYS_BIN_DIR%msys-regex-1.dll .\ImageFiles\Others + +REM use "candle.exe" to create the "object" file +candle.exe "%NUT-XML-FILE%" -out "%wixobjName%" >"log.txt" +@echo ========================================================= +@echo Please wait as MSI package creation in progress... + +@echo off +REM use "light.exe" to create the "MSi" package +light.exe "%wixobjName%" -out "%msiPackageName%" >>"log.txt" + +@echo ========================================================= +@echo MSI package "%msiPackageName%" complete +@echo ========================================================= +@echo Check output file "log.txt" for status of completion... +@echo ========================================================= diff --git a/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp new file mode 100644 index 0000000000..b0b066336d Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_horizontal.bmp differ diff --git a/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp new file mode 100644 index 0000000000..c930c05409 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/Images/NUT_wix_vertical.bmp differ diff --git a/scripts/Windows/Installer/ImageFiles/Others/.gitignore b/scripts/Windows/Installer/ImageFiles/Others/.gitignore new file mode 100644 index 0000000000..7771adbbb2 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/.gitignore @@ -0,0 +1,14 @@ +libexpat-1.dll +libgcc_s_dw2-1.dll +libiconv-2.dll +libintl-8.dll +libltdl-7.dll +libneon-27.dll +libnetsnmp-30.dll +libregex-1.dll +libz-1.dll +msys-1.0.dll +msys-crypto-1.0.0.dll +msys-regex-1.dll +msys-ssl-1.0.0.dll +pthreadgc2.dll diff --git a/scripts/Windows/Installer/ImageFiles/Others/StartService.bat b/scripts/Windows/Installer/ImageFiles/Others/StartService.bat new file mode 100644 index 0000000000..5b8333c6f9 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/StartService.bat @@ -0,0 +1 @@ +net start "Network UPS Tools" \ No newline at end of file diff --git a/scripts/Windows/Installer/ImageFiles/Others/StopService.bat b/scripts/Windows/Installer/ImageFiles/Others/StopService.bat new file mode 100644 index 0000000000..718da56a85 --- /dev/null +++ b/scripts/Windows/Installer/ImageFiles/Others/StopService.bat @@ -0,0 +1 @@ +net stop "Network UPS Tools" \ No newline at end of file diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/cgi-bin/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/cgi-bin/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/html/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/html/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/include/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/include/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/man1/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/man1/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/man3/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/man3/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/pkgconfig/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/pkgconfig/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/emptyDir/run/temp.txt b/scripts/Windows/Installer/ImageFiles/emptyDir/run/temp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/Windows/Installer/ImageFiles/icons/New.ico b/scripts/Windows/Installer/ImageFiles/icons/New.ico new file mode 100644 index 0000000000..27881dfe97 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/New.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/Up.ico b/scripts/Windows/Installer/ImageFiles/icons/Up.ico new file mode 100644 index 0000000000..86f6b5a847 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/Up.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/completi.ico b/scripts/Windows/Installer/ImageFiles/icons/completi.ico new file mode 100644 index 0000000000..93a95a1b19 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/completi.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/custicon.ico b/scripts/Windows/Installer/ImageFiles/icons/custicon.ico new file mode 100644 index 0000000000..878d3ba54b Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/custicon.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico b/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico new file mode 100644 index 0000000000..906ce3246d Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/exclamic.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/info.ico b/scripts/Windows/Installer/ImageFiles/icons/info.ico new file mode 100644 index 0000000000..7e0ff7f14c Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/info.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/insticon.ico b/scripts/Windows/Installer/ImageFiles/icons/insticon.ico new file mode 100644 index 0000000000..94753ac296 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/insticon.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico b/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico new file mode 100644 index 0000000000..afe5ab659f Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/powernut_Stop.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico b/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico new file mode 100644 index 0000000000..05d3b312f0 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/powernut_logo.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/removico.ico b/scripts/Windows/Installer/ImageFiles/icons/removico.ico new file mode 100644 index 0000000000..097cafe274 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/removico.ico differ diff --git a/scripts/Windows/Installer/ImageFiles/icons/repairic.ico b/scripts/Windows/Installer/ImageFiles/icons/repairic.ico new file mode 100644 index 0000000000..6fb68610c9 Binary files /dev/null and b/scripts/Windows/Installer/ImageFiles/icons/repairic.ico differ diff --git a/scripts/Windows/Installer/NUT-Installer.xml.in b/scripts/Windows/Installer/NUT-Installer.xml.in new file mode 100644 index 0000000000..7348d6ac31 --- /dev/null +++ b/scripts/Windows/Installer/NUT-Installer.xml.in @@ -0,0 +1,2078 @@ + + + + + + + + + + + + + + + + + + + + + + + + If the automatic USB driver installation fails, you can try to install a driver manually. For this go to : + +https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/ + +After installling libusb-win32, run libUSB's Inf Wizard and choose your device. + + + Click Next to continue [Wizard]. + + + + + + If you use a USB UPS, please plug it in now, so that we can try to install the relevant driver. + + + + + + + + + + + + + + + + + + + [DlgTitleFont][ProductName] installs the following libraries, location of licenses are - + + + Click Next to continue [Wizard]. + + + + 1. msys-1.0.dll - http://sourceforge.net/projects/mingw/files/MSYS/BaseSystem/msys-core/msys-1.0.16-1/ + + + 2. msys-ssl-1.0.0.dll and msys-crypto-1.0.0.dll - http://sourceforge.net/projects/mingw/files/MSYS/openssl/openssl-1.0.0-1/ + + + 3. libregex-1.dll - http://sourceforge.net/projects/mingw/files/MSYS/BaseSystem/regex/regex-1.20090805-2/ + + + + + + + + + + + + + Click the Finish button to exit the [Wizard]. + + + {\VerdanaBold13}Completing the [ProductName] [Wizard] + + + + + + + + + + + + {\VerdanaBold13}[ProductName] [Wizard] ended prematurely + + + [ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again. + + + Click the Finish button to exit the [Wizard]. + + + + + + + + + + Please wait while the [Wizard] prepares to guide you through the installation. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + + + + + + + + + + + + + + Please wait while the [Wizard] [Progress2] [ProductName]. This may take several minutes. + + + + [DlgTitleFont][Progress1] [ProductName] + + + + + + + + + + + + + + + + + + + + {\VerdanaBold13}[ProductName] [Wizard] was interrupted + + + [ProductName] setup was interrupted. Your system has not been modified. To install this program at a later time, please run the installation again. + + + Click the Finish button to exit the [Wizard]. + + + + + + + + + + + + + + + + + + + + + + + + + + + Browse to the destination folder + + + [DlgTitleFont]Change current destination folder + + + + + + + + + + + + + + + + Are you sure you want to cancel [ProductName] installation? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it. + + + + Some files that need to be updated are currently in use. + + + [DlgTitleFont]Files in Use + + + + + + + + + + + "Yes"]]> + + + + + + + + + {\rtf1\ansi\ansicpg1252\deff0\deftab720 + {\fonttbl{\f0\froman\fprq2 Times New Roman;}} + {\colortbl\red0\green0\blue0;} + \deflang1033\horzdoc{\*\fchars }{\*\lchars } + \pard\plain\f0\fs18 \par + GNU GENERAL PUBLIC LICENSE \par + Version 2, June 1991 + \par \par +Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. \par + + Preamble \par +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. \par + +\par +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + \par +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + \par + \par +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their rights. + \par + \par +We protect your rights with two steps: +\par (1) copyright the software, and +\par (2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + \par + \par +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + \par + \par +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + \par + \par +The precise terms and conditions for copying, distribution and +modification follow. + \par + \par + GNU GENERAL PUBLIC LICENSE + \par \par + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + \par \par + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + \par \par + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + \par \par + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + \par \par +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + \par \par + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + \par \par + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + \par \par + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + \par \par + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + \par \par +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + \par \par +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + \par \par +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + \par \par + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + \par \par + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + \par \par + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + \par \par + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + \par \par +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + \par \par +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + \par \par + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + \par \par + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + \par \par + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + \par \par + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + \par \par +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + \par \par +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + \par \par +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + \par \par + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + \par \par + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + \par \par +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + \par \par + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + \par \par + NO WARRANTY + \par \par + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + \par \par + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + \par \par + END OF TERMS AND CONDITIONS + \par \par + How to Apply These Terms to Your New Programs + \par \par + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + \par \par + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + \par \par + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + \par \par + 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. + \par \par + 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. + \par \par + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + \par \par +Also add information on how to contact you by electronic and paper mail. + \par \par +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + \par \par + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + \par +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + \par \par +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + \par \par + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + \par \par + <signature of Ty Coon>, 1 April 1989 + \par + Ty Coon, President of Vice + \par \par +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + \par \par + + } + + + + + + + + [DlgTitleFont]Re&pair + + + + + + + + + [DlgTitleFont]&Remove + + + + + + + + + + + + + + + + + + + Select the operation you wish to perform. + + + [DlgTitleFont]Repair or Remove installation + + + Removes [ProductName] from your computer. + + + Repairs errors in the most recent installation state - fixes missing or corrupt files, shortcuts and registry entries. + + + + + + + + + + + + + + + The [Wizard] will allow you to change the way [ProductName] features are installed on your computer or even to remove [ProductName] from your computer. Click Next to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + 1]]> + + + + + + + + + + + The [Wizard] will complete the installation of [ProductName] on your computer. Click Install to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Resuming the [ProductName] [Wizard] + + + + + 1]]> + + + + + + + + + + + + + Click Install to begin the installation. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + The [Wizard] is ready to begin the [InstallMode] installation + + + [DlgTitleFont]Ready to Install + + + + + + + + + 1]]> + 1]]> + + + + + + + + + + Click Remove to remove [ProductName] from your computer. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + You have chosen to remove the program from your computer. + + + [DlgTitleFont]Remove [ProductName] + + + + + + 1]]> + 1]]> + 1]]> + + + + + + + + + + + + + Click Repair to repair the installation of [ProductName]. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard. + + + + The [Wizard] is ready to begin the repair of [ProductName]. + + + [DlgTitleFont]Repair [ProductName] + + + + + + + + + + + + + + + + The [Wizard] will install [ProductName] on your computer. Click Next to continue or Cancel to exit the [Wizard]. + + + {\VerdanaBold13}Welcome to the [ProductName] [Wizard] + + + + + + + + + + + bytes + GB + KB + MB + Entire feature will be unavailable + Feature will be installed when required + Entire feature will be installed to run from CD + Entire feature will be installed on local hard drive + Entire feature will be installed to run from network + Will be installed to run from CD + Will be installed on local hard drive + Will be installed to run from network + Gathering required information... + This feature will remain uninstalled + This feature will be set to be installed when required + This feature will be installed to run from CD + This feature will be installed on the local hard drive + This feature will be installed to run from the network + This feature will become unavailable + Will be installed when required + This feature will be available to run from CD + This feature will be installed on your local hard drive + This feature will be available to run from the network + This feature will be uninstalled completely, you won't be able to run it from CD + This feature will change from run from CD state to set to be installed when required + This feature will remain to be run from CD + This feature will change from run from CD state to be installed on the local hard drive + This feature frees up [1] on your hard drive. + This feature requires [1] on your hard drive. + Compiling cost for this feature... + This feature will be completely removed + This feature will be removed from your local hard drive, but will be set to be installed when required + This feature will be removed from your local hard drive, but will be still available to run from CD + This feature will remain on you local hard drive + This feature will be removed from your local hard drive, but will be still available to run from the network + This feature will be uninstalled completely, you won't be able to run it from the network + This feature will change from run from network state to set to be installed when required + This feature will change from run from network state to be installed on the local hard drive + This feature will remain to be run from the network + This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. + This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. + This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive. + This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive. + Time remaining: {[1] minutes }{[2] seconds} + Available + Difference + Required + Disk Size + Volume + Validating install + Copying new files + Copying network install files + Computing space requirements + Computing space requirements + Computing space requirements + Creating shortcuts + Publishing Qualified Components + Publishing Product Features + Publishing product information + Registering Class servers + Registering extension servers + Registering MIME info + Registering program identifiers + Allocating registry space + Searching for installed applications + Binding executables + Searching for qualifying products + Creating folders + Deleting services + Creating duplicate files + Searching for related applications + Installing ODBC components + Installing new services + Evaluating launch conditions + Migrating feature states from related applications + Moving files + Patching files + Updating component registration + Registering COM+ Applications and Components + Registering fonts + Registering product + Registering type libraries + Registering user + Removing duplicated files + Updating environment strings + Removing applications + Removing files + Removing folders + Removing INI files entries + Removing ODBC components + Removing system registry values + Removing shortcuts + Searching for qualifying products + Registering modules + Unregistering modules + Initializing ODBC directories + Starting services + Stopping services + Unpublishing Qualified Components + Unpublishing Product Features + Unregister Class servers + Unregistering COM+ Applications and Components + Unregistering extension servers + Unregistering fonts + Unregistering MIME info + Unregistering program identifiers + Unregistering type libraries + Updating environment strings + Writing INI files values + Writing system registry values + Advertising application + Generating script operations for action: + Installing system catalog + Publishing assembly information + Unpublishing assembly information + Rolling back action: + Removing backup files + Removing moved files + Unpublishing product information + {{Fatal error: }} + {{Error [1]. }} + Warning [1]. + + Info [1]. + The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is [1]. {{The arguments are: [2], [3], [4]}} + + {{Disk full: }} + Action [Time]: [1]. [2] + [ProductName] + {[2]}{, [3]}{, [4]} + Message type: [1], Argument: [2] + === Logging started: [Date] [Time] === + === Logging stopped: [Date] [Time] === + Action start [Time]: [1]. + Action ended [Time]: [1]. Return value [2]. + Time remaining: {[1] minutes }{[2] seconds} + Out of memory. Shut down other applications before retrying. + Installer is no longer responding. + Installer stopped prematurely. + Please wait while Windows configures [ProductName] + Gathering required information... + Removing older versions of this application... + Preparing to remove older versions of this application... + {[ProductName] }Setup completed successfully. + {[ProductName] }Setup failed. + Error reading from file: [2]. {{ System error [3].}} Verify that the file exists and that you can access it. + Cannot create the file '[2]'. A directory with this name already exists. Cancel the install and try installing to a different location. + Please insert the disk: [2] + The installer has insufficient privileges to access this directory: [2]. The installation cannot continue. Log on as administrator or contact your system administrator. + Error writing to file: [2]. Verify that you have access to that directory. + Error reading from file [2]. {{ System error [3].}} Verify that the file exists and that you can access it. + Another application has exclusive access to the file '[2]'. Please shut down all other applications, then click Retry. + There is not enough disk space to install this file: [2]. Free some disk space and click Retry, or click Cancel to exit. + Source file not found: [2]. Verify that the file exists and that you can access it. + Error reading from file: [3]. {{ System error [2].}} Verify that the file exists and that you can access it. + Error writing to file: [3]. {{ System error [2].}} Verify that you have access to that directory. + Source file not found{{(cabinet)}}: [2]. Verify that the file exists and that you can access it. + Cannot create the directory '[2]'. A file with this name already exists. Please rename or remove the file and click retry, or click Cancel to exit. + The volume [2] is currently unavailable. Please select another. + The specified path '[2]' is unavailable. + Unable to write to the specified folder: [2]. + A network error occurred while attempting to read from the file: [2] + An error occurred while attempting to create the directory: [2] + A network error occurred while attempting to create the directory: [2] + A network error occurred while attempting to open the source file cabinet: [2] + The specified path is too long: [2] + The Installer has insufficient privileges to modify this file: [2]. + A portion of the folder path '[2]' is invalid. It is either empty or exceeds the length allowed by the system. + The folder path '[2]' contains words that are not valid in folder paths. + The folder path '[2]' contains an invalid character. + '[2]' is not a valid short file name. + Error getting file security: [3] GetLastError: [2] + Invalid Drive: [2] + Error applying patch to file [2]. It has probably been updated by other means, and can no longer be modified by this patch. For more information contact your patch vendor. {{System Error: [3]}} + A file that is required cannot be installed because the cabinet file [2] is not digitally signed. This may indicate that the cabinet file is corrupt. + A file that is required cannot be installed because the cabinet file [2] has an invalid digital signature. This may indicate that the cabinet file is corrupt.{{ Error [3] was returned by WinVerifyTrust.}} + Failed to correctly copy [2] file: CRC error. + Failed to correctly move [2] file: CRC error. + Failed to correctly patch [2] file: CRC error. + The file '[2]' cannot be installed because the file cannot be found in cabinet file '[3]'. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package. + The cabinet file '[2]' required for this installation is corrupt and cannot be used. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package. + There was an error creating a temporary file that is needed to complete this installation.{{ Folder: [3]. System error code: [2]}} + Could not create key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not open key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not delete value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not delete key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not read value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not write value [2] to key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not get value names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not get sub key names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not read security information for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel. + Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application. + Another installation is in progress. You must complete that installation before continuing this one. + Error accessing secured data. Please make sure the Windows Installer is configured properly and try the install again. + User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product. Your current install will now continue. + User '[2]' has previously initiated an install for product '[3]'. That user will need to run that install again before they can use that product. + Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB. Free some disk space and retry. + Are you sure you want to cancel? + The file [2][3] is being held in use{ by the following process: Name: [4], Id: [5], Window Title: '[6]'}. Close that application and retry. + The product '[2]' is already installed, preventing the installation of this product. The two products are incompatible. + There is not enough disk space on the volume '[2]' to continue the install with recovery enabled. [3] KB are required, but only [4] KB are available. Click Ignore to continue the install without saving recovery information, click Retry to check for available space again, or click Cancel to quit the installation. + Could not access network location [2]. + The following applications should be closed before continuing the install: + Could not find any previously installed compliant products on the machine for installing this product. + An error occurred while applying security settings. [2] is not a valid user or group. This could be a problem with the package, or a problem connecting to a domain controller on the network. Check your network connection and click Retry, or Cancel to end the install. {{Unable to locate the user's SID, system error [3]}} + The key [2] is not valid. Verify that you entered the correct key. + The installer must restart your system before configuration of [2] can continue. Click Yes to restart now or No if you plan to manually restart later. + You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to manually restart later. + An installation for [2] is currently suspended. You must undo the changes made by that installation to continue. Do you want to undo those changes? + A previous installation for this product is in progress. You must undo the changes made by that installation to continue. Do you want to undo those changes? + An installation package for the product [2] cannot be found. Try the installation again using a valid copy of the installation package '[3]'. + Installation completed successfully. + Installation failed. + Product: [2] -- [3] + You may either restore your computer to its previous state or continue the install later. Would you like to restore? + An error occurred while writing installation information to disk. Check to make sure enough disk space is available, and click Retry, or Cancel to end the install. + One or more of the files required to restore your computer to its previous state could not be found. Restoration will not be possible. + [2] cannot install one of its required products. Contact your technical support group. {{System Error: [3].}} + The older version of [2] cannot be removed. Contact your technical support group. {{System Error [3].}} + Installed [2] + Configured [2] + Removed [2] + File [2] was rejected by digital signature policy. + The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance. + There is a problem with this Windows Installer package. A script required for this install to complete could not be run. Contact your support personnel or package vendor. {{Custom action [2] script error [3], [4]: [5] Line [6], Column [7], [8] }} + There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action: [2], location: [3], command: [4] }} + There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. {{Action [2], location: [3], command: [4] }} + There is a problem with this Windows Installer package. A DLL required for this install to complete could not be run. Contact your support personnel or package vendor. {{Action [2], entry: [3], library: [4] }} + Removal completed successfully. + Removal failed. + Advertisement completed successfully. + Advertisement failed. + Configuration completed successfully. + Configuration failed. + You must be an Administrator to remove this application. To remove this application, you can log on as an Administrator, or contact your technical support group for assistance. + The path [2] is not valid. Please specify a valid path. + Out of memory. Shut down other applications before retrying. + There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume. + There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume. + The folder [2] does not exist. Please enter a path to an existing folder. + You have insufficient privileges to read this folder. + A valid destination folder for the install could not be determined. + Error attempting to read from the source install database: [2]. + Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation. + Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation. + Module [2] failed to register. HRESULT [3]. Contact your support personnel. + Module [2] failed to unregister. HRESULT [3]. Contact your support personnel. + Failed to cache package [2]. Error: [3]. Contact your support personnel. + Could not register font [2]. Verify that you have sufficient permissions to install fonts, and that the system supports this font. + Could not unregister font [2]. Verify that you that you have sufficient permissions to remove fonts. + Could not create Shortcut [2]. Verify that the destination folder exists and that you can access it. + Could not remove Shortcut [2]. Verify that the shortcut file exists and that you can access it. + Could not register type library for file [2]. Contact your support personnel. + Could not unregister type library for file [2]. Contact your support personnel. + Could not update the ini file [2][3]. Verify that the file exists and that you can access it. + Could not schedule file [2] to replace file [3] on reboot. Verify that you have write permissions to file [3]. + Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. + Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel. + Error removing ODBC driver: [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers. + Error installing ODBC driver: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. + Error configuring ODBC data source: [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it. + Service '[2]' ([3]) failed to start. Verify that you have sufficient privileges to start system services. + Service '[2]' ([3]) could not be stopped. Verify that you have sufficient privileges to stop system services. + Service '[2]' ([3]) could not be deleted. Verify that you have sufficient privileges to remove system services. + Service '[2]' ([3]) could not be installed. Verify that you have sufficient privileges to install system services. + Could not update environment variable '[2]'. Verify that you have sufficient privileges to modify environment variables. + You do not have sufficient privileges to complete this installation for all users of the machine. Log on as administrator and then retry this installation. + Could not set file security for file '[3]'. Error: [2]. Verify that you have sufficient privileges to modify the security permissions for this file. + Component Services (COM+ 1.0) are not installed on this computer. This installation requires Component Services in order to complete successfully. Component Services are available on Windows 2000. + Error registering COM+ Application. Contact your support personnel for more information. + Error unregistering COM+ Application. Contact your support personnel for more information. + The description for service '[2]' ([3]) could not be changed. + The Windows Installer service cannot update the system file [2] because the file is protected by Windows. You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}} + The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}} + The Windows Installer service cannot update one or more protected Windows files. {{SFP Error: [2]. List of protected files:\r\n[3]}} + User installations are disabled via policy on the machine. + An error occured during the installation of assembly component [2]. HRESULT: [3]. {{assembly interface: [4], function: [5], assembly name: [6]}} + + TARGETDIR="" + + + + + + + + + + + + + + + TARGETDIR="" + + + + + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + @@@@@]]> + + + + + + + + + + + TARGETDIR="" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TARGETDIR="" + + + + + + + LIBUSBINSTALL=1 + + + \ No newline at end of file diff --git a/scripts/Windows/Installer/README.adoc b/scripts/Windows/Installer/README.adoc new file mode 100644 index 0000000000..fdf377fedd --- /dev/null +++ b/scripts/Windows/Installer/README.adoc @@ -0,0 +1,130 @@ +NUT for Windows Installer +========================= + +This component aims to provide an integrated installer for NUT on Windows, +including registration as the preferred handler of certain USB vendor and +product identifiers. + +Various waves of the NUT for Windows effort happened over the decades, with +the latest one being integration of the platform-specific source code which +was previously available in side projects, into the main Network UPS Tools +project and its continuous-integration builds, between NUT v2.8.0 and v2.8.1 +releases (over 2022-2023 time frame). + +NOTE: This document aims to revive institutional knowledge about producing +a NUT for Windows installer file. Currently builds of NUT for Windows are +quite possible, they can result in preparation of a tarball of an installed +directory tree which is immediately usable on Windows. However, separate +steps may be needed for USB specifically (so that NUT drivers and not the +default "HID Battery" handler can attach to the device) and for OS Service +integration. It is assumed that the installer would handle these rituals. + +Prerequisites for building NUT for Windows +------------------------------------------ + +Please see the documentation provided with the NUT sources about prerequisites +needed for semi-native builds (on Windows using MinGW x64 and MSYS2 projects) +and cross-builds (on Linux with mingw64 packages): + +* linkdoc:/docs/config-prereqs.txt +* linkdoc:/scripts/Windows/README.adoc + +With these dependencies in place, the common NUT `ci_build.sh` script can be +used to wrap the typical configuration, build and installation procedures. +Similar results can be of course achieved by direct use of the `configure` +script, which may be the way to go particularly for customized builds. + +See also `scripts/Windows/build-mingw-nut.sh` for the cross-build wrapper +details. + +Prerequisites for building the installer +---------------------------------------- + +NUT for Windows uses the WiX Toolset to codify and implement the installer: + +* https://wixtoolset.org/ +* https://wixtoolset.org/docs/intro/ +* https://wixtoolset.org/docs/tools/wixexe/ +* https://wixtoolset.org/docs/tools/heat/ +* link:https://wixtoolset.org/docs/v3/overview/candle/[candle.exe] and + link:https://wixtoolset.org/docs/v3/overview/light/[light.exe] tools + were used in current scripts (dating from 2013) but were obsoleted + after WiX v3 and became part of `wix.exe` according to + https://wixtoolset.org/docs/fourthree/ update notes +* https://github.com/wixtoolset/ +* https://github.com/wixtoolset/wix/ + +The toolset in turn requires the .NET framework (and the related NuGet tool) +which is available on Windows and Linux platforms, at least. In case of the +latter (for cross-builds) note that many of the newer distribution releases +should include dotnet packages in their repositories; for earlier releases +a Microsoft repository can be added -- as detailed in their documentation: + +* https://learn.microsoft.com/en-us/dotnet/core/install/linux +* https://dotnet.microsoft.com/en-us/download +* https://www.nuget.org/downloads + +For example, on Ubuntu the installation can be performed like this: +---- +:; sudo apt-get update && \ + sudo apt-get install -y dotnet-sdk-7.0 nuget +---- + +NOTE: For installation on Ubuntu 20.04 and older, where an additional +package repository is required, please see instructions at +https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-2004 + +Subsequent installation of the toolset on either platform would be: +---- +:; dotnet tool install --global wix +---- + +Maintaining the recipes and building the installer artifact +----------------------------------------------------------- + +NOTE: To be investigated, modernized and completed per +https://github.com/networkupstools/nut/issues/1485 + +Files were not touched considerably since 2013 (or earlier?) and would +likely need deeper investigation and update to be useful. Findings so far: + +- Directory `scripts/Windows/DriverInstaller` contains a helper + program (currently named `wdi-simple`) for driver installation + +- Directory `scripts/Windows/Installer` (with this document) contains + the batch file script to call WiX tools (currently geared for WiX v3 + or older) and the XML file which describes the installer actions and + the file/directory structures it would deliver. + * It would be helpful to find how to generate the filesystem structure + (names, `GUID`s, what else?) based on the prototype area prepared by + `make install DESTDIR=...` during a NUT build. It does not have to + be part of *every* build (e.g. we can want this separated by driver + types to allow end-users to install just a subset of NUT, similar + to how this happens with multi-package approach on Unix/Linux) but + an automated process which makes maintenance easier would be great: + let a human sort where different filenames are categorized, and let + automation discover that some installed files are not mentioned or + some listed files are not present. + +- Directory `scripts/Windows/Installer/ImageFiles` contains various + resources used by the installer, including apparently `Others/` for + third-party files and `emptyDir` as a target for NUT `make install`? + * This may be moderately compatible with what the Makefile driven + installation does with current codebase -- especially how it pulls + third-party library DLL files into the prototype install area. + +- The `scripts/Windows/wininit.c` program provides a rough equivalent + to POSIX init scripts, to wrap various NUT daemons as a single unit. + Its git history saw several re-architecting decisions, so if there + are any concerns if the current version does "the right thing", we + have some precedent ideas to look at. To a lesser extent, similar + code might have appeared and disappeared in the Windows-branch code + of daemons like `upsd` and `upsmon` before it condensed in this one. + +The batch file and the XML manifest currently list a few libraries from +msys/mingw (as of back when this was coded). Current Makefile recipes +take a more active approach by walking the dynamic dependencies of the +built NUT binaries to copy those from the build environment. + +The batch file takes care to pass sample configuration and documentation +files through `unix2dos` -- keep this in mind for new implementations. diff --git a/scripts/Windows/Makefile b/scripts/Windows/Makefile deleted file mode 100644 index abf1b97368..0000000000 --- a/scripts/Windows/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -halt: halt.c - gcc -mwindows -mno-cygwin -s -o halt.exe halt.c diff --git a/scripts/Windows/Makefile.am b/scripts/Windows/Makefile.am new file mode 100644 index 0000000000..abbfd32a37 --- /dev/null +++ b/scripts/Windows/Makefile.am @@ -0,0 +1,97 @@ +# Network UPS Tools: scripts/Windows + +AM_CFLAGS = -I$(top_srcdir)/include + +../include/nut_version.h: FORCE + +cd ../include/ && $(MAKE) $(AM_MAKEFLAGS) nut_version.h + +EXTRA_DIST = \ + winevent.mc \ + build-mingw-nut.sh \ + build-mingw-prereqs.sh \ + dllldd.sh \ + README.adoc \ + DriverInstaller/wdi-simple.c \ + DriverInstaller/README.adoc \ + Installer/README.adoc \ + Installer/BuildInstaller.bat \ + Installer/NUT-Installer.xml.in \ + Installer/ImageFiles/icons/completi.ico \ + Installer/ImageFiles/icons/info.ico \ + Installer/ImageFiles/icons/custicon.ico \ + Installer/ImageFiles/icons/Up.ico \ + Installer/ImageFiles/icons/exclamic.ico \ + Installer/ImageFiles/icons/powernut_Stop.ico \ + Installer/ImageFiles/icons/repairic.ico \ + Installer/ImageFiles/icons/New.ico \ + Installer/ImageFiles/icons/removico.ico \ + Installer/ImageFiles/icons/powernut_logo.ico \ + Installer/ImageFiles/icons/insticon.ico \ + Installer/ImageFiles/Images/NUT_wix_vertical.bmp \ + Installer/ImageFiles/Images/NUT_wix_horizontal.bmp \ + Installer/ImageFiles/Others/StopService.bat \ + Installer/ImageFiles/Others/StartService.bat \ + Installer/ImageFiles/emptyDir/man1/temp.txt \ + Installer/ImageFiles/emptyDir/cgi-bin/temp.txt \ + Installer/ImageFiles/emptyDir/pkgconfig/temp.txt \ + Installer/ImageFiles/emptyDir/include/temp.txt \ + Installer/ImageFiles/emptyDir/html/temp.txt \ + Installer/ImageFiles/emptyDir/man3/temp.txt \ + Installer/ImageFiles/emptyDir/run/temp.txt + +CLEANFILES = *-spellchecked */*-spellchecked + +bin_PROGRAMS = + +FORCE: + +if HAVE_MINGW_RESGEN + +# Avoid "Using $< in a non-suffix rule context is a GNUmake idiom" by $? +winevent.rc winevent.h: winevent.mc + $(WINDMC) $? + +winevent.o: winevent.rc winevent.h + $(WINDRES) winevent.rc winevent.o + +wininit.$(OBJEXT): winevent.h + +# Some binutils packages (e.g. on Slackware 15) include windres and windmc +# tools, even though they do not deliver the rest of files such as windows.h +# that would be needed for actual builds targeting Windows. +if HAVE_WINDOWS +bin_PROGRAMS += nut halt + +nut_SOURCES = wininit.c +nut_LDADD = ../../common/libcommon.la winevent.o + +halt_SOURCES = halt.c +endif HAVE_WINDOWS + +CLEANFILES += winevent.rc winevent.o winevent.h + +endif HAVE_MINGW_RESGEN + +SPELLCHECK_SRC = README.adoc DriverInstaller/README.adoc Installer/README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/Windows/README.adoc b/scripts/Windows/README.adoc new file mode 100644 index 0000000000..a3ad9e67a0 --- /dev/null +++ b/scripts/Windows/README.adoc @@ -0,0 +1,753 @@ +NUT and MS Windows +================== + +Introduction +------------ + +NUT is now also available for the Microsoft Windows platform. + +This methodology (and Windows support in general) are currently experimental, +so pull requests are welcome to tie up some loose ends (add more prerequisites, +test and fix programs, re-enable some code just commented away by `ifdef`s...) + +NOTE: It is possible to prepare a Windows machine with tools and prerequisites +for building NUT natively, as detailed in `docs/config-prereqs.txt` and easily +handled by NUT common `ci_build.sh` script. Most prerequisites are already +packaged in that environment, but notably net-snmp is missing -- but can be +built from source following this document. Possibly, the instructions below +would converge there over time to keep it simple. + +For additional reference about prerequisite preparation and further ideas for +the NUT for Windows effort, please see detailed report in the mailing list: + +* https://alioth-lists.debian.net/pipermail/nut-upsdev/2016-April/007171.html - + [Nut-upsdev] How to build NUT Windows Port + +* https://alioth-lists.debian.net/pipermail/nut-upsdev/2016-April/007172.html - + [Nut-upsdev] NUT Windows port sources review + + +Cross compiling from Linux +-------------------------- + +Fortunately, you are not forced to have a real Windows system to compile NUT. +The following chapters will guide you through setting up up a +link:http://mingw-w64.sourceforge.net[MinGW-w64] build environment and +compiling NUT. + +NOTE: These instructions were re-verified (and codebase slightly amended) +with an Ubuntu 21.10 container as the dedicated build environment. +Support was added to NUT common `ci_build.sh` script to call the helper +`build-mingw-nut.sh` from this directory when cross-building on Linux +for Windows in the specially crafted sandbox (conformance is assumed), +if you use one of `BUILD_TYPE=cross-windows-mingw(-64|-32|)` as this +made NUT CI farm integration easier. + +NOTE: The Ubuntu (20.04 or newer) environment provided by WSL2 also seems +suitable for semi-native builds on a Windows system, following the same +instructions. However, note that some builds may be broken or complicated +by antivirus software (it really dislikes someone writing into EXE files). + +Beside MinGW detailed below, you would need the usual dependencies to +configure and build NUT (if you would bootstrap it from github sources +rather than a tarball -- without a pre-generated `configure` script). +Notably, `asciidoc` with its many dependencies may be required for +generation of man pages into the intermediate tarball used by script +referenced below. + +MinGW-w64 build environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You will first need to setup a MinGW-w64 build environment. + +NOTE: While adding `ccache` is optional, it is quite recommended especially +if you plan to iterate many builds (whether of NUT or the dependencies). +At least for Ubuntu 21.10 packaging, it is integrated with `mingw-w64` +tool naming out of the box. + +On Debian/Ubuntu and similar systems, use: + + # apt-get update + # apt-get install mingw-w64 + +On Redhat and similar systems, use: + + # ??? + +You will also need pthread and mingw regex libraries, and other recommended +dependencies as detailed below. + +[NOTE] +================================================================================ + +If you use script `./build-mingw-nut.sh` mentioned below, you may skip setting +these environment variables when building NUT. You would however need to use +them once (per `ARCH`) to provide the prerequisites below if built from source. + +When using the compilation approach, use the following HOST_FLAG, BUILD_FLAG +and CC, CFLAGS, LDFLAGS and PREFIX: + +- prefer either to +------ +:; export ARCH="x86_64-w64-mingw32" +------ +or +------ +:; export ARCH="i686-w64-mingw32" +------ +(it can help to open two terminals and copy one ARCH into each + and then the lines below into both of them; be sure to use separate + directory trees for the unpacked build workspaces) + +- for either-ARCH build environment further set: +------ +:; export HOST_FLAG="--host=$ARCH" +:; PREFIX="/usr/$ARCH" +------ + +- NOTE: Technically, these instructions may apply to builds with MinGW on + Windows semi-natively (e.g. to add the net-snmp libraries which are not + packaged for MSYS2 MinGW currently). Generally you can use environment + variables set by different launchers of MinGW terminal sessions depending + on the target profile (32/64 bit, gcc/clang, libc implementation...) + +** For `sudo make install` in instructions below, you may have to omit the + `sudo` part if missing in your MSYS2 MinGW environment; + +** For a "native build" directly for consumption in the currently configured + environment, you would not use cross-build path in `PREFIX` and not set + the `HOST_FLAG` value: ++ +------ +:; export ARCH="$MINGW_CHOST" +:; PREFIX="$MINGW_PREFIX" +:; export HOST_FLAG="" +------ + +** If you wanted a "real cross-build" for a different MinGW environment, you + might want to set those (but the NUT build would then need to be told to + search for headers, libraries and pkg-config data in extra locations): ++ +------ +:; export ARCH="$MINGW_CHOST" +:; PREFIX="$MINGW_PREFIX/$ARCH" +:; export HOST_FLAG="--host=$ARCH" +------ +** You might want then to verify that it sets values you expect with a command + like this: ++ +------ +:; set | grep -E '^(ARCH|PREFIX|HOST_FLAG)=' +#export ARCH="x86_64-w64-mingw32" +#PREFIX="/mingw64/x86_64-w64-mingw32" +------ + +- on Debian/Ubuntu style systems also: +------ +:; BUILD_FLAG="--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE`" +------ + * Note that this bit is very Debian specific! + Hints for Redhat-style systems are wanted. + +- also export the following compilation flags: +------ +:; export CFLAGS="$CFLAGS -D_POSIX=1 -I${PREFIX}/include/" +:; export CXXFLAGS="$CXXFLAGS -D_POSIX=1 -I${PREFIX}/include/" +:; export LDFLAGS="$LDFLAGS -L${PREFIX}/lib/" +:; export PKG_CONFIG_PATH="${PREFIX}"/lib/pkgconfig +------ + +- prepare the download and build area, e.g. to match copy-paste instructions +below, it would be like: +------ +:; DLDIR=~/nut-win-deps +:; WSDIR="$DLDIR"/"$ARCH" +:; mkdir -p "$WSDIR" "$DLDIR" +------ + +================================================================================ + + +pthread library +^^^^^^^^^^^^^^^ + +NOTE: The MinGW distribution in Ubuntu 21.10 already includes pthread files, +so the build instructions below were not relevant for this component. + +On older Debian systems, you can use the following packages repository: +link:https://launchpad.net/~mingw-packages/+archive/ppa[MinGW PPA] + +However at the moment this PPA seems to be stale and serve very old packages, +so it could be better to roll your own as detailed below. + +On Redhat: FIXME + +//////////////////////////////////////////////////////////////////////////////// +http://fedoraproject.org/wiki/MinGW/CrossCompilerFramework +https://fedoraproject.org/wiki/Packaging:MinGW?rd=Packaging:MinGW_Future +https://fedoraproject.org/wiki/Packaging:MinGW_Old +//////////////////////////////////////////////////////////////////////////////// + +You can also compile it (where that is still needed) using: + + :; ( cd "$DLDIR" && wget -c http://mirrors.kernel.org/sources.redhat.com/pthreads-win32/pthreads-w32-2-8-0-release.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/pthreads-w32-2-8-0-release.tar.gz + :; cd pthreads-w32-2-8-0-release/ + :; make -f GNUmakefile "CROSS=$ARCH-" $BUILD_FLAG GC-inlined + :; sudo cp *.dll ${PREFIX}/pthreads/lib/ + :; sudo cp *.a ${PREFIX}/lib/ + :; sudo cp pthread.h sched.h semaphore.h ${PREFIX}/pthreads/include + + +MinGW regex library +^^^^^^^^^^^^^^^^^^^ + +You can compile it using: + + :; ( cd "$DLDIR" && wget -c http://netcologne.dl.sourceforge.net/project/mingw/Other/UserContributed/regex/mingw-regex-2.5.1/mingw-libgnurx-2.5.1-src.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/mingw-libgnurx-2.5.1-src.tar.gz + :; cd mingw-libgnurx-2.5.1 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +libtool (libltdl) +^^^^^^^^^^^^^^^^^ + + :; ( cd "$DLDIR" && wget -c https://ftpmirror.gnu.org/libtool/libtool-2.4.6.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libtool-2.4.6.tar.gz + :; cd libtool-2.4.6 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +libusb +^^^^^^ + +* libusb-1.0 + + :; ( cd "$DLDIR" && wget -c https://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-1.0.26/libusb-1.0.26.tar.bz2 ) + :; cd "$WSDIR" + :; tar xjf "$DLDIR"/libusb-1.0.26.tar.bz2 + :; cd libusb-1.0.26 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + +* libusb-compat-0.1 (API translation layer for older codebase, uses libusb-1.0) + + :; ( cd "$DLDIR" && wget -c https://github.com/libusb/libusb-compat-0.1/archive/refs/heads/master.zip -O libusb-compat-0.1-master.zip ) + :; cd "$WSDIR" + :; unzip "$DLDIR"/libusb-compat-0.1-master.zip + :; cd libusb-compat-0.1-master + :; ./bootstrap.sh + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +zlib +^^^^ + +Needed for libpng at least, but likely many others too. + +[NOTE] +====== +On recent Debian/Ubuntu systems, you might have luck with: +------ +:; sudo apt-get install libz-mingw-w64-dev +------ +====== + +On any system, you can build from source; however the current version has +a nuance to address for mingw builds: + + :; ( cd "$DLDIR" && wget -c -O zlib-1.2.12.tar.gz https://github.com/madler/zlib/archive/refs/tags/v1.2.12.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/zlib-1.2.12.tar.gz + :; cd zlib-1.2.12 + + # Edit the `configure` script (not autotools generated) to + # neuter the MINGW `leave 1` line: + MINGW* | mingw*) + # temporary bypass + rm -f $test.[co] $test $test$shared_ext + echo "Please use win32/Makefile.gcc instead." | tee -a configure.log + - leave 1 + + ###leave 1 + LDSHARED=${LDSHARED-"$cc -shared"} + LDSHAREDLIBC="" + EXE='.exe' ;; + + :; CHOST="$ARCH" ./configure --prefix="$PREFIX" + :; make + :; sudo make install + + +openssl +^^^^^^^ + +OpenSSL is an optional dependency for NUT itself (it or Mozilla NSS can +be used to protect the networking communications), and for libneon below +(OpenSSL or GnuTLS). + +Note the non-standard `config` script bundled along, and hoops to jump +through... + + :; ( cd "$DLDIR" && wget -c https://www.openssl.org/source/openssl-1.1.1q.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/openssl-1.1.1q.tar.gz + :; cd openssl-1.1.1q + # Build options partially lifted from OBS packaging, see: + # https://build.opensuse.org/package/view_file/windows:mingw:win32/mingw32-openssl-1_1/mingw32-openssl-1_1.spec?expand=1 + :; ( case "$ARCH" in + *x86_64*) SYSTEM=MINGW64 ;; + *i?86*) SYSTEM=MINGW32 ;; + *) SYSTEM=MINGW ;; + esac + export SYSTEM + ./config \ + no-idea enable-rfc3779 zlib shared \ + -fno-common \ + --prefix="$PREFIX" --cross-compile-prefix="/usr/bin/$ARCH-" \ + -DPURIFY -D__USE_GNU + ) + :; make + :; sudo make install + + +xz (liblzma) +^^^^^^^^^^^^ + +Needed for libxml2. + + :; ( cd "$DLDIR" && wget -c https://tukaani.org/xz/xz-5.2.5.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/xz-5.2.5.tar.gz + :; cd xz-5.2.5 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +libxml2 +^^^^^^^ + +Needed for libneon. + +///////////////////////////////////////////////////////////////////////////// +// ...and for fontconfig +///////////////////////////////////////////////////////////////////////////// + + :; ( cd "$DLDIR" && wget -c https://gitlab.gnome.org/GNOME/libxml2/-/archive/v2.9.14/libxml2-v2.9.14.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libxml2-v2.9.14.tar.gz + :; cd libxml2-v2.9.14 + :; ./autogen.sh --prefix="$PREFIX" $HOST_FLAG --without-python + :; make + :; sudo make install + + +gd (cgi) +^^^^^^^^ + +Note that for the general-case build libgd supports a huge dependency tree, +so for the NUT purposes we are going for as little as possible. + +Initially the purpose was to have libgd installed to build/link against for +the Windows target. Subsequently more dependencies were documented, but still +further refinement may be needed to have it actually usable for CGI web pages +rendering. + +Finally note that end-users would have to install a CGI-capable web server to +use this feature in practice. + +* zlib: see above + +* iconv: ++ +Seems to be directly used by `libgd` (at least queried in its `configure` script): + + :; ( cd "$DLDIR" && wget -c https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.17.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libiconv-1.17.tar.gz + :; cd libiconv-1.17 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +* freetype: ++ +NOTE: For some reason it won't build in an Ubuntu 20.04 running under WSL2 -- +blocks running an `apinames.exe` program that it has just built. May be a +problem of the emulation layer (as it calls the EXE via `/tools/init`), +and/or of the system antivirus interaction?.. + + :; ( cd "$DLDIR" && wget -c https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/freetype-2.12.1.tar.gz + :; cd freetype-2.12.1 + :; ./configure --prefix="$PREFIX" $HOST_FLAG --without-brotli + :; make + :; sudo make install + + +///////////////////////////////////////////////////////////////////////////// +WIP - fontconfig not usable yet due to ICU failing to cross-build +Maybe an older version of either would fare better?.. + +* gperf is needed by fontconfig build routine as a tool usable on the build + system, so a packaged install should suffice, e.g. `apt-get install gperf`. + Otherwise, build one from source (and note that this may be needed e.g. + for native builds on Windows, but not "just" for using a cross-binary that + the build host can generate but not run natively). + + :; ( cd "$DLDIR" && wget -c http://ftp.gnu.org/pub/gnu/gperf/gperf-3.1.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/gperf-3.1.tar.gz + :; cd gperf-3.1 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +* ICU: + +For a cross-build of ICU, you should first build and install a copy +for the host system, then use it as a resource for the final build. +Set aside a good part of an hour to get this built: + + :; ( cd "$DLDIR" && wget -c https://github.com/unicode-org/icu/releases/download/release-71-1/icu4c-71_1-src.tgz ) + + # This one is built for native host architecture and provides + # some resources to ARCH builds later on: + :; cd "$DLDIR" + :; tar xzf icu4c-71_1-src.tgz + :; rm -rf icu-native || true + :; mv icu icu-native # note, no version in the pathname + :; cd icu-native/source + :; ./configure CFLAGS= CXXFLAGS= LDFLAGS= ARCH= + :; make + + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/icu4c-71_1-src.tgz + :; cd icu/source + :; ./configure --prefix="$PREFIX" $HOST_FLAG --with-cross-build="$DLDIR"/icu-native/source + :; make + :; sudo make install + + +* fontconfig: + + :; ( cd "$DLDIR" && wget -c https://www.freedesktop.org/software/fontconfig/release/fontconfig-2.14.0.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/fontconfig-2.14.0.tar.gz + :; cd fontconfig-2.14.0/ + :; ./configure --prefix="$PREFIX" $HOST_FLAG --enable-libxml2 + :; make + :; sudo make install + +// for fontconfig +///////////////////////////////////////////////////////////////////////////// + + +* libpng: + + :; ( cd "$DLDIR" && wget -c https://download.sourceforge.net/libpng/libpng-1.6.37.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libpng-1.6.37.tar.gz + :; cd libpng-1.6.37 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + +* libgd itself: ++ +TODO: This works for 64-bit builds, however 32-bit ones are burdened with `@8` +or `@12` suffixes to symbol names, and subsequent link checks in NUT fail to +find `libgd` as usable -- so CGI is not built in 32-bit mode. According to +such context, this must be something about STDCALL and/or "extern C"... + + :; ( cd "$DLDIR" && wget -c https://github.com/libgd/libgd/releases/download/gd-2.3.3/libgd-2.3.3.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libgd-2.3.3.tar.gz + :; cd libgd-2.3.3 + :; ./configure --prefix="$PREFIX" $HOST_FLAG \ + --with-png --with-freetype \ + --without-tiff --without-jpeg --without-xpm \ + --without-fontconfig + # Note: currently we configure away almost all capabilities, + # to match the dependency binaries (and/or headers) present + # on the build system. Review resulting build recipes that + # they DO NOT refer to system /usr/include locations! + # In practice we would likely need the fontconfig pieces to work. + :; make + :; sudo make install + + +libmodbus +^^^^^^^^^ + + :; ( cd "$DLDIR" && wget -c https://libmodbus.org/releases/libmodbus-3.1.7.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/libmodbus-3.1.7.tar.gz + :; cd libmodbus-3.1.7 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +net-snmp +^^^^^^^^ + + :; ( cd "$DLDIR" && wget -c https://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/net-snmp-5.9.1.tar.gz + :; cd net-snmp-5.9.1 + :; yes "" | ./configure --prefix="$PREFIX" $HOST_FLAG \ + --with-default-snmp-version=3 --disable-agent --disable-daemon \ + --with-sys-contact="" --with-sys-location="" --with-logfile=none \ + --with-persistent-directory="${PREFIX}/var/net-snmp" \ + --disable-embedded-perl --without-perl-modules --disable-perl-cc-checks \ + --enable-shared + # NOTE: ./configure script may ask a few questions, or may just print + # a banner that it would; hopefully all replies needed for current + # version are covered above + # The following long `LDFLAGS` ensure that shared `libnetsnmp-40.dll` + # gets built and later installed (and siblings which NUT does not use): + :; make LDFLAGS="-no-undefined -lws2_32 -lregex -Xlinker --ignore-unresolved-symbol=_app_name_long -Xlinker --ignore-unresolved-symbol=app_name_long" + :; find . -type f -name '*.dll' -o -name '*.dll.a' + :; sudo make install + +NOTE: net-snmp tends to only `make` a static-linking library for Windows +by default (the shared library only appears with `LDFLAGS` proposed above). +In this case consumers must link not only with `-lnetsnmp` but also its +dependencies explicitly -- see `Libs.private` line in `netsnmp.pc` of +your build (or installation in `${PREFIX}/lib/pkgconfig/netsnmp.pc`). +Builds can extract this info with `pkg-config --libs --static netsnmp` +as NUT scenarios do (for mingw, if shared-linking attempt fails). + + +libneon +^^^^^^^ + +As of release 0.32.2 libneon failed to build -- neither in Windows MSYS2 nor +in Linux mingw environments. Some tinkering was needed to make it happen, and +was posted as https://github.com/notroj/neon/pull/84 (pull request sourced +from https://github.com/jimklimov/neon/tree/fix-mingw-cross branch). Due to +this, instructions below differ from others by setting up an out-of-tree build +instead of a tarball download. Eventually it would hopefully suffice to fetch +https://notroj.github.io/neon/neon-0.32.5.tar.gz or newer (PR was merged after +libneon 0.32.4 release). + +NOTE: Ability to `make docs` here relies on presence of `xmlto` program. +In NUT CI workers prepared according to `docs/config-prereqs.txt` this should +be among dependencies for `asciidoc`; beware that with prerequisites it has +quite a large installation footprint. Alternately check PR #69, or consult +the Makefiles for current `install` target definition to run its job without +`install-docs` part (example posted below). + + #:; ( cd "$DLDIR" && git clone -b fix-mingw-cross https://github.com/jimklimov/neon neon-git ) + :; ( cd "$DLDIR" && git clone https://github.com/notroj/neon neon-git ) + :; ( cd "$DLDIR/neon-git" && ./autogen.sh ) + :; cd "$WSDIR" + :; rm -rf neon-git ; mkdir neon-git + :; cd neon-git + :; "$DLDIR"/neon-git/configure --prefix="$PREFIX" $HOST_FLAG \ + --enable-shared --with-ssl=openssl + :; make all docs + :; sudo make install \ + || sudo make install-lib install-headers install-config install-nls ###install-docs + + +///////////////////////////////////////////////////////////////////////////// +// for avahi + +libpcre +^^^^^^^ + +Needed for glib2 (further for avahi). + + :; ( cd "$DLDIR" && wget -c https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.40/pcre2-10.40.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/pcre2-10.40.tar.gz + :; cd pcre2-10.40 + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make all + :; sudo make install + :; sudo ln -s libpcre2-posix.pc ${PREFIX}/lib/pkgconfig/libpcre.pc + + +gettext/libintl +^^^^^^^^^^^^^^^ + +WARNING: Currently gettext does not build, at least on WSL2 Ubuntu, +fails with `undefined reference to '__imp_formatstring_ruby'`. + +Needed for glib2 (further for avahi). + + :; ( cd "$DLDIR" && wget -c https://ftp.gnu.org/pub/gnu/gettext/gettext-0.20.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/gettext-0.20.tar.gz + :; cd gettext-0.20 + # Flags tweaked due to http://savannah.gnu.org/bugs/?36443 + :; ./configure --prefix="$PREFIX" $HOST_FLAG \ + CFLAGS="$CFLAGS -O2" CXXFLAGS="$CXXFLAGS -O2" + :; make all + :; sudo make install + + +glib2 +^^^^^ + +WARNING: Currently glib2 does not build, at least on WSL2 Ubuntu, fails to +find gettext (not built above). + +Needed for avahi. + +Requires `meson` build system, e.g. via: +------ +:; sudo apt-get install meson +------ + +NOTE: Latest glib-2.73.3 as of this writing requires `meson >= 0.60.0` but the +one provided in WSL2 Ubuntu 20.04 OS packages is older (0.53.2). In this case, +try an older glib2 release, e.g. glib-2.72.1 seems compatible. + +Configuration options below were initially borrowed from +https://build.opensuse.org/package/view_file/windows:mingw:win32/mingw32-glib2/mingw32-glib2.spec?expand=1 + + :; ( cd "$DLDIR" && wget -c https://gitlab.gnome.org/GNOME/glib/-/archive/2.72.1/glib-2.72.1.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/glib-2.72.1.tar.gz + :; cd glib-2.72.1 + :; case "$ARCH" in + *x86_64*) cp -pf .gitlab-ci/cross_file_mingw64.txt .gitlab-ci/cross_file_${ARCH}.txt ;; + *i686*) sed 's,x86_64,i686,g' < .gitlab-ci/cross_file_mingw64.txt > .gitlab-ci/cross_file_${ARCH}.txt ;; + esac + # We do not have an ARCH-dependent pkg-config binary: + :; sed "s,^pkgconfig = .*\$,pkgconfig = 'pkg-config'," \ + -i .gitlab-ci/cross_file_${ARCH}.txt + # With meson, config and build can be done in one shot: + :; meson --prefix="$PREFIX" \ + --buildtype=plain --wrap-mode=nodownload \ + --auto-features=auto \ + --cross-file=.gitlab-ci/cross_file_${ARCH}.txt \ + . build \ + --default-library=shared \ + -Dman=false -Dgtk_doc=false \ + -Dsystemtap=false -Ddtrace=false \ + -Dinstalled_tests=false -Dlibelf=disabled + :; sudo meson install + + +avahi +^^^^^ + +WARNING: Currently avahi does not build, at least on WSL2 Ubuntu, fails to +find glib2 (not built above). + +Release 0.8 (and current git as of this writing) sources use `-Wl,-z...` +linking flags which are not supported by mingw `ld` tooling, and so fail +to configure. + +[NOTE] +====== +To build from Git sources or regenerate `configure` in tarball sources +like shown below, you should also have `gettextize` tool before running +`autogen.sh`, e.g. via: +------ +:; sudo apt install intltool autopoint +:; ( cd "$DLDIR" && git clone https://github.com/lathiat/avahi avahi-git ) +:; ( cd "$DLDIR/avahi-git" && ./autogen.sh ) +# Stock script would go on to try configuring by default, +# and that bit fails in cross-env vars +------ +====== + + :; ( cd "$DLDIR" && wget -c https://github.com/lathiat/avahi/releases/download/v0.8/avahi-0.8.tar.gz ) + :; cd "$WSDIR" + :; tar xzf "$DLDIR"/avahi-0.8.tar.gz + :; cd avahi-0.8 + :; vi common/acx_pthread.m4 + # Edit `common/acx_pthread.m4` to remove `-Wl,-z,defs` near + # AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) + # Alternatively just edit `configure` script from the tarball. + :; ./autogen.sh || true + :; ./configure --prefix="$PREFIX" $HOST_FLAG + :; make + :; sudo make install + + +// for avahi +///////////////////////////////////////////////////////////////////////////// + + +Other requirements +^^^^^^^^^^^^^^^^^^ + +ipmi, ssl with Mozilla NSS... + +* https://ftp.gnu.org/gnu/freeipmi/freeipmi-1.6.9.tar.gz +* https://ftp.mozilla.org/pub/security/nss/releases/NSS_3_79_RTM/src/nss-3.79-with-nspr-4.34.tar.gz + + +Cross-building NUT with MinGW in Linux +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After preparing at least the required dependencies above, use one of +the following methods to compile NUT as: + +* out-of-tree from git source (easier to iterate for development, so default): + + (cd scripts/Windows/ && ./build-mingw-nut.sh all64) ++ +or, depending on the build environment(s) you have prepared, ++ + (cd scripts/Windows/ && ./build-mingw-nut.sh all32) ++ +[NOTE] +====== +This is also automated for common NUT CI build script, calling it like this: +------ +# Try to guess bitness based on ARCH or CFLAGS: +BUILD_TYPE=cross-windows-mingw ./ci_build.sh + +# Or specifically: +BUILD_TYPE=cross-windows-mingw-32 ./ci_build.sh +BUILD_TYPE=cross-windows-mingw-64 ./ci_build.sh +------ +====== + + +* an existing source tarball (can be fetched from NUT website): + + :; export SOURCEMODE=stable + ### Optionally: export VER_OPT=2.8.1 + :; cd scripts/Windows/ + :; ./build-mingw-nut.sh + + +* To (re-)build from scratch with a dist tarball, e.g. testing how a stable +release would fare, starting from a git checkout, use this: + + :; ./autogen.sh && ./configure && make dist && \ + (cd scripts/Windows/ && SOURCEMODE=dist ./build-mingw-nut.sh all64) + +If everything goes fine, you will find a NUT installation tree in 'nut_install' +sub-directory. Note the script accepts some parameters e.g. for 32/64 bit build +targets. + +NOTE: For other ways of building and packaging, it might make sense for +a packaged delivery to also `make install DESTDIR=.../nut_install` from +the sources of dependency projects built above, or at least to copy the +built `*.dll` files from `${PREFIX}/bin` to `nut_install/bin`. For those +dependencies that are listed above, the script does this best-effort +activity (does not fail if some are missing, but running the programs +can fail later). diff --git a/scripts/Windows/build-mingw-nut.sh b/scripts/Windows/build-mingw-nut.sh new file mode 100755 index 0000000000..bafccc0ab4 --- /dev/null +++ b/scripts/Windows/build-mingw-nut.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash + +# NOTE: bash syntax (non-POSIX script) is used below! +# +# script to cross compile NUT for Windows from Linux using MinGW-w64 +# http://mingw-w64.sourceforge.net/ + +#set -x + +SCRIPTDIR="`dirname "$0"`" +SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" + +DLLLDD_SOURCED=true . "${SCRIPTDIR}/dllldd.sh" + +# default to update source then build + +# These paths are somewhat related: +[ -n "${WINDIR-}" ] || WINDIR="$(pwd)" +[ -n "${TOP_DIR-}" ] || TOP_DIR="$WINDIR/../.." + +# These may be located elsewhere: +[ -n "${BUILD_DIR-}" ] || BUILD_DIR="$WINDIR/nut_build" +[ -n "${INSTALL_DIR-}" ] || INSTALL_DIR="$WINDIR/nut_install" + +# This should match the tarball and directory name, +# if a stable version is used: +[ -n "$VER_OPT" ] || VER_OPT=2.8.1 +DEBUG=true + +# default to 32bits build +# Note: README specifies dependencies to pre-build and install; +# those DLLs should correspond to same architecture selection +cmd=all32 +if [ -n "$1" ] ; then + cmd=$1 +fi + +[ -n "$SOURCEMODE" ] || SOURCEMODE="out-of-tree" + +rm -rf "$BUILD_DIR" "$INSTALL_DIR" +CONFIGURE_SCRIPT="./configure" +case "$SOURCEMODE" in +stable) +# FIXME +# Stable version (download the latest stable archive) + VER_OPT_SHORT="`echo "$VER_OPT" | awk -F. '{print $1"."$2}'`" + if [ ! -s "nut-$VER_OPT.tar.gz" ] ; then + wget "https://www.networkupstools.org/source/$VER_OPT_SHORT/nut-$VER_OPT.tar.gz" + fi + rm -rf "nut-$VER_OPT" + tar -xzf "nut-$VER_OPT.tar.gz" + mv "nut-$VER_OPT" "$BUILD_DIR" + ;; +dist) + # In-place version (no download) + cd ../.. + rm -f nut-?.?.?*.tar.gz + [ -s Makefile ] || { ./autogen.sh && ./configure; } + make dist + SRC_ARCHIVE=$(ls -1 nut-?.?.?*.tar.gz | sort -n | tail -1) + cd scripts/Windows + tar -xzf "../../$SRC_ARCHIVE" + mv nut-?.?.?* "$BUILD_DIR" + ;; +out-of-tree) + CONFIGURE_SCRIPT="../../../configure" + cd ../.. + if [ ! -x ./configure ]; then + ./autogen.sh + fi + if [ -s Makefile ]; then + make distclean + fi + cd scripts/Windows + mkdir -p "$BUILD_DIR" + ;; +esac + +cd "$BUILD_DIR" || exit + +if [ -z "$INSTALL_WIN_BUNDLE" ]; then + echo "NOTE: You might want to export INSTALL_WIN_BUNDLE=true to use main NUT Makefile" + echo "recipe for DLL co-bundling (default: false to use logic maintained in $0" +fi >&2 + +if [ "$cmd" == "all64" ] || [ "$cmd" == "b64" ] || [ "$cmd" == "all32" ] || [ "$cmd" == "b32" ] ; then + ARCH="x86_64-w64-mingw32" + if [ "$cmd" == "all32" ] || [ "$cmd" == "b32" ] ; then + ARCH="i686-w64-mingw32" + fi + + HOST_FLAG="--host=$ARCH" + # --build needs to be specified, beside of --host, to avoid Warning + # but this version is very Debian specific!!! + # FIXME: find something more generic + BUILD_FLAG="--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE`" + export CC="$ARCH-gcc" + export CXX="$ARCH-g++" + + # TODO: Detect/parameterize? + # This prefix is currently valid for mingw packaging in Debian/Ubuntu. + ARCH_PREFIX="/usr/$ARCH" + export PATH="${ARCH_PREFIX}/bin:$PATH" + + # Note: _WIN32_WINNT>=0x0600 is needed for inet_ntop in mingw headers + # and the value 0xffff is anyway forced into some components at least + # by netsnmp cflags. + export CFLAGS+=" -D_POSIX=1 -D_POSIX_C_SOURCE=200112L -I${ARCH_PREFIX}/include/ -D_WIN32_WINNT=0xffff" + export CXXFLAGS+=" -D_POSIX=1 -D_POSIX_C_SOURCE=200112L -I${ARCH_PREFIX}/include/ -D_WIN32_WINNT=0xffff" + export LDFLAGS+=" -L${ARCH_PREFIX}/lib/" + + KEEP_NUT_REPORT_FEATURE_FLAG="" + if [ x"${KEEP_NUT_REPORT_FEATURE-}" = xtrue ]; then + KEEP_NUT_REPORT_FEATURE_FLAG="--enable-keep_nut_report_feature" + fi + + # Note: installation prefix here is "/" and desired INSTALL_DIR + # location is passed to `make install` as DESTDIR below. + $CONFIGURE_SCRIPT $HOST_FLAG $BUILD_FLAG --prefix=/ \ + $KEEP_NUT_REPORT_FEATURE_FLAG \ + PKG_CONFIG_PATH="${ARCH_PREFIX}/lib/pkgconfig" \ + --without-pkg-config --with-all=auto \ + --without-systemdsystemunitdir \ + --with-pynut=app \ + --with-augeas-lenses-dir=/augeas-lenses \ + --enable-Werror \ + || exit + echo "$0: configure phase complete ($?)" >&2 + + make 1>/dev/null || exit + echo "$0: build phase complete ($?)" >&2 + + if [ "x$INSTALL_WIN_BUNDLE" = xtrue ] ; then + # Going forward, this should be the main mode - "legacy code" + # below picked up and transplanted into main build scenarios: + echo "NOTE: INSTALL_WIN_BUNDLE==true so using main NUT Makefile logic for DLL co-bundling" >&2 + make install-win-bundle DESTDIR="${INSTALL_DIR}" || exit + else + # Legacy code from when NUT for Windows effort started; + # there is no plan to maintain it much (this script is PoC): + echo "NOTE: INSTALL_WIN_BUNDLE!=true so using built-in logic for DLL co-bundling" >&2 + + make install DESTDIR="${INSTALL_DIR}" || exit + + # Per docs, Windows loads DLLs from EXE file's dir or some + # system locations or finally PATH, so unless the caller set + # the latter, we can not load the pre-linked DLLs from ../lib: + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx#standard_search_order_for_desktop_applications + + # Be sure upsmon can run even if at cost of some duplication + # (maybe even do "cp -pf" if some system dislikes "ln"); also + # on a modern Windows one could go to their installed "sbin" to + # mklink .\libupsclient-3.dll ..\bin\libupsclient-3.dll + (cd "$INSTALL_DIR/bin" && ln libupsclient*.dll ../sbin/) + (cd "$INSTALL_DIR/cgi-bin" && ln ../bin/libupsclient*.dll ./) \ + || echo "NOTE: FAILED to process OPTIONAL cgi-bin directory; was NUT CGI enabled?" >&2 + + echo "NOTE: Adding third-party dependency libraries for each installed program" >&2 + echo " Do not worry about lack of libnut* and libups* in system locations" >&2 + + # Cover dependencies for nut-scanner (not pre-linked) + # Note: lib*snmp*.dll not listed below, it is + # statically linked into binaries that use it + (cd "$INSTALL_DIR/bin" && cp -pf "${ARCH_PREFIX}/bin"/{libgnurx,libusb,libltdl}*.dll .) || true + (cd "$INSTALL_DIR/bin" && cp -pf "${ARCH_PREFIX}/lib"/libwinpthread*.dll .) || true + + # Steam-roll over all executables/libs we have here and copy + # over resolved dependencies from the cross-build environment: + (cd "$INSTALL_DIR" && { dllldddir . | while read D ; do cp -pf "$D" ./bin/ ; done ; } ) || true + + # Hardlink libraries for sbin (alternative: all bins in one dir): + (cd "$INSTALL_DIR/sbin" && { DESTDIR="$INSTALL_DIR" dllldddir . | while read D ; do ln -f ../bin/"`basename "$D"`" ./ ; done ; } ) || true + + # Hardlink libraries for cgi-bin if present: + (cd "$INSTALL_DIR/cgi-bin" && { DESTDIR="$INSTALL_DIR" dllldddir . | while read D ; do ln -f ../bin/"`basename "$D"`" ./ ; done ; } ) \ + || echo "NOTE: FAILED to process OPTIONAL cgi-bin directory; was NUT CGI enabled?" >&2 + fi + + echo "$0: install phase complete ($?)" >&2 + cd .. +else + echo "Usage:" + echo " $0 [all64 | b64 | all32 | b32]" + echo " Default: 'all32'" + echo "Optionally export SOURCEMODE=[stable|dist|out-of-tree]" +fi diff --git a/scripts/Windows/build-mingw-prereqs.sh b/scripts/Windows/build-mingw-prereqs.sh new file mode 100755 index 0000000000..90e0a9a8d5 --- /dev/null +++ b/scripts/Windows/build-mingw-prereqs.sh @@ -0,0 +1,137 @@ +#!/bin/sh + +# Copyright (C) 2022-2023 by Jim Klimov +# Licensed same as NUT +# +# Helper automating the nuances from NUT::scripts/Windows/README.adoc +# to provide prerequisites needed in semi-native or cross-builds. +# +# NOTE: Currently constrained to providing net-snmp under MSYS2. +# We can not rely on certain common shell facilities like `true` +# and `false` programs being available (in PATH or at all). +# TODO: Support `make uninstall` attempts for older versions?.. + +prepareEnv() { + [ -n "${MAKE-}" ] || MAKE="make -j 8" + export MAKE + + if [ -z "${SUDO-}" ] ; then + SUDO=" " # avoid reeval + if (command -v sudo) ; then SUDO="sudo" ; fi + fi + export SUDO + + [ -n "${DLDIR-}" ] || DLDIR=~/nut-win-deps + + if [ -n "${ARCH-}" ] && [ -n "${PREFIX-}" ] ; then + [ -n "${WSDIR-}" ] || WSDIR="$DLDIR"/"$ARCH" + mkdir -p "$WSDIR" "$DLDIR" + return 0 + fi + + BUILD_FLAG="" + HOST_FLAG="" + export HOST_FLAG + if [ -n "${MSYS2_PATH-}" ]; then + # Assume semi-native build for same env + ARCH="$MINGW_CHOST" + PREFIX="$MINGW_PREFIX" + PATH="$PREFIX/bin:$PATH" + export ARCH PATH PREFIX + + if ! (command -v sudo) ; then sudo() ( "$@" ) ; fi + else + # TODO: Select by args, envvars, directory presence... + ARCH="x86_64-w64-mingw32" + #ARCH="i686-w64-mingw32" + + # Assumes Ubuntu/Debian with mingw prepared, per README + HOST_FLAG="--host=$ARCH" + PREFIX="/usr/$ARCH" + + export ARCH PREFIX + + if (command -v dpkg-architecture) ; then + BUILD_FLAG="--build=`dpkg-architecture -qDEB_BUILD_GNU_TYPE`" + fi + fi + + CFLAGS="$CFLAGS -D_POSIX=1 -I${PREFIX}/include/" + CXXFLAGS="$CXXFLAGS -D_POSIX=1 -I${PREFIX}/include/" + LDFLAGS="$LDFLAGS -L${PREFIX}/lib/" + PKG_CONFIG_PATH="${PREFIX}"/lib/pkgconfig + + export CFLAGS CXXFLAGS LDFLAGS PKG_CONFIG_PATH + + [ -n "${WSDIR-}" ] || WSDIR="$DLDIR"/"$ARCH" + mkdir -p "$WSDIR" "$DLDIR" +} + +# Provide prerequisites; reentrant (quick skip if installed; quick install if built) +provide_netsnmp() ( + PKGCFG_NAME="netsnmp" + DEP_PRJNAME="net-snmp" + DEP_VERSION="5.9.1" + DEP_DIRNAME="${DEP_PRJNAME}-${DEP_VERSION}" + DEP_ARCHIVE="${DEP_DIRNAME}.tar.gz" + + FORCE=false + if [ x"${1-}" = x"-f" ] ; then FORCE=true ; fi + + set +e + if [ x"$FORCE" = x"false" ] ; then + # TODO: Check version + if pkg-config --exists "$PKGCFG_NAME" ; then return 0 ; fi + + # Quickly install if prebuilt + if [ -d "${WSDIR}/${DEP_DIRNAME}/.inst" ]; then ( + cd "${WSDIR}/${DEP_DIRNAME}/.inst" || exit + (command -v rsync) && $SUDO rsync -avPHK ./ / && exit + $SUDO cp -pr ./ / && exit + exit 1 + ) && return 0 + fi + + # no stashed .inst; any Makefile at least? + if [ -s "${WSDIR}/${DEP_DIRNAME}/Makefile" ]; then ( cd "${WSDIR}/${DEP_DIRNAME}" && $SUDO $MAKE install ) && return ; fi + fi + + # (Re-)make and install from scratch + set -e + + # Funny ways to fetch from Sourceforge help get the archive, + # not the download page... For some reason, this bites CI + # builds on Appveyor but not local runs: + ( cd "$DLDIR" && curl -vL "https://sourceforge.net/projects/${DEP_PRJNAME}/files/${DEP_PRJNAME}/${DEP_VERSION}/${DEP_ARCHIVE}" > "${DEP_ARCHIVE}" ) || \ + ( cd "$DLDIR" && wget -c "https://sourceforge.net/projects/${DEP_PRJNAME}/files/${DEP_PRJNAME}/${DEP_VERSION}/${DEP_ARCHIVE}" -O "${DEP_ARCHIVE}" ) + + cd "${WSDIR}" + rm -rf ${DEP_DIRNAME} || echo "" + + tar xzf "$DLDIR/${DEP_ARCHIVE}" || exit + cd "./${DEP_DIRNAME}" + + yes "" | ./configure --prefix="$PREFIX" --with-default-snmp-version=3 \ + --disable-agent --disable-daemon --with-sys-contact="" --with-sys-location="" \ + --with-logfile=none --with-persistent-directory="${PREFIX}/var/net-snmp" \ + --disable-embedded-perl --without-perl-modules --disable-perl-cc-checks \ + --enable-shared || exit + + $MAKE LDFLAGS="-no-undefined -lws2_32 -lregex -Xlinker --ignore-unresolved-symbol=_app_name_long -Xlinker --ignore-unresolved-symbol=app_name_long" || exit + + # Beside leaving a pre-install location for future runs, + # this may build some more artifacts: + rm -rf "`pwd`/.inst" || echo "" + $MAKE DESTDIR="`pwd`/.inst" install || exit + + # Summarize what we have got + find ./ -type f -name "*.dll" -o -name "*.dll.a"; + + $SUDO $MAKE install +) + +prepareEnv || exit + +# TODO: Loop, params, help, etc... +# For now, let it pass "-f" to the builder +provide_netsnmp "$@" diff --git a/scripts/Windows/dllldd.sh b/scripts/Windows/dllldd.sh new file mode 100755 index 0000000000..2d09f91c61 --- /dev/null +++ b/scripts/Windows/dllldd.sh @@ -0,0 +1,217 @@ +#!/bin/sh + +# This is a helper script to find Windows DLL library files used by another +# Portable Executable (DLL or EXE) file, restricted to paths in the MinGW +# environment. It can do so recursively, to facilitate installation of NUT +# for Windows, bundled with open-source dependencies. +# +# Copyright (C) +# 2022 Jim Klimov + +REGEX_WS="`printf '[\t ]'`" +REGEX_NOT_WS="`printf '[^\t ]'`" +dllldd() ( + # Traverse an EXE or DLL file for DLLs it needs directly, + # which are provided in the cross-build env (not system ones). + # Assume no whitespaces in paths and filenames of interest. + + # grep for standard-language strings where needed: + LANG=C + LC_ALL=C + export LANG LC_ALL + + # Otherwise try objdump, if ARCH is known (linux+mingw builds) or not (MSYS2 builds) + SEEN=0 + if [ -n "${ARCH-}${MINGW_PREFIX-}${MSYSTEM_PREFIX-}" ] ; then + for OD in objdump "$ARCH-objdump" ; do + (command -v "$OD" >/dev/null 2>/dev/null) || continue + + ODOUT="`$OD -x "$@" 2>/dev/null | grep -Ei "DLL Name:" | awk '{print $NF}' | sort | uniq | grep -vEi '^(|/.*/)(msvcrt|userenv|bcrypt|rpcrt4|usp10|(advapi|kernel|user|wsock|ws2_|gdi|ole||shell)(32|64))\.dll$'`" \ + && [ -n "$ODOUT" ] || continue + + for F in $ODOUT ; do + if [ -n "$DESTDIR" -a -d "${DESTDIR}" ] ; then + OUT="`find "$DESTDIR" -type f -name "$F" \! -size 0 2>/dev/null | head -1`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } + fi + if [ -n "$ARCH" -a -d "/usr/${ARCH}" ] ; then + OUT="`ls -1 "/usr/${ARCH}/bin/$F" "/usr/${ARCH}/lib/$F" 2>/dev/null || true`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } + fi + if [ -n "$MSYSTEM_PREFIX" -a -d "$MSYSTEM_PREFIX" ] ; then + OUT="`ls -1 "${MSYSTEM_PREFIX}/bin/$F" "${MSYSTEM_PREFIX}/lib/$F" 2>/dev/null || true`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } + fi + if [ -n "$MINGW_PREFIX" -a "$MINGW_PREFIX" != "$MSYSTEM_PREFIX" -a -d "$MINGW_PREFIX" ] ; then + OUT="`ls -1 "${MINGW_PREFIX}/bin/$F" "${MINGW_PREFIX}/lib/$F" 2>/dev/null || true`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue ; } + fi + + # Look for compiler-provided libraries, e.g. in cross-builds on linux+mingw + # we have a selection of such C++ required artifacts as: + # /usr/lib/gcc/x86_64-w64-mingw32/9.3-win32/libgcc_s_seh-1.dll + # /usr/lib/gcc/x86_64-w64-mingw32/9.3-win32/libstdc++-6.dll + # /usr/lib/gcc/x86_64-w64-mingw32/9.3-posix/libgcc_s_seh-1.dll + # /usr/lib/gcc/x86_64-w64-mingw32/9.3-posix/libstdc++-6.dll + # /usr/lib/gcc/i686-w64-mingw32/9.3-win32/libstdc++-6.dll + # /usr/lib/gcc/i686-w64-mingw32/9.3-posix/libstdc++-6.dll + # while on MSYS2 there is one in standard path matched above: + # /mingw64/bin/libstdc++-6.dll + # A clumsy alternative would be to link deliverable C++ libs/bins + # statically with "-static-libgcc -static-libstdc++" options. + COMPILER_PATHS="" + if [ -n "$CC" ] ; then + # gcc and clang support this option: + COMPILER_PATHS="`$CC --print-search-dirs | grep libraries: | sed 's,^libraries: *=/,/,'`" + else + # FIXME: Try to look up in config.log first? + if [ -n "$ARCH" ] && (command -v "${ARCH}-gcc") 2>/dev/null >/dev/null ; then + COMPILER_PATHS="`"${ARCH}-gcc" --print-search-dirs | grep libraries: | sed 's,^libraries: *=/,/,'`" + fi + fi + if [ -n "$CXX" ] ; then + # g++ and clang support this option: + COMPILER_PATHS="`$CXX --print-search-dirs | grep libraries: | sed 's,^libraries: *=/,/,'`:${COMPILER_PATHS}" + else + # FIXME: Try to look up in config.log first? + if [ -n "$ARCH" ] && (command -v "${ARCH}-g++") 2>/dev/null >/dev/null ; then + COMPILER_PATHS="`"${ARCH}-g++" --print-search-dirs | grep libraries: | sed 's,^libraries: *=/,/,'`" + fi + fi + if [ -n "$COMPILER_PATHS" ] ; then + COMPILER_PATHS="`echo "$COMPILER_PATHS" | tr ':' '\n'`" + for P in $COMPILER_PATHS ; do + OUT="`ls -1 "${P}/$F" 2>/dev/null || true`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; SEEN="`expr $SEEN + 1`" ; continue 2 ; } + done + fi + + echo "WARNING: '$F' was not found in searched locations (system paths)!" >&2 + done + done + if [ "$SEEN" != 0 ] ; then + return 0 + fi + fi + + # if `ldd` handles Windows PE (e.g. on MSYS2), we are lucky: + # libiconv-2.dll => /mingw64/bin/libiconv-2.dll (0x7ffd26c90000) + # but it tends to say "not a dynamic executable" + # or that file type is not supported + OUT="`ldd "$@" 2>/dev/null | grep -Ei '\.dll' | grep -E '/(bin|lib)/' | sed "s,^${REGEX_WS}*\(${REGEX_NOT_WS}${REGEX_NOT_WS}*\)${REGEX_WS}${REGEX_WS}*=>${REGEX_WS}${REGEX_WS}*\(${REGEX_NOT_WS}${REGEX_NOT_WS}*\)${REGEX_WS}.*\$,\2," | sort | uniq | grep -Ei '\.dll$'`" \ + && [ -n "$OUT" ] && { echo "$OUT" ; return 0 ; } + + return 1 +) + +dlllddrec() ( + # Recurse to find the (mingw-provided) tree of dependencies + dllldd "$1" | while read D ; do + echo "$D" + dlllddrec "$D" + done | sort | uniq +) + +# Alas, can't rely on having BASH, and dash fails to parse its syntax +# even if hidden by conditionals or separate method like this (might +# optionally source it from another file though?) +#diffvars_bash() { +# diff -bu <(echo "$1") <(echo "$2") | grep -E '^\+[^+]' | sed 's,^\+,,' +#} + +dllldddir() ( + # Recurse the current (or specified) directory, find all EXE/DLL here, + # and locate their dependency DLLs, and produce a unique-item list + if [ $# = 0 ]; then + dllldddir . + return + fi + + # Assume no whitespace in built/MSYS/MinGW paths... + ORIGFILES="`find "$@" -type f | grep -Ei '\.(exe|dll)$'`" || return + + # Quick OK, nothing here? + [ -n "$ORIGFILES" ] || return 0 + + # Loop until we see nothing new: + SEENDLLS="`dllldd $ORIGFILES | sort | uniq`" + [ -n "$SEENDLLS" ] || return 0 + + #if [ -z "$BASH_VERSION" ] ; then + TMP1="`mktemp`" + TMP2="`mktemp`" + trap "rm -f '$TMP1' '$TMP2'" 0 1 2 3 15 + #fi + + NEXTDLLS="$SEENDLLS" + while [ -n "$NEXTDLLS" ] ; do + MOREDLLS="`dllldd $NEXTDLLS | sort | uniq`" + + # Next iteration we drill into those we have not seen yet + #if [ -n "$BASH_VERSION" ] ; then + # NEXTDLLS="`diffvars_bash "$SEENDLLS" "$MOREDLLS"`" + #else + echo "$SEENDLLS" > "$TMP1" + echo "$MOREDLLS" > "$TMP2" + NEXTDLLS="`diff -bu "$TMP1" "$TMP2" | grep -E '^\+[^+]' | sed 's,^\+,,'`" + #fi + + if [ -n "$NEXTDLLS" ] ; then + SEENDLLS="`( echo "$SEENDLLS" ; echo "$NEXTDLLS" ) | sort | uniq`" + fi + done + + if [ -z "$BASH_VERSION" ] ; then + rm -f "$TMP1" "$TMP2" + trap - 0 1 2 3 15 + fi + + echo "$SEENDLLS" +) + +dllldddir_pedantic() ( + # For `ldd` or `objdump` versions that do not act on many files + + # Recurse the current (or specified) directory, find all EXE/DLL here, + # and locate their dependency DLLs, and produce a unique-item list + if [ $# = 0 ]; then + dllldddir_pedantic . + return + fi + + # Two passes: one finds direct dependencies of all EXE/DLL under the + # specified location(s); then trims this list to be unique, and then + # the second pass recurses those libraries for their dependencies: + find "$@" -type f | grep -Ei '\.(exe|dll)$' \ + | while read E ; do dllldd "$E" ; done | sort | uniq \ + | while read D ; do echo "$D"; dlllddrec "$D" ; done | sort | uniq +) + +if [ x"${DLLLDD_SOURCED-}" != xtrue ] ; then + # Work like a command-line tool: + case "$1" in + ""|-h|-help|--help) + cat << EOF +Tool to find DLLs needed by an EXE or another DLL + +Directly used libraries: + $0 dllldd ONEFILE.EXE + +Recursively used libraries: + $0 dlllddrec ONEFILE.EXE + +Find all EXE/DLL files under specified (or current) dir, +and list their set of required DLLs + $0 dllldddir [DIRNAME...] + $0 dllldddir_pedantic [DIRNAME...] +EOF + ;; + dlllddrec|dllldd|dllldddir|dllldddir_pedantic) "$@" ;; + *) dlllddrec "$1" ;; + esac + + exit 0 +fi + +# Caller said DLLLDD_SOURCED=true +echo "SOURCED dllldd methods" diff --git a/scripts/Windows/halt.c b/scripts/Windows/halt.c index 11f7899a5b..bbaa56fff8 100644 --- a/scripts/Windows/halt.c +++ b/scripts/Windows/halt.c @@ -21,8 +21,12 @@ DESCRIPTION AUTHOR Ben Collver + Jim Klimov - slight adjustments for NUT builds */ +#include "config.h" /* should be first */ +#include "common.h" + #include #ifndef EWX_FORCEIFHUNG @@ -39,6 +43,10 @@ int WINAPI WinMain( HANDLE my_token; UINT my_flags; + NUT_UNUSED_VARIABLE(hInstance); + NUT_UNUSED_VARIABLE(hPrevInstance); + NUT_UNUSED_VARIABLE(nCmdShow); + my_flags = EWX_SHUTDOWN | EWX_FORCEIFHUNG; if (strstr(lpCmdLine, "q") != NULL) { diff --git a/scripts/Windows/winevent.mc b/scripts/Windows/winevent.mc new file mode 100644 index 0000000000..2bd2545ca0 --- /dev/null +++ b/scripts/Windows/winevent.mc @@ -0,0 +1,28 @@ +MessageIdTypedef=DWORD + +SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS + Informational=0x1:STATUS_SEVERITY_INFORMATIONAL + Warning=0x2:STATUS_SEVERITY_WARNING + Error=0x3:STATUS_SEVERITY_ERROR + ) + + +FacilityNames=(System=0x0:FACILITY_SYSTEM + Runtime=0x2:FACILITY_RUNTIME + Stubs=0x3:FACILITY_STUBS + Io=0x4:FACILITY_IO_ERROR_CODE + ) + +LanguageNames=(English=0x409:MSG00409) + +; // The following are message definitions. + +MessageId=0x1 +Severity=Error +Facility=Runtime +SymbolicName=SVC_EVENT +Language=English +%1. +. + + diff --git a/scripts/Windows/wininit.c b/scripts/Windows/wininit.c new file mode 100644 index 0000000000..f86cd2bf7b --- /dev/null +++ b/scripts/Windows/wininit.c @@ -0,0 +1,649 @@ +/* wininit.c - MS Windows service which replace the init script + + Copyright (C) + 2010 Frederic Bohe + + 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 +*/ + +#ifdef WIN32 + +#include "config.h" /* should be first */ +#include "common.h" +#include "winevent.h" +#include "wincompat.h" + +#define NUT_START TRUE +#define NUT_STOP FALSE + +typedef struct conn_s { + HANDLE handle; + OVERLAPPED overlapped; + char buf[LARGEBUF]; + struct conn_s *prev; + struct conn_s *next; +} conn_t; + +static DWORD upsd_pid = 0; +static DWORD upsmon_pid = 0; +static BOOL service_flag = TRUE; +HANDLE svc_stop = NULL; +static SERVICE_STATUS SvcStatus; +static SERVICE_STATUS_HANDLE SvcStatusHandle; + +static void print_event(DWORD priority, const char * fmt, ...) +{ + HANDLE EventSource; + va_list ap; + CHAR * buf; + int ret; + + buf = xmalloc(LARGEBUF); + + va_start(ap, fmt); + ret = vsnprintf(buf, LARGEBUF, fmt, ap); + va_end(ap); + + if(ret<0) { + return; + } + + if( !service_flag ) { + upslogx(LOG_ERR, "EventLog : %s\n",buf); + } + + EventSource = RegisterEventSource(NULL, SVCNAME); + + if( NULL != EventSource ) { + ReportEvent( EventSource, /* event log handle */ + priority, /* event type */ + 0, /* event category */ + SVC_EVENT, /* event identifier */ + NULL, /* no security identifier*/ + 1, /* size of string array */ + 0, /* no binary data */ + (const char **)&buf, /* array of string */ + NULL); /* no binary data */ + + DeregisterEventSource(EventSource); + + } + + if( buf ) + free(buf); +} + +/* returns PID of the newly created process or 0 on failure */ +static DWORD create_process(char * command) +{ + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + BOOL res; + DWORD LastError; + + memset(&StartupInfo,0,sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(StartupInfo); + memset(&ProcessInformation,0,sizeof(ProcessInformation)); + + res = CreateProcess( + NULL, + command, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + LastError = GetLastError(); + + if( res == 0 ) { + print_event(LOG_ERR, "Can't create process %s : %d", command, LastError); + return 0; + } + + return ProcessInformation.dwProcessId; +} + +/* return PID of created process or 0 on failure */ +static DWORD run_drivers() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe start",path); + free(path); + return create_process(command); +} + +/* return PID of created process or 0 on failure */ +static DWORD stop_drivers() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe stop",path); + free(path); + return create_process(command); +} + +/* return PID of created process or 0 on failure */ +static void run_upsd() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsd.exe",path); + free(path); + upsd_pid = create_process(command); +} + +static void stop_upsd() +{ + if ( sendsignal( UPSD_PIPE_NAME, COMMAND_STOP ) ) { + print_event(LOG_ERR, "Error stopping upsd (%d)",GetLastError()); + } +} + +/* return PID of created process or 0 on failure */ +static void run_upsmon() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsmon.exe",path); + free(path); + upsmon_pid = create_process(command); +} + +static void stop_upsmon() +{ + if ( sendsignal( UPSMON_PIPE_NAME, COMMAND_STOP ) ) { + print_event(LOG_ERR, "Error stopping upsmon (%d)",GetLastError()); + } +} + +/* Return 0 if powerdown flag is set */ +static DWORD test_powerdownflag() +{ + char command[MAX_PATH]; + char *path; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + BOOL res; + DWORD LastError; + DWORD status; + int i = 10; + int timeout = 500; + + path = getfullpath(PATH_SBIN); + snprintf(command,sizeof(command),"%s\\upsmon.exe -K",path); + free(path); + + memset(&StartupInfo,0,sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(StartupInfo); + memset(&ProcessInformation,0,sizeof(ProcessInformation)); + + res = CreateProcess( + NULL, + command, + NULL, + NULL, + FALSE, + CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &StartupInfo, + &ProcessInformation + ); + LastError = GetLastError(); + + if( res == 0 ) { + print_event(LOG_ERR, "Can't create process %s : %d", command, LastError); + return 1; + } + + while( i > 0) { + res = GetExitCodeProcess(ProcessInformation.hProcess, &status); + if( res != 0) { + if( status != STILL_ACTIVE) { + return status; + } + } + Sleep(timeout); + i--; + } + + return 1; +} + +static DWORD shutdown_ups() +{ + char command[MAX_PATH]; + char *path; + + path = getfullpath(PATH_BIN); + snprintf(command,sizeof(command),"%s\\upsdrvctl.exe shutdown",path); + free(path); + return create_process(command); +} + +/* return 0 on failure */ +static int parse_nutconf(BOOL start_flag) +{ + char fn[SMALLBUF]; + FILE *nutf; + char buf[SMALLBUF]; + char fullname[SMALLBUF]; + + snprintf(fn,sizeof(fn),"%s/nut.conf",confpath()); + + nutf = fopen(fn, "r"); + if(nutf == NULL) { + snprintf(buf,sizeof(buf),"Error opening %s",fn); + print_event(LOG_ERR,buf); + return 0; + } + + while( fgets(buf,sizeof(buf),nutf) != NULL ) { + if(buf[0] != '#') { + if( strstr(buf,"standalone") != NULL || + strstr(buf,"netserver") != NULL ) { + if( start_flag == NUT_START ) { + print_event(LOG_INFO,"Starting drivers"); + run_drivers(); + print_event(LOG_INFO,"Starting upsd"); + run_upsd(); + /* Wait a moment for the drivers to start */ + Sleep(5000); + print_event(LOG_INFO,"Starting upsmon"); + run_upsmon(); + return 1; + } + else { + print_event(LOG_INFO,"stop upsd"); + stop_upsd(); + print_event(LOG_INFO,"stop drivers"); + stop_drivers(); + print_event(LOG_INFO,"stop upsmon"); + stop_upsmon(); + /* Give a chance to upsmon to write the POWERDOWNFLAG file */ + Sleep(1000); + if( test_powerdownflag() == 0 ) { + print_event(LOG_INFO,"shutdown ups"); + shutdown_ups(); + } + print_event(LOG_INFO,"End of NUT stop"); + return 1; + } + } + if( strstr(buf,"netclient") != NULL ) { + if( start_flag == NUT_START ) { + run_upsmon(); + return 1; + } + else { + stop_upsmon(); + return 1; + } + } + } + } + + GetFullPathName(fn,sizeof(fullname),fullname,NULL); + snprintf(buf,sizeof(buf),"nut disabled, please adjust the configuration to your needs. Then set MODE to a suitable value in %s to enable it.",fullname); + print_event(LOG_ERR,buf); + return 0; +} + +static int SvcInstall(const char * SvcName, const char * args) +{ + SC_HANDLE SCManager; + SC_HANDLE Service; + TCHAR Path[MAX_PATH]; + + if( !GetModuleFileName( NULL, Path, MAX_PATH ) ) { + printf("Cannot install service (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + if( args != NULL ) { + snprintfcat(Path, sizeof(Path), " %s", args); + } + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServiceActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + Service = CreateService( + SCManager, /* SCM database */ + SvcName, /* name of service */ + SvcName, /* service name to display */ + SERVICE_ALL_ACCESS, /* desired access */ + SERVICE_WIN32_OWN_PROCESS, /* service type */ + SERVICE_AUTO_START, /* start type */ + SERVICE_ERROR_NORMAL, /* error control type */ + Path, /* path to service binary */ + NULL, /* no load ordering group */ + NULL, /* no tag identifier */ + NULL, /* no dependencies */ + NULL, /* LocalSystem account */ + NULL); /* no password */ + + if (Service == NULL) { + upslogx(LOG_ERR, "CreateService failed (%d)\n", (int)GetLastError()); + CloseServiceHandle(SCManager); + return EXIT_FAILURE; + } + else { + upslogx(LOG_INFO, "Service installed successfully\n"); + } + + CloseServiceHandle(Service); + CloseServiceHandle(SCManager); + + return EXIT_SUCCESS; +} + +static int SvcUninstall(const char * SvcName) +{ + SC_HANDLE SCManager; + SC_HANDLE Service; + + SCManager = OpenSCManager( + NULL, /* local computer */ + NULL, /* ServicesActive database */ + SC_MANAGER_ALL_ACCESS); /* full access rights */ + + if (NULL == SCManager) { + upslogx(LOG_ERR, "OpenSCManager failed (%d)\n", (int)GetLastError()); + return EXIT_FAILURE; + } + + Service = OpenService( + SCManager, /* SCM database */ + SvcName, /* name of service */ + DELETE); /* need delete access */ + + if (Service == NULL) { + upslogx(LOG_ERR, "OpenService failed (%d)\n", (int)GetLastError()); + CloseServiceHandle(SCManager); + return EXIT_FAILURE; + } + + if (! DeleteService(Service) ) { + upslogx(LOG_ERR,"DeleteService failed (%d)\n", (int)GetLastError()); + } + else { + upslogx(LOG_ERR,"Service deleted successfully\n"); + } + + CloseServiceHandle(Service); + CloseServiceHandle(SCManager); + + return EXIT_SUCCESS; +} + +static void ReportSvcStatus( DWORD CurrentState, + DWORD Win32ExitCode, + DWORD WaitHint) +{ + static DWORD CheckPoint = 1; + + SvcStatus.dwCurrentState = CurrentState; + SvcStatus.dwWin32ExitCode = Win32ExitCode; + SvcStatus.dwWaitHint = WaitHint; + + if (CurrentState == SERVICE_START_PENDING) + SvcStatus.dwControlsAccepted = 0; + else SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + + if ( (CurrentState == SERVICE_RUNNING) || + (CurrentState == SERVICE_STOPPED) ) { + SvcStatus.dwCheckPoint = 0; + } + else { + SvcStatus.dwCheckPoint = CheckPoint++; + } + + /* report the status of the service to the SCM */ + SetServiceStatus( SvcStatusHandle, &SvcStatus ); +} + +static void WINAPI SvcCtrlHandler( DWORD Ctrl ) +{ + switch(Ctrl) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + /* Signal the service to stop */ + SetEvent(svc_stop); + ReportSvcStatus(SvcStatus.dwCurrentState, NO_ERROR, 0); + + return; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } +} + +static void SvcStart(char * SvcName) +{ + /* Register the handler function for the service */ + SvcStatusHandle = RegisterServiceCtrlHandler( + SvcName, + SvcCtrlHandler); + + if( !SvcStatusHandle ) { + upslogx(LOG_ERR, "RegisterServiceCtrlHandler\n"); + return; + } + + SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SvcStatus.dwServiceSpecificExitCode = 0; + + /* Report initial status to the SCM */ + ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 ); +} + +static void SvcReady(void) +{ + svc_stop = CreateEvent( + NULL, /* default security attributes */ + TRUE, /* manual reset event */ + FALSE, /* not signaled */ + NULL); /* no name */ + + if( svc_stop == NULL ) { + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + return; + } + ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0); +} + +static void close_all(void) +{ + pipe_conn_t *conn; + + for (conn = pipe_connhead; conn; conn = conn->next) { + pipe_disconnect(conn); + } + +} + +static void WINAPI SvcMain( DWORD argc, LPTSTR *argv ) +{ + DWORD ret; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + int maxhandle = 0; + pipe_conn_t *conn; + DWORD priority; + char * buf; + + NUT_UNUSED_VARIABLE(argc); + NUT_UNUSED_VARIABLE(argv); + + if(service_flag) { + SvcStart(SVCNAME); + } + + /* A service has no console, so do has its children. */ + /* So if we want to be able to send CTRL+BREAK signal we must */ + /* create a console which will be inherited by children */ + AllocConsole(); + + print_event(LOG_INFO,"Starting"); + + /* pipe for event log proxy */ + pipe_create(EVENTLOG_PIPE_NAME); + + /* parse nut.conf and start relevant processes */ + if ( parse_nutconf(NUT_START) == 0 ) { + print_event(LOG_INFO, "exiting"); + if( service_flag ) { + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + } + return; + } + + if(service_flag) { + SvcReady(); + } + + while (1) { + maxhandle = 0; + memset(&handles,0,sizeof(handles)); + + /* Wait on the read IO of each connections */ + for (conn = pipe_connhead; conn; conn = conn->next) { + handles[maxhandle] = conn->overlapped.hEvent; + maxhandle++; + } + /* Add the new pipe connected event */ + handles[maxhandle] = pipe_connection_overlapped.hEvent; + maxhandle++; + + /* Add SCM event handler in service mode*/ + if(service_flag) { + handles[maxhandle] = svc_stop; + maxhandle++; + } + + ret = WaitForMultipleObjects(maxhandle,handles,FALSE,INFINITE); + + if (ret == WAIT_FAILED) { + print_event(LOG_ERR, "Wait failed"); + return; + } + + if( handles[ret] == svc_stop && service_flag ) { + parse_nutconf(NUT_STOP); + if(service_flag) { + print_event(LOG_INFO, "Exiting"); + close_all(); + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0); + } + return; + } + + /* Retrieve the signaled connection */ + for(conn = pipe_connhead; conn != NULL; conn = conn->next) { + if( conn->overlapped.hEvent == handles[ret-WAIT_OBJECT_0]) { + break; + } + } + /* a new pipe connection has been signaled */ + if (handles[ret] == pipe_connection_overlapped.hEvent) { + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + if( conn != NULL) { + if( pipe_ready(conn) ) { + buf = conn->buf; + /* a frame is a DWORD indicating priority followed by an array of char (not necessarily followed by a terminal 0 */ + priority =*((DWORD *)buf); + buf = buf + sizeof(DWORD); + print_event(priority,buf); + + pipe_disconnect(conn); + } + } + } + } +} + +int main(int argc, char **argv) +{ + int i; + while ((i = getopt(argc, argv, "+IUN")) != -1) { + switch (i) { + case 'I': + return SvcInstall(SVCNAME,NULL); + case 'U': + return SvcUninstall(SVCNAME); + case 'N': + service_flag = FALSE; + upslogx(LOG_ERR, "Running in non-service mode\n"); + break; + default: + break; + } + } + + optind = 0; + + SERVICE_TABLE_ENTRY DispatchTable[] = + { + { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, + { NULL, NULL } + }; + /* This call returns when the service has stopped */ + if(service_flag ) { + if (!StartServiceCtrlDispatcher( DispatchTable )) + { + print_event(LOG_ERR, "StartServiceCtrlDispatcher failed : exiting, this is a Windows service which can't be run as a regular application by default. Try -N to start it as a regular application"); + } + } + else { + SvcMain(argc,argv); + } + + return EXIT_SUCCESS; +} + +#else + +/* Just avoid: ISO C forbids an empty translation unit [-Werror=pedantic] */ +int main (int argc, char ** argv); + +#endif /* WIN32 */ diff --git a/scripts/augeas/.gitignore b/scripts/augeas/.gitignore index 44960455a2..0a87d83815 100644 --- a/scripts/augeas/.gitignore +++ b/scripts/augeas/.gitignore @@ -2,3 +2,4 @@ /nutupsconf.aug.in /nutupsconf.aug.in.AUTOGEN_WITHOUT /gen-nutupsconf-aug.py +/README diff --git a/scripts/augeas/Makefile.am b/scripts/augeas/Makefile.am index 5aca61bbd9..62917d2db1 100644 --- a/scripts/augeas/Makefile.am +++ b/scripts/augeas/Makefile.am @@ -1,6 +1,11 @@ +# Network UPS Tools: scripts/augeas EXTRA_DIST = gen-nutupsconf-aug.py.in nutupsconf.aug.tpl \ - README tests/test_nut.aug + README.adoc tests/test_nut.aug + +# Note: spellchecking is currently ensured by docs/Makefile.am +# due to inclusion into docs/developer-guide.txt, and the heading +# level may be also dictated by that. PYTHON = @PYTHON@ @@ -9,7 +14,7 @@ PYTHON = @PYTHON@ # is assumed to only differ from a generated gen-nutupsconf-aug.py by the # @PYTHON@ shebang. dist-hook: - @if [ -n "$(PYTHON)" ] && $(PYTHON) -c "import re,glob,codecs"; then \ + @if [ -n "$(PYTHON)" ] && [ x"no" != x"$(PYTHON)" ] && $(PYTHON) -c "import re,glob,codecs"; then \ echo "Regenerating Augeas ups.conf lens with '$(PYTHON)'."; \ $(PYTHON) $(distdir)/gen-nutupsconf-aug.py.in $(distdir)/; \ else \ @@ -41,12 +46,15 @@ if WITH_AUGLENS endif MAINTAINERCLEANFILES = Makefile.in .dirstamp -CLEANFILES = *-spellchecked +CLEANFILES = *-spellchecked README # Can be re-generated by configure script and/or make: DISTCLEANFILES = gen-nutupsconf-aug.py # Generated by autogen.sh and needed to run the configure script: -MAINTAINERCLEANFILES += nutupsconf.aug.in +MAINTAINERCLEANFILES += nutupsconf.aug.in nutupsconf.aug.in.AUTOGEN_WITHOUT + +# Part of dist tarball, regardless of use for current build: +EXTRA_DIST += nutupsconf.aug.in DISTCLEANFILES += *.aug diff --git a/scripts/augeas/README b/scripts/augeas/README.adoc similarity index 81% rename from scripts/augeas/README rename to scripts/augeas/README.adoc index bfa03d3fa3..d70eada59f 100644 --- a/scripts/augeas/README +++ b/scripts/augeas/README.adoc @@ -12,11 +12,14 @@ In order to address this point, NUT now provides configuration tools and manipulation abstraction, to anybody who want to manipulate NUT configuration, through Augeas lenses and modules. -From link:http://augeas.net[Augeas homepage]: - -"Augeas is a configuration editing tool. It parses configuration files in their -native formats and transforms them into a tree. Configuration changes are made -by manipulating this tree and saving it back into native config files." +.From link:http://augeas.net[Augeas homepage]: +[quote] +____ +_Augeas is a configuration editing tool. It parses configuration files +in their native formats and transforms them into a tree. Configuration +changes are made by manipulating this tree and saving it back into +native config files._ +____ In other words, Augeas is the dreamed Registry, with all the advantages (such as a uniform interface and tools), and the added bonus of being @@ -39,11 +42,11 @@ are welcome). As an example, on Debian and derivatives, do the following: - $ apt-get install augeas-lenses augeas-tools + :; apt-get install augeas-lenses augeas-tools And optionally: - $ apt-get install libaugeas0 libaugeas-dev python-augeas + :; apt-get install libaugeas0 libaugeas-dev python-augeas On RedHat and derivatives, you have to install the packages 'augeas' and 'augeas-libs'. @@ -53,23 +56,23 @@ On RedHat and derivatives, you have to install the packages 'augeas' and NUT lenses and modules for Augeas ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -These are the *.aug files in the present directory. +These are the `*.aug` files in the present directory. You can either install the files to the right location on your system, -generally in '/usr/share/augeas/lenses/', or use these from NUT -source directory ('nut/scripts/augeas'). The latter is to be preferred for +generally in `/usr/share/augeas/lenses/`, or use these from NUT +source directory (`nut/scripts/augeas`). The latter is to be preferred for the time being. Create a test sandbox --------------------- -NOTE: for now, it's easier to include an existing /etc/nut/ directory. +NOTE: For now, it is easier to include an existing `/etc/nut/` directory. - $ export AUGEAS_ROOT=./augeas-sandbox - $ mkdir $AUGEAS_ROOT - $ sudo cp -pr /etc/nut $AUGEAS_ROOT - $ sudo chown -R $(id -nu):$(id -ng) $AUGEAS_ROOT + :; export AUGEAS_ROOT=./augeas-sandbox + :; mkdir $AUGEAS_ROOT + :; sudo cp -pr /etc/nut $AUGEAS_ROOT + :; sudo chown -R $(id -nu):$(id -ng) $AUGEAS_ROOT Start testing and using @@ -89,13 +92,13 @@ Shell Start an augeas shell using: - $ augtool -b + :; augtool -b -NOTE: if you have not installed NUT lenses, add '-I/path/to/nut/scripts/augeas'. +NOTE: If you have not installed NUT lenses, add `-I/path/to/nut/scripts/augeas`. From there, you can perform different actions like: -- list existing nut related files: +- list existing NUT-related files: augtool> ls /files/etc/nut/ nut.conf/ = (none) @@ -104,7 +107,7 @@ From there, you can perform different actions like: ups.conf/ = (none) upsd.conf/ = (none + -or using: +or using the matcher: + augtool> match /files/etc/nut/* /files/etc/nut/nut.conf = (none) @@ -112,21 +115,26 @@ or using: /files/etc/nut/upsmon.conf = (none) /files/etc/nut/ups.conf = (none) /files/etc/nut/upsd.conf = (none) - -NOTE: if you don't see anything, you may search for error messages by using: + +[NOTE] +====== +If you don't see anything, you may search for error messages by using: + augtool> ls /augeas/files/etc/nut/*/errors + and + augtool> get /augeas/files/etc/nut/ups.conf/error/message /augeas/files/etc/nut/ups.conf/error/message = Permission denied +====== -- create a new device entry (in ups.conf), called 'augtest': +- create a new device entry (in `ups.conf`), called `augtest`: augtool> set /files/etc/nut/ups.conf/augtest/driver dummy-ups augtool> set /files/etc/nut/ups.conf/augtest/port auto augtool> save -- list the devices using the 'usbhid-ups' driver: +- list the devices currently using the `usbhid-ups` driver: augtool> match /files/etc/nut/ups.conf/*/driver dummy-ups @@ -134,15 +142,15 @@ and C ~ -A library is available for C programs, along with pkg-config support. +A library is available for C programs, along with `pkg-config` support. You can get the compilation and link flags using the following code -in your configure script or Makefile: +in your program's `configure` script or `Makefile`: CFLAGS="`pkg-config --silence-errors --cflags augeas`" LDFLAGS="`pkg-config --silence-errors --libs augeas`" -Here is an code sample using this library for NUT configuration: +Here is a code sample using this library for NUT configuration: -------------------------------------------------------------------------------- augeas *a = aug_init(NULL, NULL, AUG_NONE); @@ -155,7 +163,7 @@ ret = aug_save(a); Python ~~~~~~ -The `augeas` class abstracts access to the configuration files. +The `augeas` class abstracts access to the configuration files. $ python Python 2.5.1 (r251:54863, Apr 8 2008, 01:19:33) @@ -201,7 +209,7 @@ Test the conformity testing module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Existing configuration files can be tested for conformity. To do so, use: - + $ augparse -I ./ ./test_nut.aug @@ -218,7 +226,7 @@ driver_name="usbhid-ups" port_name="auto" a = augeas.augeas() - + # Generate nut.conf a.set("/files/etc/nut/nut.conf/MODE", "standalone") diff --git a/scripts/augeas/gen-nutupsconf-aug.py.in b/scripts/augeas/gen-nutupsconf-aug.py.in index 04b6fa1b2a..6132ccb37b 100755 --- a/scripts/augeas/gen-nutupsconf-aug.py.in +++ b/scripts/augeas/gen-nutupsconf-aug.py.in @@ -84,6 +84,9 @@ if __name__ == '__main__': # 1.4/ Extract variable names for line in matchResults: + if ("Could not addvar" in line): + # Debug trace e.g. in snmp-ups.c + continue row = line.split(',') if len(row) >= 2: # Absence of quotes indicate that we have a #define diff --git a/scripts/augeas/nutupsdconf.aug.in b/scripts/augeas/nutupsdconf.aug.in index 35bf896f09..09924cfe94 100644 --- a/scripts/augeas/nutupsdconf.aug.in +++ b/scripts/augeas/nutupsdconf.aug.in @@ -41,6 +41,7 @@ let path = word let upsd_maxage = [ opt_spc . key "MAXAGE" . sep_spc . store num . eol ] let upsd_trackingdelay = [ opt_spc . key "TRACKINGDELAY" . sep_spc . store num . eol ] let upsd_allow_no_device = [ opt_spc . key "ALLOW_NO_DEVICE" . sep_spc . store num . eol ] +let upsd_allow_not_all_listeners = [ opt_spc . key "ALLOW_NOT_ALL_LISTENERS" . sep_spc . store num . eol ] let upsd_statepath = [ opt_spc . key "STATEPATH" . sep_spc . store path . eol ] let upsd_listen = [ opt_spc . key "LISTEN" . sep_spc . [ label "interface" . store ip ] @@ -53,13 +54,20 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] * MAXAGE seconds * TRACKINGDELAY seconds * ALLOW_NO_DEVICE Boolean + * ALLOW_NOT_ALL_LISTENERS Boolean * STATEPATH path * LISTEN interface port - * Multiple LISTEN addresses may be specified. The default is to bind to 0.0.0.0 if no LISTEN addresses are specified. - * LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 + * Multiple lines each with one LISTEN address (or host name) and an optional + * port may be specified. The default is to bind to IPv4 and IPv6 "localhost" + * addresses (subject to CLI options `-4` or `-6` constraining IP version, + * or system configuration or support), if no LISTEN addresses are specified. + * LISTEN 127.0.0.1 + * LISTEN 192.168.50.1 + * LISTEN ::1 + * LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 * *************************************************************************) -let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_allow_no_device | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile +let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_allow_no_device | upsd_allow_not_all_listeners | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile let upsd_lns = (upsd_other|comment|empty)* diff --git a/scripts/augeas/nutupsmonconf.aug.in b/scripts/augeas/nutupsmonconf.aug.in index e3941764a2..712d81e520 100644 --- a/scripts/augeas/nutupsmonconf.aug.in +++ b/scripts/augeas/nutupsmonconf.aug.in @@ -91,6 +91,11 @@ let upsmon_notify_type = "ONLINE" | "REPLBATT" | "NOCOMM" | "NOPARENT" + | "CAL" + | "OFF" + | "NOTOFF" + | "BYPASS" + | "NOTBYPASS" let upsmon_notify = [ del_spc . key "NOTIFYMSG" . sep_spc . [ label "type" . store upsmon_notify_type . sep_spc ] diff --git a/scripts/devd/.gitignore b/scripts/devd/.gitignore index 4c6801b08f..acf3d44dd3 100644 --- a/scripts/devd/.gitignore +++ b/scripts/devd/.gitignore @@ -1 +1,2 @@ nut-usb.conf* +nut-usb.quirks diff --git a/scripts/devd/Makefile.am b/scripts/devd/Makefile.am index c1c8097411..b9d9401ad3 100644 --- a/scripts/devd/Makefile.am +++ b/scripts/devd/Makefile.am @@ -1,3 +1,4 @@ +# Network UPS Tools: scripts/devd if WITH_DEVD devdconfdir = $(devddir) @@ -7,7 +8,11 @@ if WITH_USB endif endif -EXTRA_DIST = README +if WITH_FREEBSD_QUIRKS_DIR + freebsdquirks_DATA = nut-usb.quirks +endif + +EXTRA_DIST = README.adoc MAINTAINERCLEANFILES = Makefile.in .dirstamp @@ -15,12 +20,37 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp DISTCLEANFILES = nut-usb.conf # we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = nut-usbups.rules.in - +# or stronger... # Generated by autogen.sh and needed to run the configure script # (technically, generated by tools/nut-usbinfo.pl script among # GENERATED_USB_OS_FILES): -MAINTAINERCLEANFILES += nut-usbups.rules.in -MAINTAINERCLEANFILES += nut-usbups.rules.in.AUTOGEN_WITHOUT MAINTAINERCLEANFILES += nut-usb.conf.in MAINTAINERCLEANFILES += nut-usb.conf.in.AUTOGEN_WITHOUT +MAINTAINERCLEANFILES += nut-usb.quirks + +# Part of dist tarball, regardless of use for current build: +EXTRA_DIST += nut-usb.conf.in nut-usb.quirks + +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked diff --git a/scripts/devd/README b/scripts/devd/README deleted file mode 100644 index 58f9fda918..0000000000 --- a/scripts/devd/README +++ /dev/null @@ -1,11 +0,0 @@ -On FreeBSD, devd has a similar role to udev on Linux. The devd.conf file -defines actions to perform when devices are plugged in. - -The tools/nut-usbinfo.pl script generates nut-usb.conf.in by processing USB -macros in all of the drivers. In this case, the defined action for each -matching UPS is to change the permissions such that the NUT drivers can access -the devices without requiring root privileges. You may need to restart devd and -re-plug in the UPS to trigger the actions. - -The format of this configuration file should work with devd on FreeBSD 9.0 and -9.1, at the very least. diff --git a/scripts/devd/README.adoc b/scripts/devd/README.adoc new file mode 100644 index 0000000000..baefb3a9fa --- /dev/null +++ b/scripts/devd/README.adoc @@ -0,0 +1,20 @@ +NUT USB integration resources for FreeBSD devd +============================================== + +On FreeBSD, the `devd` subsystem has a similar role to `udev` on Linux. + +NOTE: Some FreeBSD based systems rely on "quirks" instead. + +The `devd.conf` file defines actions to perform when devices are plugged in. + +The `tools/nut-usbinfo.pl` script (under NUT source tree root) generates +the `nut-usb.conf.in` here by processing USB macros in all of the drivers. +In this case, the defined action for each matching UPS is to change the +permissions such that the NUT drivers can access the USB device nodes +without requiring root privileges. + +You may need to restart `devd` and re-plug in the UPS (or reboot) after +installation of the file in order to trigger the actions. + +The format of this configuration file should work with `devd` on FreeBSD 9.0 +and 9.1, at the very least. diff --git a/scripts/hotplug/Makefile.am b/scripts/hotplug/Makefile.am index 06a6fe3116..5fa7809f86 100644 --- a/scripts/hotplug/Makefile.am +++ b/scripts/hotplug/Makefile.am @@ -1,21 +1,48 @@ +# Network UPS Tools: scripts/hotplug + +EXTRA_DIST = README.adoc if WITH_HOTPLUG hotplugusbdir = $(hotplugdir)/usb dist_hotplugusb_DATA = libhid.usermap hotplugusb_SCRIPTS = libhidups +else + # Part of dist tarball, regardless of use for current build: + EXTRA_DIST += libhid.usermap endif -EXTRA_DIST = README - MAINTAINERCLEANFILES = Makefile.in .dirstamp # Generated by configure script: DISTCLEANFILES = libhidups -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = libhid.usermap - +# We should never remove this one, apart from a distclean-check +# or stronger... # Generated by autogen.sh and needed to run the configure script # (technically, generated by tools/nut-usbinfo.pl script among # GENERATED_USB_OS_FILES): MAINTAINERCLEANFILES += libhid.usermap + +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked diff --git a/scripts/hotplug/README b/scripts/hotplug/README deleted file mode 100644 index 75c7d36717..0000000000 --- a/scripts/hotplug/README +++ /dev/null @@ -1,37 +0,0 @@ -Desc: Hotplug script for NUT USB drivers -File: scripts/hotplug/README -Date: 8 January 2007 -Auth: Arnaud Quette - -This document introduces Linux Hotplug script for NUT USB -drivers (usbhid-ups, bcmxcp_usb and tripplite_usb). - -These are needed, on older Linux systems, to ensure the right -privileges are set on the usb files (ie allowing nut user to read AND -write to the UPS device). - -Alternative ------------ - -For newer 2.6 kernels with the udev mechanism, you should use the -scripts in scripts/udev instead of this one. - -Installation ------------- - -For most users, these files will be automatically installed in -/etc/hotplug upon "make install", if that directory exists. You can -specify an alternate directory by ./configure --with-hotplug-dir=DIR. - -Manual installation -------------------- -These scripts can be used with Linux 2.4 to 2.6.13. - -- possibly change libhidups to match NUT user -- copy libhidups and libhid.usermap to /etc/hotplug/usb/ -- make libhidups executable with: - chmod a+x /etc/hotplug/usb/libhidups -- call update-usb.usermap or equivalent if needed - -You can then plug your UPS, and start NUT. - diff --git a/scripts/hotplug/README.adoc b/scripts/hotplug/README.adoc new file mode 100644 index 0000000000..a53b6dac46 --- /dev/null +++ b/scripts/hotplug/README.adoc @@ -0,0 +1,46 @@ +Hotplug script for NUT USB drivers +================================== +Arnaud Quette +v1.0, 8 January 2007 (start date) + +This document introduces Linux Hotplug script for NUT USB +drivers (`usbhid-ups`, `bcmxcp_usb` and `tripplite_usb`). + +These are needed, on older Linux systems, to ensure the right +privileges are set on the USB device node files (i.e. allowing +`nut` user to read AND write to the UPS device). + + +Alternative +----------- + +For newer 2.6 kernels with the `udev` mechanism, you should use +the scripts in `scripts/udev` instead of this one. + + +Installation +------------ + +For most users, these files will be automatically installed in +`/etc/hotplug` upon `make install`, if that directory exists. + +You can specify an alternate directory by running: +---- +:; ./configure --with-hotplug-dir=DIR +---- + + +Manual installation +------------------- + +These scripts can be used with Linux 2.4 to 2.6.13. + +- possibly change `libhidups` to match NUT user +- copy `libhidups` and `libhid.usermap` to `/etc/hotplug/usb/` +- make `libhidups` executable with: +---- +:; chmod a+x /etc/hotplug/usb/libhidups +---- +- call `update-usb.usermap` or equivalent if needed + +You can then plug your UPS, and start NUT. diff --git a/scripts/installer/Makefile.am b/scripts/installer/Makefile.am new file mode 100644 index 0000000000..10a67121cb --- /dev/null +++ b/scripts/installer/Makefile.am @@ -0,0 +1,57 @@ +# Network UPS Tools: scripts/installer + +EXTRA_DIST = \ + README.adoc \ + make_package.sh \ + version.sh \ + install.sh \ + uninstall-lsnw.sh \ + uninstall-ipp \ + nutconf-dummy \ + common_EN/license.txt \ + common_EN/install.res \ + common/solaris_init \ + common/ipp-shutdown-daemon.sh \ + common/README_ipp-os-shutdown.adoc \ + common/ipp-status \ + common/ipp.conf \ + common/ipp-wrapper \ + common/ipp-event.sh \ + common/string.sh \ + common/ipp-host-shutdown.sample \ + common/shutdown \ + common/ipp-notifier.sh \ + common/ipp-os-shutdown \ + common/init \ + common/aix_init \ + aix/ipp-os-shutdown.conf.sample \ + aix/aix_init \ + hpux/ipp-os-shutdown.conf.sample \ + solcmn/solaris_init \ + solcmn/ipp-os-shutdown.conf.sample + +SPELLCHECK_SRC = README.adoc common/README_ipp-os-shutdown.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked + +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/installer/README.txt b/scripts/installer/README.adoc similarity index 68% rename from scripts/installer/README.txt rename to scripts/installer/README.adoc index d584c01bc8..7c94a8be24 100644 --- a/scripts/installer/README.txt +++ b/scripts/installer/README.adoc @@ -1,22 +1,29 @@ +NUT Installer (command-line) +============================ + This directory contains scripts and data used for NUT packaging marketed earlier as Eaton IPSS Unix (or IPP for Unix, or UPP), a freely available download. Most of the work was done on behalf of Eaton by Frederic Bohe, Vaclav Krpec, Arnaud Quette and Jim Klimov. This includes the package (tarball) creation script which relies on -presence of third-party library binaries in a $ARCH/libs directory, +presence of third-party library binaries in a `$ARCH/libs` directory, and init-scripts from NUT source tree (originally expected as a "nut" subdirectory), as well as an interactive installer script to set up the package on a target deployment covering package (re-)installation, initial device discovery, password setup, etc., and helper scripts for status overview and shutdown handling. -The installer relied on "nutconf" tool (emulating dummy script for -tests provided here), which was added to NUT sources. +The installer relies on "nutconf" tool (emulating dummy script for +tests provided here), which is part of NUT sources. -Note that heavy use of LD_LIBRARY_PATH in these scripts may become +Note that heavy use of `LD_LIBRARY_PATH` in these scripts may become counterproductive when the package is installed on a system that is too many versions away from the intended target (e.g. mixing up the symbols during dynamic linking), so while this contribution here is published "as is", more work would be needed to make it useful in modern environments. Helper scripts should be quickly useful though. + +Developed (pre-?)2013-2018 by Eaton; contributed to NUT 2022 by Eaton + +Maintained since 2024 by Jim Klimov diff --git a/scripts/installer/common/README_ipp-os-shutdown.txt b/scripts/installer/common/README_ipp-os-shutdown.adoc similarity index 96% rename from scripts/installer/common/README_ipp-os-shutdown.txt rename to scripts/installer/common/README_ipp-os-shutdown.adoc index 5bec5a2431..c565cf9d75 100644 --- a/scripts/installer/common/README_ipp-os-shutdown.txt +++ b/scripts/installer/common/README_ipp-os-shutdown.adoc @@ -53,6 +53,7 @@ which can invoke a customized copy of `ipp-host-shutdown.sample` script for host-specific procedures (such as clusterware shutdown). The `ipp-os-shutdown` script has several roles: + * it manages the delayed shutdown (which can be canceled before the timer expires, and can not be aborted after the timer expires - so it proceeds to the end) @@ -82,13 +83,13 @@ It is recommended to configure `SHUTDOWN_TIMER=-1` (default) on those more important hosts which should stay up as long as possible and only shut down if all required UPSes have posted a low-battery status or forced-shutdown command. These hosts would by default schedule delayed -UPS powercycling. To be on the safe side, sufficient `shutdown_duration` +UPS power-cycling. To be on the safe side, sufficient `shutdown_duration` seconds should be configured in their `netxml-ups` driver blocks in the `/usr/local/ups/etc/ups.conf` file on the host. If the customer elects to use the early shutdown strategy for all hosts, the `POWERDOWNFLAG_USER=enforce` should be configured in the hosts with -the highest `SHUTDOWN_TIMER` value so they would cause UPS powercycling. +the highest `SHUTDOWN_TIMER` value so they would cause UPS power-cycling. Do not forget to define sufficient `DELAY` value for the OS shutdown to complete before the UPS turns itself off. The UPS would turn on the load automatically after some time, when external power is back and it has @@ -99,11 +100,11 @@ web-interface for the UPS). * Install the package as usual -- Uncompress the `ipp-*.tar.gz` archive for your OS +- Un-compress the `ipp-*.tar.gz` archive for your OS - Change into the resulting `ipp-*` subdirectory -- (optional) If a complete reinstallation is desired (including removal +- (optional) If a complete re-installation is desired (including removal of old configuration files so they do not conflict with the new delivery), please also execute `IPP_WIPE_OLD_CONFIG=yes; export IPP_WIPE_OLD_CONFIG` in the shell before running the installation script @@ -151,7 +152,7 @@ either `$SDFLAG_HALT` (halt) or `$SDFLAG_REBOOT` (reboot). If the customer elects to use the early shutdown strategy for all hosts, the `POWERDOWNFLAG_USER=enforce` should be configured in the `ipp.conf` file on hosts with the highest `SHUTDOWN_TIMER` value so they would cause -UPS powercycling explicitly. By default, it may be enabled or forbidden +UPS power-cycling explicitly. By default, it may be enabled or forbidden depending on the cause of shutdown. Make sure to define a sufficient `DELAY` value (in seconds) as well, for @@ -183,7 +184,7 @@ ONBATT) is defined manually in the `ipp.conf` file and shutdown routine has started (and reported to be irreversible) - to report that the shutdown is in irreversible stage, as reaction -to CTRL+C during console-run invokations like `ipp-os-shutdown -t now` +to CTRL+C during console-run invocations like `ipp-os-shutdown -t now` - to execute custom shutdown script if it is found, and to skip it if not available @@ -201,7 +202,7 @@ the Eaton NMC Web-GUI - to implement numerous fixes and improvements in the `install.sh` script, including integration of new settings for early shutdown -and UPS powercycling strategy +and UPS power-cycling strategy === A few important notes helpful during testing diff --git a/scripts/installer/make_package.sh b/scripts/installer/make_package.sh index acccf35314..2ebc08bf77 100755 --- a/scripts/installer/make_package.sh +++ b/scripts/installer/make_package.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # make_package.sh # Copyright (c) 2013-2015, by Eaton (R) Corporation. All rights reserved. diff --git a/scripts/perl/Nut.pm b/scripts/perl/Nut.pm index f4f81f27f6..25d56dba21 100644 --- a/scripts/perl/Nut.pm +++ b/scripts/perl/Nut.pm @@ -728,7 +728,7 @@ Nut - a module to talk to a UPS via NUT (Network UPS Tools) upsd This is an object-oriented (whoo!) interface between Perl and upsd from the Network UPS Tools package version 1.5 and above -(http://www.networkupstools.org/). +(https://www.networkupstools.org/). Note that it only talks to upsd for you in a Perl-ish way. It doesn't monitor the UPS continously. diff --git a/scripts/python/.gitignore b/scripts/python/.gitignore new file mode 100644 index 0000000000..5e09026a09 --- /dev/null +++ b/scripts/python/.gitignore @@ -0,0 +1,4 @@ +# A script may be generated here which would be installed into system PATH +# to re-exec the real client with its full path name (so that it knows its +# resource directory location): +/NUT-Monitor diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index 8a50571230..0b83249cd5 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -1,23 +1,227 @@ -# Network UPS Tools: data/html - -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 \ - app/icons/scalable/nut-monitor.svg \ - app/README \ - app/pixmaps/var-rw.png \ - app/pixmaps/on_line.png \ - app/pixmaps/warning.png \ - app/pixmaps/on_battery.png \ - app/pixmaps/var-ro.png \ - app/locale/fr/LC_MESSAGES/NUT-Monitor.mo \ - app/locale/it/LC_MESSAGES/NUT-Monitor.mo \ - module/PyNUT.py.in \ - module/test_nutclient.py.in +# Network UPS Tools: scripts/python + +SUBDIRS = module + +# Recognize settings from configure.ac (for make install handling) +nut_with_nut_monitor = @nut_with_nut_monitor@ +nut_with_nut_monitor_dir = @nut_with_nut_monitor_dir@ +nut_with_nut_monitor_py2gtk2 = @nut_with_nut_monitor_py2gtk2@ +nut_with_nut_monitor_py3qt5 = @nut_with_nut_monitor_py3qt5@ + +# Note: this may be "desktop-file-install" to use in e.g. +# packaging postinstall scripts for tighter OS integration; +# note also the icon files, etc. that may want symlinks to +# system-defined locations. For examples please see e.g. +# https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=nut-monitor +#nut_with_nut_monitor_desktop = @nut_with_nut_monitor_desktop@ + +nut_with_pynut = @nut_with_pynut@ +nut_with_pynut_py = @nut_with_pynut_py@ +nut_with_pynut_py2 = @nut_with_pynut_py2@ +nut_with_pynut_py3 = @nut_with_pynut_py3@ + +PYTHON_SITE_PACKAGES = @PYTHON_SITE_PACKAGES@ +PYTHON2_SITE_PACKAGES = @PYTHON2_SITE_PACKAGES@ +PYTHON3_SITE_PACKAGES = @PYTHON3_SITE_PACKAGES@ + +BINDIR = @BINDIR@ + +NUT_MONITOR_PY2GTK2 = \ + app/ui/gui-1.3.glade \ + app/nut-monitor-py2gtk2.desktop + +NUT_MONITOR_PY2GTK2_TEMPLATE = \ + app/NUT-Monitor-py2gtk2.in + +NUT_MONITOR_PY2GTK2_GENERATED_SCRIPT = \ + app/NUT-Monitor-py2gtk2 + +NUT_MONITOR_PY3QT5 = \ + app/ui/aboutdialog1.ui \ + app/ui/dialog1.ui \ + app/ui/dialog2.ui \ + app/ui/window1.ui \ + app/nut-monitor-py3qt5.desktop + +NUT_MONITOR_PY3QT5_TEMPLATE = \ + app/NUT-Monitor-py3qt5.in + +NUT_MONITOR_PY3QT5_GENERATED_SCRIPT = \ + app/NUT-Monitor-py3qt5 + +NUT_MONITOR_COMMON = \ + README.adoc \ + app/nut-monitor.appdata.xml \ + app/icons/48x48/nut-monitor.png \ + app/icons/64x64/nut-monitor.png \ + app/icons/256x256/nut-monitor.png \ + app/icons/scalable/nut-monitor.svg \ + app/README.adoc \ + app/screenshots/nut-monitor-1.png \ + app/screenshots/nut-monitor-2.png \ + app/screenshots/nut-monitor-3.png \ + app/pixmaps/on_battery.png \ + app/pixmaps/on_line.png \ + app/pixmaps/var-ro.png \ + app/pixmaps/var-rw.png \ + app/pixmaps/warning.png \ + app/locale/fr/LC_MESSAGES/NUT-Monitor.mo \ + app/locale/it/LC_MESSAGES/NUT-Monitor.mo \ + app/locale/ru/LC_MESSAGES/NUT-Monitor.mo + +PYNUT_COMMON = \ + module/README.adoc + +# Note: we both distribute and install the generated *.mo translation files +# so they are listed above and not in NUT_MONITOR_COMMON_TEMPLATE +NUT_MONITOR_COMMON_TEMPLATE = \ + app/locale/NUT-Monitor.pot \ + app/locale/fr/fr.po \ + app/locale/it/it.po \ + app/locale/ru/ru.po + +PYNUT_TEMPLATE = \ + module/setup.py.in \ + module/PyNUT.py.in \ + module/test_nutclient.py.in + +PYNUT_GENERATED_NOEXEC = \ + module/PyNUT.py + +PYNUT_GENERATED_SCRIPT = \ + module/test_nutclient.py + +# For now, we have a "dispatcher" script and applet manifest, +# to select a functional choice of the GUI client, if possible: +NUT_MONITOR_DISPATCHER_NOEXEC = \ + app/nut-monitor.desktop +NUT_MONITOR_DISPATCHER_SCRIPT = \ + app/NUT-Monitor + +################################################################# +# `make dist` tarball contents: +EXTRA_DIST = \ + $(NUT_MONITOR_DISPATCHER_NOEXEC) $(NUT_MONITOR_DISPATCHER_SCRIPT) \ + $(NUT_MONITOR_COMMON) $(NUT_MONITOR_COMMON_TEMPLATE) \ + $(PYNUT_COMMON) $(PYNUT_TEMPLATE) + +EXTRA_DIST += $(NUT_MONITOR_PY2GTK2) $(NUT_MONITOR_PY2GTK2_TEMPLATE) +EXTRA_DIST += $(NUT_MONITOR_PY3QT5) $(NUT_MONITOR_PY3QT5_TEMPLATE) + +################################################################# +# `make install` handling (nobase_ to keep tree structure): +# Make py2/py3-only builds, delivered preferred symlinks, etc. optional +if WITH_NUT_MONITOR +nutmonitordir = $(nut_with_nut_monitor_dir) +nobase_nutmonitor_DATA = $(NUT_MONITOR_DISPATCHER_NOEXEC) $(NUT_MONITOR_COMMON) +nobase_nutmonitor_SCRIPTS = $(NUT_MONITOR_DISPATCHER_SCRIPT) + +if HAVE_MSGFMT +# Note lack of "$<" below - it is a non-portable GNU Make extension +# The POT-Creation-Date: is removed by current python gettext builder to avoid +# "spurious" changes that do not benefit (otherwise unmodified) contents; see: +# https://github.com/sphinx-doc/sphinx/pull/3490 +# https://github.com/sphinx-doc/sphinx/issues/3443 +# Note that OUTFILE may be in builddir (not necessarily same as srcdir) +ACT_MSGFMT = { \ + $(GREP) -v -E '^.?POT-Creation-Date: ' < "$${SRCFILE}" > "$${OUTFILE}.tmpsrc" && \ + $(MSGFMT) -o "$${OUTFILE}" "$${OUTFILE}.tmpsrc" && \ + rm -f "$${OUTFILE}.tmpsrc" ; \ +} + +app/locale/fr/LC_MESSAGES/NUT-Monitor.mo: app/locale/fr/fr.po + @$(MKDIR_P) "$(builddir)/app/locale/fr/LC_MESSAGES" + SRCFILE="$^"; OUTFILE="$@"; $(ACT_MSGFMT) + +app/locale/it/LC_MESSAGES/NUT-Monitor.mo: app/locale/it/it.po + @$(MKDIR_P) "$(builddir)/app/locale/it/LC_MESSAGES" + SRCFILE="$^"; OUTFILE="$@"; $(ACT_MSGFMT) + +app/locale/ru/LC_MESSAGES/NUT-Monitor.mo: app/locale/ru/ru.po + @$(MKDIR_P) "$(builddir)/app/locale/ru/LC_MESSAGES" + SRCFILE="$^"; OUTFILE="$@"; $(ACT_MSGFMT) +endif HAVE_MSGFMT + +if WITH_NUT_MONITOR_PY2GTK2 +nobase_nutmonitor_DATA += $(NUT_MONITOR_PY2GTK2) +nobase_nutmonitor_SCRIPTS += $(NUT_MONITOR_PY2GTK2_GENERATED_SCRIPT) +endif WITH_NUT_MONITOR_PY2GTK2 + +if WITH_NUT_MONITOR_PY3QT5 +nobase_nutmonitor_DATA += $(NUT_MONITOR_PY3QT5) +nobase_nutmonitor_SCRIPTS += $(NUT_MONITOR_PY3QT5_GENERATED_SCRIPT) +endif WITH_NUT_MONITOR_PY3QT5 + +if WITH_PYNUT_APP +nobase_nutmonitor_DATA += $(PYNUT_COMMON) $(PYNUT_GENERATED_NOEXEC) +nobase_nutmonitor_SCRIPTS += $(PYNUT_GENERATED_SCRIPT) +endif WITH_PYNUT_APP + +sysbindir = $(BINDIR) +sysbin_SCRIPTS = NUT-Monitor + +# Dummy redirector for /usr/bin/... presence +NUT-Monitor: Makefile + @echo '#!/bin/sh' > "$@" + @echo 'exec "$(nutmonitordir)/app/NUT-Monitor" "$$@"' >> "$@" + +endif WITH_NUT_MONITOR + + +# These are dumped into site-packages directly, right? +if WITH_PYNUT_PY +pynut_py_sitedir = $(PYTHON_SITE_PACKAGES) +pynut_py_site_DATA = $(PYNUT_GENERATED_NOEXEC) +pynut_py_site_SCRIPTS = $(PYNUT_GENERATED_SCRIPT) +endif + + +if WITH_PYNUT_PY2 +pynut_py2_sitedir = $(PYTHON2_SITE_PACKAGES) +pynut_py2_site_DATA = $(PYNUT_GENERATED_NOEXEC) +pynut_py2_site_SCRIPTS = $(PYNUT_GENERATED_SCRIPT) +endif + + +if WITH_PYNUT_PY3 +pynut_py3_sitedir = $(PYTHON3_SITE_PACKAGES) +pynut_py3_site_DATA = $(PYNUT_GENERATED_NOEXEC) +pynut_py3_site_SCRIPTS = $(PYNUT_GENERATED_SCRIPT) +endif + +################################################################# + +SPELLCHECK_SRC = \ + README.adoc \ + app/README.adoc \ + module/README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked */*-spellchecked + +################################################################# MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + $(AM_V_at)rm -rf *.pyc __pycache__ */*.pyc */__pycache__ */*/*.pyc */*/__pycache__ + $(AM_V_at)rm -f NUT-Monitor module/setup.py diff --git a/scripts/python/README b/scripts/python/README deleted file mode 100644 index 15932d9908..0000000000 --- a/scripts/python/README +++ /dev/null @@ -1,32 +0,0 @@ -Python NUT Client files ------------------------ - -This directory contains various NUT Client related Python scripts, written by -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. - -To import it on Python programs you have to use the following (case sensitive) : -'import PyNUT' - -This module provides a 'PyNUTClient' class that can be used to connect and get -data from an upsd data server. - -To install the PyNUT module on Debian/Ubuntu, copy it to: -/usr/share/python-support/python-pynut/ - -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. - - -* "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/, -- nut-monitor.png to something like /usr/share/pixmaps/ -- and nut-monitor.desktop to /usr/share/applications diff --git a/scripts/python/README.adoc b/scripts/python/README.adoc new file mode 100644 index 0000000000..72e268063b --- /dev/null +++ b/scripts/python/README.adoc @@ -0,0 +1,61 @@ +Python NUT Client files +----------------------- + +This directory contains various NUT Client related Python scripts, written +by David Goncalves, and released under GPL v3. + +module +~~~~~~ + +This directory contains `PyNUT.py`, which is a Python abstraction class to +access NUT data 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 into Python programs you have to use the following line (case +sensitive): + + import PyNUT + +This module provides a `PyNUTClient` class that can be used to connect and +get 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 +and it also serves as a code example. For this to be fully functional, you will +need to adapt the login, password and upsname to fit your configuration. +A NUT data server should be running for the test program to verify connection +and protocol support. + +For one practical example, you can research `tests/NIT/nit.sh` in NUT sources. + +app +~~~ + +This directory contains the `NUT-Monitor` UI application, which uses the +`PyNUT` class, along with its own resources. + +There are two closely related separate implementations, for Python 2 with GTK2 +and for Python 3 with Qt5. Both can be installed at the same time, if your +distribution has not yet outlawed the obsolete Python 2 interpreters. + +To install it, you will either need to keep the files together, or to install: + +- Depending on the Python version(s) your system has, put `NUT-Monitor-py2gtk2` + and/or `NUT-Monitor-py3qt5` to `/usr/bin/`, `/usr/X11R6/bin/` or something + like that (optionally making a simple `NUT-Monitor` symlink to the preferred + implementation version or using the provided wrapper script), +- `ui/*.glade` (for `NUT-Monitor-py2gtk2`) or `ui/*.ui` (for `NUT-Monitor-py3qt5`) + files to `/usr/share/nut-monitor/`, +- `nut-monitor.png` to something like `/usr/share/pixmaps/`, +- finally, `nut-monitor-py2gtk2.desktop` and/or `nut-monitor-py3qt5.desktop` + (optionally symlinked as `nut-monitor.desktop`) to `/usr/share/applications/` + +The `PyNUT` module can be kept nearby, or must be installed as a "site" or "vendor" +provided script into your Python modules location. See the wrapper script for more technical details. 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..11c5a07e3b --- /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-2023 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: +[ -s "$0"-py2gtk2 -a -x "$0"-py2gtk2 ] && PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,'`" || PYTHON_PY2GTK2="" +[ -s "$0"-py3qt5 -a -x "$0"-py3qt5 ] && 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"-py3qt5 "$@" + fi +else + if [ -n "$PYTHON_PY2GTK2" ] ; then + exec "$0"-py2gtk2 "$@" + fi + if [ -n "$PYTHON_PY3QT5" ] ; then + exec "$0"-py3qt5 "$@" + 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 96% rename from scripts/python/app/NUT-Monitor.in rename to scripts/python/app/NUT-Monitor-py2gtk2.in index c0357a9a9b..652132f1c8 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 @@ -75,8 +75,11 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() + # FIXME: tell the system to not guess and definitively use + # the `nut-monitor-py2gtk2.desktop` for windowing resources: + # g_set_prgname('nut-monitor-py2gtk2') - 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.__widgets["interface"] = gtk.glade.XML( self.__glade_file, "window1", APP ) self.__widgets["main_window"] = self.__widgets["interface"].get_widget("window1") @@ -109,11 +112,11 @@ class interface : # Create the tray icon and connect it to the show/hide method... self.__widgets["status_icon"] = gtk.StatusIcon() - self.__widgets["status_icon"].set_from_file( "/usr/share/nut-monitor/pixmaps/on_line.png" ) + self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) self.__widgets["status_icon"].set_visible( True ) self.__widgets["status_icon"].connect( "activate", self.tray_activated ) - self.__widgets["ups_status_image"].set_from_file( "/usr/share/nut-monitor/pixmaps/on_line.png" ) + self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) # Define interface callbacks actions self.__callbacks = { "on_window1_destroy" : self.quit, @@ -271,8 +274,8 @@ class interface : #------------------------------------------------------------------- # Change the status icon and tray icon def change_status_icon( self, icon="on_line", blink=False ) : - self.__widgets["status_icon"].set_from_file( "/usr/share/nut-monitor/pixmaps/%s.png" % icon ) - self.__widgets["ups_status_image"].set_from_file( "/usr/share/nut-monitor/pixmaps/%s.png" % icon ) + self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) + self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) self.__widgets["status_icon"].set_blinking( blink ) #------------------------------------------------------------------- @@ -683,6 +686,11 @@ class interface : self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) return + if not self.__ups_handler.CheckUPSAvailable( self.__current_ups ): + self.gui_status_message( _("UPS '{0}' is not reachable").format( self.__current_ups ) ) + self.gui_status_notification( _("UPS '{0}' is not reachable").format( self.__current_ups ), "warning.png" ) + return + self.__connected = True self.__widgets["ups_connect"].hide() self.__widgets["ups_disconnect"].show() @@ -728,9 +736,9 @@ class interface : for k,v in vars.iteritems() : if ( rwvars.has_key( k ) ) : - icon_file = "/usr/share/nut-monitor/pixmaps/var-rw.png" + icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-rw.png" ) else : - icon_file = "/usr/share/nut-monitor/pixmaps/var-ro.png" + icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-ro.png" ) icon = gtk.gdk.pixbuf_new_from_file( icon_file ) self.__widgets["ups_vars_tree_store"].append( [ icon, k, v ] ) diff --git a/scripts/python/app/NUT-Monitor-py3qt5.in b/scripts/python/app/NUT-Monitor-py3qt5.in new file mode 100755 index 0000000000..a149ddaf31 --- /dev/null +++ b/scripts/python/app/NUT-Monitor-py3qt5.in @@ -0,0 +1,968 @@ +#!@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. +# +# 2023-11-27 Laurent Bigonville - Version 2.0.1 +# Set the DesktopFileName + + +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 ) + try: + self.__app.setDesktopFileName("nut-monitor-py3qt5") + except Exception as ex: + pass + + 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 + + # Normally icons should be installed to OS location by packaging: + path = QStandardPaths.locate(QStandardPaths.GenericDataLocation, os.path.join( "icons", "hicolor", "256x256", "apps", filename ) ) + if os.path.exists(path): + return path + + # Fall back to NUT-specific area where `make install` might put them, + # e.g. /usr/share/nut/nut-monitor/icons/... or some such, if not the + # same location as checked in first attempt above: + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, os.path.join( "icons", "hicolor", "256x256", "apps", filename ) ) + if os.path.exists(path): + return path + + # No banana! + 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 + + if not self.__ups_handler.CheckUPSAvailable( self.__current_ups ): + self.gui_status_message( _("UPS '{0}' is not reachable").format( self.__current_ups ) ) + self.gui_status_notification( _("UPS '{0}' is not reachable").format( 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 deleted file mode 100644 index b7acd299fa..0000000000 --- a/scripts/python/app/README +++ /dev/null @@ -1,7 +0,0 @@ -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) - -David Goncalves diff --git a/scripts/python/app/README.adoc b/scripts/python/app/README.adoc new file mode 100644 index 0000000000..f2d31afb96 --- /dev/null +++ b/scripts/python/app/README.adoc @@ -0,0 +1,67 @@ +NUT-Monitor +=========== + +NUT-Monitor is a graphical application to access and manage UPSes connected to +a NUT (Network UPS Tools) server. + +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. + +Screenshots +----------- + +image::screenshots/nut-monitor-1.png[Example of device status overview] + +image::screenshots/nut-monitor-2.png[Example report of device variables] + +image::screenshots/nut-monitor-3.png[Example modification of a writable variable] + +Kudos +----- + +NUT-Monitor and PyNUT (for Python 2 syntax) were originally authored +by David Goncalves + +NUT-Monitor was converted to Python 3 + Qt5 by Luke Dashjr + +PyNUT was extended, and two variants of NUT-Monitor converged and wrapped +for Python 2+3 dual support by Jim Klimov diff --git a/scripts/python/app/locale/NUT-Monitor.pot b/scripts/python/app/locale/NUT-Monitor.pot index caf9c6687c..d36e61af28 100644 --- a/scripts/python/app/locale/NUT-Monitor.pot +++ b/scripts/python/app/locale/NUT-Monitor.pot @@ -358,7 +358,7 @@ msgid "" "For more information about NUT (Network UPS Tools)\n" "please visit the author's website.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" msgstr "" #: gui-1.3.glade.h:37 diff --git a/scripts/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo b/scripts/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo index ede50eb252..9b7d3f958c 100644 Binary files a/scripts/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo and b/scripts/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo differ diff --git a/scripts/python/app/locale/fr/fr.po b/scripts/python/app/locale/fr/fr.po index e46edd64b2..2eacbe8b2a 100644 --- a/scripts/python/app/locale/fr/fr.po +++ b/scripts/python/app/locale/fr/fr.po @@ -5,7 +5,7 @@ # msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: NUT Monitor\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-10-14 22:47+0200\n" "PO-Revision-Date: 2013-10-14 22:50+0200\n" @@ -375,14 +375,14 @@ msgid "" "For more information about NUT (Network UPS Tools)\n" "please visit the author's website.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" msgstr "" "Interface pour gĆ©rer les onduleurs connectĆ©s Ć  un serveur NUT.\n" "\n" "Pour plus d'informations sur NUT (Network UPS Tools)\n" "veuillez consulter le site de l'auteur.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" #: gui-1.3.glade.h:37 msgid "http://www.lestat.st" diff --git a/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo b/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo index 5a95c22f13..7aed48fadc 100644 Binary files a/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo and b/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo differ diff --git a/scripts/python/app/locale/it/it.po b/scripts/python/app/locale/it/it.po index ada6329947..da0287090e 100644 --- a/scripts/python/app/locale/it/it.po +++ b/scripts/python/app/locale/it/it.po @@ -375,14 +375,14 @@ msgid "" "For more information about NUT (Network UPS Tools)\n" "please visit the author's website.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" msgstr "" "Interfaccia grafica per gestire dispositivi connessi a un server di NUT.\n" "\n" "Per maggiori informazioni su NUT (Network UPS Tools)\n" "si visiti il sito dell'autore.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" #: gui-1.3.glade.h:37 msgid "http://www.lestat.st" diff --git a/scripts/python/app/locale/ru/LC_MESSAGES/NUT-Monitor.mo b/scripts/python/app/locale/ru/LC_MESSAGES/NUT-Monitor.mo index 89ee020832..a924cefbf7 100644 Binary files a/scripts/python/app/locale/ru/LC_MESSAGES/NUT-Monitor.mo and b/scripts/python/app/locale/ru/LC_MESSAGES/NUT-Monitor.mo differ diff --git a/scripts/python/app/locale/ru/ru.po b/scripts/python/app/locale/ru/ru.po index b163415e18..95bfcbc148 100644 --- a/scripts/python/app/locale/ru/ru.po +++ b/scripts/python/app/locale/ru/ru.po @@ -374,14 +374,14 @@ msgid "" "For more information about NUT (Network UPS Tools)\n" "please visit the author's website.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" msgstr "" "Š“Ń€Š°Ń„ŠøчŠµŃŠŗŠ°Ń утŠøŠ»ŠøтŠ° уŠæрŠ°Š²Š»ŠµŠ½Šøя устрŠ¾Š¹ŃŃ‚Š²Š°Š¼Šø, ŠæŠ¾Š“ŠŗŠ»ŃŽŃ‡ŠµŠ½Š½Ń‹Š¼Šø Šŗ сŠµŃ€Š²ŠµŃ€Ńƒ NUT.\n" "\n" "Š”Š»Ń ŠæŠ¾Š»ŃƒŃ‡ŠµŠ½Šøя Š“Š¾ŠæŠ¾Š»Š½ŠøтŠµŠ»ŃŒŠ½Š¾Š¹ ŠøŠ½Ń„Š¾Ń€Š¼Š°Ń†ŠøŠø Š¾ NUT (Network UPS Tools)\n" "ŠæŠ¾Š¶Š°Š»ŃƒŠ¹ŃŃ‚Š° ŠæŠ¾ŃŠµŃ‚ŠøтŠµ Š²ŠµŠ±-сŠ°Š¹Ń‚ ŠæрŠ¾ŠµŠŗтŠ°.\n" "\n" -"http://www.networkupstools.org\n" +"https://www.networkupstools.org\n" #: gui-1.3.glade.h:37 msgid "http://www.lestat.st" 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.appdata.xml b/scripts/python/app/nut-monitor.appdata.xml index da0f163b6c..7222dafce9 100644 --- a/scripts/python/app/nut-monitor.appdata.xml +++ b/scripts/python/app/nut-monitor.appdata.xml @@ -2,6 +2,7 @@ nut-monitor.desktop + nut-monitor.desktop CC0-1.0 GPL-3.0+ NUT Monitor @@ -23,12 +24,12 @@
  • Favorites, to store different devices
  • Display all device variables
  • Modify writable variables on UPS and devices
  • -
  • Support English and French
  • +
  • Supports English, Russian and French localization (Python2 version only at the moment)
  • NUT Monitor requires that you have a running NUT system, that you can connect to, either locally or remotely. - For more information on NUT: http://www.networkupstools.org/ + For more information on NUT: https://www.networkupstools.org/

    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/screenshots/nut-monitor-1.png b/scripts/python/app/screenshots/nut-monitor-1.png new file mode 100644 index 0000000000..a3be9c9102 Binary files /dev/null and b/scripts/python/app/screenshots/nut-monitor-1.png differ diff --git a/scripts/python/app/screenshots/nut-monitor-2.png b/scripts/python/app/screenshots/nut-monitor-2.png new file mode 100644 index 0000000000..3c511e8b79 Binary files /dev/null and b/scripts/python/app/screenshots/nut-monitor-2.png differ diff --git a/scripts/python/app/screenshots/nut-monitor-3.png b/scripts/python/app/screenshots/nut-monitor-3.png new file mode 100644 index 0000000000..fa6714266d Binary files /dev/null and b/scripts/python/app/screenshots/nut-monitor-3.png differ diff --git a/scripts/python/app/ui/aboutdialog1.ui b/scripts/python/app/ui/aboutdialog1.ui new file mode 100644 index 0000000000..f57969c33e --- /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">https://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 99% rename from scripts/python/app/gui-1.3.glade rename to scripts/python/app/ui/gui-1.3.glade index eb42aba3b0..5ab1f72836 100644 --- a/scripts/python/app/gui-1.3.glade +++ b/scripts/python/app/ui/gui-1.3.glade @@ -1028,7 +1028,7 @@ want to delete from list... For more information about NUT (Network UPS Tools) please visit the author's website. -http://www.networkupstools.org +https://www.networkupstools.org http://www.lestat.st/informatique/projets/nut-monitor-en http://www.lestat.st diff --git a/scripts/python/app/gui-1.3.glade.h b/scripts/python/app/ui/gui-1.3.glade.h similarity index 97% rename from scripts/python/app/gui-1.3.glade.h rename to scripts/python/app/ui/gui-1.3.glade.h index 8b9c95f164..33c3d4ecdd 100644 --- a/scripts/python/app/gui-1.3.glade.h +++ b/scripts/python/app/ui/gui-1.3.glade.h @@ -32,7 +32,7 @@ char *s = N_("GUI to manage devices connected a NUT server.\n" "For more information about NUT (Network UPS Tools)\n" "please visit the author's website.\n" "\n" - "http://www.networkupstools.org\n" + "https://www.networkupstools.org\n" ""); char *s = N_("http://www.lestat.st"); char *s = N_("Copyright (C) 2010 David Goncalves \n" 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/.gitignore b/scripts/python/module/.gitignore index 4f98b981bd..7606bf7f7f 100644 --- a/scripts/python/module/.gitignore +++ b/scripts/python/module/.gitignore @@ -1,2 +1,12 @@ /PyNUT.py /test_nutclient.py +/setup.py +/PyNUTClient +/PyNUTClient.egg-info +/build +/dist +/src +/.tox +/README.txt +/.pypi-* +/LICENSE-GPL3 diff --git a/scripts/python/module/MANIFEST.in b/scripts/python/module/MANIFEST.in new file mode 100644 index 0000000000..5488969592 --- /dev/null +++ b/scripts/python/module/MANIFEST.in @@ -0,0 +1 @@ +include tox.ini diff --git a/scripts/python/module/Makefile.am b/scripts/python/module/Makefile.am new file mode 100644 index 0000000000..fc46eab0ad --- /dev/null +++ b/scripts/python/module/Makefile.am @@ -0,0 +1,159 @@ +# Network UPS Tools: scripts/python/module (PyNUTClient) + +# See also: .github/workflows/PyNUTClient.yml +# Note: this Makefile is focused on PyPI publication +# The usual autotools stuff including clean-up is in parent dir +# (to allow easier mixing of the module and app, if/when desired) + +# Non-maintainers can at most generate the source layout for python setuptools +# (having only shell scripting as a prerequisite suffices for that) +all: PyNUTClient + +check-local: + @echo "You may want to set up a NUT data server and run 'make tox' here: `pwd`" + +# NOT tying into "make check" because a lot of stars must align for this test: +tox: dist .pypi-tools-tox + tox + +EXTRA_DIST = tox.ini MANIFEST.in + +NUT_SOURCE_GITREV_NUMERIC = @NUT_SOURCE_GITREV_NUMERIC@ +PYTHON = @PYTHON@ + +GENERATED_DIST = dist build *.egg-info +GENERATED_SRC = PyNUTClient README.txt LICENSE-GPL3 + +# These are normally generated by a NUT build, but if we want to iterate +# specifically PyNUTClient packaging - `make veryclean dist` should do it here: +GENERATED_PY = test_nutclient.py PyNUT.py setup.py + +# (Re-)generate files normally made by `configure` from .in templates +# No touch-files here, intended for manual use in developer iterations +py-in: $(GENERATED_PY) +redist: clean py-in dist + +$(GENERATED_DIST): .pypi-dist + +# NOTE: We only clean .pypi-tools* in MAINTAINERCLEANFILES to avoid regular +# re-detection of the probably unchanging environment! +clean-local: + rm -rf $(GENERATED_SRC) $(GENERATED_DIST) + rm -f .pypi-src .pypi-dist* + +veryclean: clean + rm -f $(GENERATED_PY) + +# Python test envs take a while to populate, so maybe better not clean +# them too enthusiastically. Can revise (move to "clean-local") later, +# if this choice proves a problem. +distclean-local: + rm -rf .tox + rm -f $(GENERATED_PY) + +MAINTAINERCLEANFILES = Makefile.in .dirstamp .pypi-tools* + +PyNUTClient: .pypi-src + +# Tagged releases should only have three blocks of digits separated by dots +upload publish: + +@echo " PYPI Checking upload type for module version '$(NUT_SOURCE_GITREV_NUMERIC)'" ; \ + case x"`echo "$(NUT_SOURCE_GITREV_NUMERIC)" | tr -d '[0-9]'`" in \ + x..) echo " PYPI ...release"; $(MAKE) $(AM_MAKEFLAGS) upload-pypi ;; \ + x*) echo " PYPI ...testing"; $(MAKE) $(AM_MAKEFLAGS) upload-testpypi ;; \ + esac + +# README.txt is also a part of module standard expectations +.pypi-src: test_nutclient.py.in PyNUT.py.in setup.py.in README.adoc Makefile $(top_srcdir)/LICENSE-GPL3 + @echo " PYPI Generate PyPI module source" + @rm -rf $(GENERATED_SRC) "$@" + @mkdir -p PyNUTClient + @for S in "$(srcdir)"/*.py.in ; do \ + B="`basename "$${S}" .in`" ; \ + if test x"$${B}" = xsetup.py ; then \ + if ! test -s "$${B}" ; then \ + sed -e "s/[@]NUT_SOURCE_GITREV_NUMERIC[@]/$(NUT_SOURCE_GITREV_NUMERIC)/" < "$(srcdir)/$${B}.in" > "$${B}" || exit ; \ + fi ; \ + continue; \ + fi; \ + if test -s "$${B}" ; then \ + cp -pf "$${B}" PyNUTClient/ || exit ; \ + continue; \ + fi ; \ + sed -e "s,[@]PYTHON[@],@PYTHON@," < "$(srcdir)/$${B}.in" > "PyNUTClient/$${B}" || exit ; \ + if test -x "$(srcdir)/$${B}.in" ; then chmod +x "PyNUTClient/$${B}"; fi ; \ + done ; \ + cp -pf "$(srcdir)/README.adoc" README.txt || exit ; \ + cp -pf "$(top_srcdir)/LICENSE-GPL3" . || exit ; \ + echo "from . import PyNUT" > PyNUTClient/__init__.py || exit + @touch "$@" + +.pypi-tools-python: + @echo " PYPI Checking that PYTHON variable is defined and usable: $(PYTHON)" + @test -n "$(PYTHON)" && command -v $(PYTHON) + @touch "$@" + +.pypi-tools-dist-wheel: .pypi-tools-python + @echo " PYPI Prepare PyPI tools to generate a distribution (wheel)" + @$(PYTHON) -m pip install wheel + @touch "$@" + +.pypi-tools-dist-build: .pypi-tools-python + @echo " PYPI Prepare PyPI tools to generate a distribution (build)" + @$(PYTHON) -m pip install build + @touch "$@" + +.pypi-tools-tox: .pypi-tools-python + @echo " PYPI Prepare Python multi-environment testing tools (tox)" + @$(PYTHON) -m pip install tox + @touch "$@" + +# Install via OS packaging or pip? +# https://twine.readthedocs.io/en/stable/ +.pypi-tools-upload: .pypi-tools-python + @echo " PYPI Prepare PyPI tools and resources to upload a distribution (twine)" + @$(PYTHON) -m pip install twine + @command -v twine + @test -s $(HOME)/.pypirc + @touch "$@" + +# Using pypa/setuptools +.pypi-dist: .pypi-src + +@$(MAKE) $(AM_MAKEFLAGS) .pypi-dist-pip-build || \ + $(MAKE) $(AM_MAKEFLAGS) .pypi-dist-obsolete || \ + $(MAKE) $(AM_MAKEFLAGS) .pypi-dist-pip-wheel + @touch "$@" + +# The most modern approach as of 2023 +.pypi-dist-pip-build: .pypi-tools-dist-build .pypi-src + @echo " PYPI Generate PyPI module distribution (using 'build' module)" + @rm -rf $(GENERATED_DIST) "$@" + @$(PYTHON) -m build --skip-dependency-check --no-isolation + @touch "$@" + +# Using "setup.py" directly causes warnings about its deprecation +# While "pip" distribution generator also uses it internally, +# it "knows what it's doing" and goes quietly about its work :) +# Does not support "sdis" though. Oh well. +.pypi-dist-pip-wheel: .pypi-tools-dist-wheel .pypi-src + @echo " PYPI Generate PyPI module distribution (using 'pip wheel')" + @rm -rf $(GENERATED_DIST) "$@" + @$(PYTHON) -m pip wheel --no-deps -w dist . + @touch "$@" + +.pypi-dist-obsolete: .pypi-tools-dist-wheel .pypi-src + @echo " PYPI Generate PyPI module distribution (using setup.py directly)" + @rm -rf $(GENERATED_DIST) "$@" + @$(PYTHON) setup.py sdist bdist_wheel + @touch "$@" + +# These need $HOME/.pypirc prepared with credentials (API tokens) from +# https://pypi.org/manage/account/ and https://test.pypi.org/manage/account/ +# TODO: .asc/.sig files for releases? +upload-pypi: .pypi-dist .pypi-tools-upload + @echo " PYPI Upload PyPI module distribution to production/release PyPI repository" + @twine upload dist/* + +upload-testpypi: .pypi-dist .pypi-tools-upload + @echo " PYPI Upload PyPI module distribution to testing/staging PyPI repository" + @twine upload -r testpypi dist/* diff --git a/scripts/python/module/PyNUT.py.in b/scripts/python/module/PyNUT.py.in index 3379239278..6f3c4ff510 100644 --- a/scripts/python/module/PyNUT.py.in +++ b/scripts/python/module/PyNUT.py.in @@ -43,6 +43,18 @@ # in Python 3.9, by spelling out b"STR" or str.encode('ascii'); # the change was also tested to work with Python 2.7, 3.4, 3.5 and # 3.7 (to the extent of accompanying test_nutclient.py at least). +# +# 2022-08-12 Jim Klimov - Version 1.5.0 +# Fix ListClients() method to actually work with current NUT protocol +# Added DeviceLogin() method +# Added GetUPSNames() method +# Fixed raised PyNUTError() exceptions to carry a Python string +# (suitable for Python2 and Python3), not byte array from protocol, +# so exception catchers can process them naturally (see test script). +# +# 2023-01-18 Jim Klimov - Version 1.6.0 +# Added CheckUPSAvailable() method originally by Michal Hlavinka +# from 2013-01-07 RedHat/Fedora packaging import telnetlib @@ -61,8 +73,8 @@ class PyNUTClient : __timeout = None __srv_handler = None - __version = "1.4.0" - __release = "2021-09-27" + __version = "1.6.0" + __release = "2023-01-18" def __init__( self, host="127.0.0.1", port=3493, login=None, password=None, debug=False, timeout=5 ) : @@ -113,18 +125,30 @@ if something goes wrong. self.__srv_handler.write( ("USERNAME %s\n" % self.__login).encode('ascii') ) result = self.__srv_handler.read_until( b"\n", self.__timeout ) if result[:2] != b"OK" : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) if self.__password != None : self.__srv_handler.write( ("PASSWORD %s\n" % self.__password).encode('ascii') ) result = self.__srv_handler.read_until( b"\n", self.__timeout ) if result[:2] != b"OK" : - raise PyNUTError( result.replace( b"\n", b"" ) ) + if result == b"ERR INVALID-ARGUMENT\n" : + # Quote the password (if it has whitespace etc) + # TODO: Escape special chard like NUT does? + self.__srv_handler.write( ("PASSWORD \"%s\"\n" % self.__password).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n", self.__timeout ) + if result[:2] != b"OK" : + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) + else: + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) def GetUPSList( self ) : """ Returns the list of available UPS from the NUT server The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Description' + +Note that fields here are byte sequences (not locale-aware strings) +which is of little concern for Python2 but is important in Python3 +(e.g. when we use "str" type `ups` variables or check their "validity"). """ if self.__debug : print( "[DEBUG] GetUPSList from server" ) @@ -132,7 +156,7 @@ The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Des self.__srv_handler.write( b"LIST UPS\n" ) result = self.__srv_handler.read_until( b"\n" ) if result != b"BEGIN LIST UPS\n" : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) result = self.__srv_handler.read_until( b"END LIST UPS\n" ) ups_list = {} @@ -144,6 +168,22 @@ The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Des return( ups_list ) + def GetUPSNames( self ) : + """ Returns the list of available UPS names from the NUT server as strings + +The result is a set of str objects (comparable with ups="somename" and +useful as arguments to other methods). Helps work around Python2/Python3 +string API changes. + """ + if self.__debug : + print( "[DEBUG] GetUPSNames from server" ) + + self_ups_list = [] + for b in self.GetUPSList(): + self_ups_list.append(b.decode('ascii')) + + return self_ups_list + def GetUPSVars( self, ups="" ) : """ Get all available vars from the specified UPS @@ -156,7 +196,7 @@ available vars. self.__srv_handler.write( ("LIST VAR %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) if result != ("BEGIN LIST VAR %s\n" % ups).encode('ascii') : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) ups_vars = {} result = self.__srv_handler.read_until( ("END LIST VAR %s\n" % ups).encode('ascii') ) @@ -170,6 +210,23 @@ available vars. return( ups_vars ) + def CheckUPSAvailable( self, ups="" ) : + """ Check whether UPS is reachable + +Just tries to contact UPS with a safe command. +The result is True (reachable) or False (unreachable) + """ + if self.__debug : + print( "[DEBUG] CheckUPSAvailable called..." ) + + self.__srv_handler.write( ("LIST CMD %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if result != ("BEGIN LIST CMD %s\n" % ups).encode('ascii') : + return False + + self.__srv_handler.read_until( ("END LIST CMD %s\n" % ups).encode('ascii') ) + return True + def GetUPSCommands( self, ups="" ) : """ Get all available commands for the specified UPS @@ -182,7 +239,7 @@ of the command as value self.__srv_handler.write( ("LIST CMD %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) if result != ("BEGIN LIST CMD %s\n" % ups).encode('ascii') : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) ups_cmds = {} result = self.__srv_handler.read_until( ("END LIST CMD %s\n" % ups).encode('ascii') ) @@ -219,7 +276,7 @@ The result is presented as a dictionary containing 'key->val' pairs self.__srv_handler.write( ("LIST RW %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) if ( result != ("BEGIN LIST RW %s\n" % ups).encode('ascii') ) : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) result = self.__srv_handler.read_until( ("END LIST RW %s\n" % ups).encode('ascii') ) offset = len( ("VAR %s" % ups).encode('ascii') ) @@ -249,7 +306,7 @@ rights to set it (maybe login/password). if ( result == b"OK\n" ) : return( "OK" ) else : - raise PyNUTError( result ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) def RunUPSCommand( self, ups="", command="" ) : """ Send a command to the specified UPS @@ -265,24 +322,61 @@ Returns OK on success or raises an error if ( result == b"OK\n" ) : return( "OK" ) else : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) + + def DeviceLogin( self, ups="") : + """ Establish a login session with a device (like upsmon does) + +Returns OK on success or raises an error +USERNAME and PASSWORD must have been specified earlier in the session (once) +and upsd.conf should permit that user with one of `upsmon` role types. + +Note there is no "device LOGOUT" in the protocol, just one for general end +of connection. + """ + + if self.__debug : + print( "[DEBUG] DeviceLogin called..." ) + + if ups is None or (ups not in self.GetUPSNames()): + if self.__debug : + print( "[DEBUG] DeviceLogin: %s is not a valid UPS" % ups ) + raise PyNUTError( "ERR UNKNOWN-UPS" ) + + self.__srv_handler.write( ("LOGIN %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result.startswith( ("User %s@" % self.__login).encode('ascii')) and result.endswith (("[%s]\n" % ups).encode('ascii')) ): + # User dummy-user@127.0.0.1 logged into UPS [dummy] + # Read next line then + result = self.__srv_handler.read_until( b"\n" ) + if ( result == b"OK\n" ) : + return( "OK" ) + else : + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) def FSD( self, ups="") : """ Send FSD command 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" ) : + if self.__debug : + print( "[DEBUG] Primary level functions are not available" ) + raise PyNUTError( "ERR ACCESS-DENIED" ) if self.__debug : print( "[DEBUG] FSD called..." ) @@ -291,7 +385,7 @@ TODO: API change pending to replace MASTER with PRIMARY if ( result == b"OK FSD-SET\n" ) : return( "OK" ) else : - raise PyNUTError( result.replace( b"\n", b"" ) ) + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) def help(self) : """ Send HELP command @@ -319,26 +413,52 @@ TODO: API change pending to replace MASTER with PRIMARY The result is a dictionary containing 'key->val' pairs of 'UPSName' and a list of clients """ if self.__debug : - print( "[DEBUG] ListClients from server" ) + print( "[DEBUG] ListClients from server: %s" % ups ) - if ups and (ups not in self.GetUPSList()): - raise PyNUTError( "%s is not a valid UPS" % ups ) + # If (!ups) we use this list below to recurse: + self_ups_list = self.GetUPSNames() + if ups and (ups not in self_ups_list): + if self.__debug : + print( "[DEBUG] ListClients: %s is not a valid UPS" % ups ) + raise PyNUTError( "ERR UNKNOWN-UPS" ) if ups: - self.__srv_handler.write( ("LIST CLIENTS %s\n" % ups).encode('ascii') ) + self.__srv_handler.write( ("LIST CLIENT %s\n" % ups).encode('ascii') ) else: - self.__srv_handler.write( b"LIST CLIENTS\n" ) + # NOTE: Currently NUT does not support just listing all clients + # (not providing an "ups" argument) => NUT_ERR_INVALID_ARGUMENT + self.__srv_handler.write( b"LIST CLIENT\n" ) result = self.__srv_handler.read_until( b"\n" ) - if result != b"BEGIN LIST CLIENTS\n" : - raise PyNUTError( result.replace( b"\n", b"" ) ) - - result = self.__srv_handler.read_until( b"END LIST CLIENTS\n" ) + if ( (ups and result != ("BEGIN LIST CLIENT %s\n" % ups).encode('ascii')) or (ups is None and result != b"BEGIN LIST CLIENT\n") ): + if ups is None and (result == b"ERR INVALID-ARGUMENT\n") : + # For ups==None, list all upses, list their clients + if self.__debug : + print( "[DEBUG] Recurse ListClients() because it did not specify one UPS to query" ) + ups_list = {} + for ups in self_ups_list : + # Update "ups_list" dict with contents of recursive call return + ups_list.update(self.ListClients(ups)) + return( ups_list ) + + # had a seemingly valid arg, but no success: + if self.__debug : + print( "[DEBUG] ListClients from server got unexpected result: %s" % result ) + + raise PyNUTError( result.replace( b"\n", b"" ).decode('ascii') ) + + if ups : + result = self.__srv_handler.read_until( ("END LIST CLIENT %s\n" % ups).encode('ascii') ) + else: + # Should not get here with current NUT: + result = self.__srv_handler.read_until( b"END LIST CLIENT\n" ) ups_list = {} for line in result.split( b"\n" ): + ###print( "[DEBUG] ListClients line: '%s'" % line ) if line[:6] == b"CLIENT" : - host, ups = line[7:].split(b' ') + ups, host = line[7:].split(b' ') ups.replace(b' ', b'') + host.replace(b' ', b'') if not ups in ups_list: ups_list[ups] = [] ups_list[ups].append(host) diff --git a/scripts/python/module/README.adoc b/scripts/python/module/README.adoc new file mode 100644 index 0000000000..18b1d84ddd --- /dev/null +++ b/scripts/python/module/README.adoc @@ -0,0 +1,391 @@ +Introduction +============ + +The PyNUT module provides an abstraction class `PyNUTClient` written in +Python, which allows to connect to a NUT (Network UPS Tools) server and +execute different commands without an application developer needing to +know the NUT communication protocol. It also includes a small self-test +application as a consumer of the module. + +The project was originally started in 2008 by David Goncalves +at https://www.lestat.st/informatique/projets/pynut and was soon added to +the main NUT codebase and further evolved there. + +The `PyNUTClient` class was born from another project the original author +had -- a graphical application to monitor and manage the UPSes connected +to a NUT server. It is now known as `NUT-Monitor` and is also provided +along with NUT sources. + +With that GUI application being written in Python too, it was decided +to split the project in two parts (the PyNUT class + GUI application, +serving as model + view) in order to keep the two parts independent. +That class was subsequently renamed to `PyNUTClient` since version 1.1 +of the original project, allowing to develop other classes nearby later. + +It is published to PyPI repository as +link:https://pypi.org/project/PyNUTClient/[PyNUTClient] +and so should be installable with `pip` tooling. + +Currently the module is regularly tested to work with Python interpreter +versions 2.7, 3.4, 3.5 and 3.7. + +[NOTE] +====== +Text fields returned by methods are byte sequences (not locale-aware +strings), except NUT protocol error codes quoted into `PyNUTError` +exceptions which are decoded into strings as originally `ascii` text. +This is of little concern for Python 2, but is important in Python 3. + +The `ups` argument to methods is handled as a string, so conversion may +be needed to compare with names returned in the lists and dictionaries, +e.g.: +---- +strUps = bUps.decode('ascii') +bUps = strUps.encode('ascii') +---- + +Only the names returned by `GetUPSNames()` method specifically are +converted into string type. + +Since Python 3 support was added just recently, module code may later be +converted to use string types like `str` or `unicode` in the returned +dictionaries and sets instead, if the community deems this to be more +idiomatic. + +Examples below *do not* specify the `b'some text'` markup that would be +pedantically correct (for Python 3 at least). +====== + +.List of methods in the class +------ +class PyNUTClient : + def __init__( self, host='127.0.0.1', port=3493, + login=None, password=None, debug=False, timeout=5 ) : + + def help( self ) : + + def ver( self ) : + + def DeviceLogin( self, ups="" ) : + + def FSD( self, ups="" ) : + + def CheckUPSAvailable( self, ups="" ) : + + def GetRWVars( self, ups='' ) : + + def GetUPSCommands( self, ups='' ) : + + def GetUPSList( self ) : + + def GetUPSNames( self ) : + + def GetUPSVars( self, ups='' ) : + + def ListClients( self, ups = None ) : + + def RunUPSCommand( self, ups='', command='' ) : + + def SetRWVar( self, ups='', var='', value='' ) : +------ + +The module also provides the `PyNUTError` class to represent any exceptions +raised by `PyNUTClient` logic. + +Documentation +------------- + +Although the original project was written in French, for the reasons of general +distribution with NUT, all of its code is commented in English. While this file +contains the description from https://www.lestat.st/informatique/projets/pynut +the class `PyNUTClient` is compatible with the Python module PyDOC, so you can +type `pydoc PyNUT` to obtain succinct documentation on your current version of +the module. + +For more examples see the provided test script and the NUT-Monitor application +in the NUT sources. + +Commands below are listed in alphabetic order. + +__init__ +~~~~~~~~ + +When you initialize the class instance, it performs the connection to the NUT +data server or raises a Python exception from the `__connect()` method called +internally. + + +help +~~~~ + +Sends the `HELP` command to the NUT data server and returns whatever bits of +wisdom it has to offer. + + +ver +~~~ + +Sends the `VER` command to the NUT data server and returns its self-reported +identification such as version, product or distribution it may be bundled with. + +Note that the NUT client interactions should not rely on reported versions, +but follow the protocol as defined. + + +DeviceLogin +~~~~~~~~~~~ + +Establish a "client" session with the specified UPS. This uses credentials +specified earlier to `__init__()` this class instance, and on server side +(in `upsd.users` file) these credentials must have one of `upsmon` roles. + +The command should return `OK` if everything went well, or raise an exception +in case of failure, such as invalid or insufficiently privileged credentials. + +Note there is no `DeviceLogout()`, just the general connection termination. + + +FSD +~~~ + +Send the FSD (Forced ShutDown) command to the specified UPS. + +The command should return `OK` if everything went well, or raise an exception +in case of failure, such as that this server does not allow to manage that UPS +as a "primary" (or "master" before NUT 2.8.0). + + +CheckUPSAvailable +~~~~~~~~~~~~~~~~~ + +Returns a boolean state whether the specified UPS is recognized and available +(`True`), or is not (`False`). + +Internally, requests a listing of commands supported by the device name, and +evaluates the server response. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='server', login='upsadmin', password='upspass' ) + result = ups.CheckUPSAvailable( ups='ups1' ) + print( result ) + + >> True +----- + +See also: `GetUPSCommands()` + + +GetRWVars +~~~~~~~~~ + +Returns a list of modifiable variables on the specified UPS, as a dictionary +of "variable"-"current value" pairs. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='server', login='upsadmin', password='upspass' ) + result = ups.GetRWVars( ups='ups1' ) + print( result ) + + >> {'battery.date': '10/25/07', 'ups.id': 'test'} +----- + +See also: `GetUPSVars()`, `SetRWVar()` + + +GetUPSCommands +~~~~~~~~~~~~~~ + +Returns a list of commands supported by the specified UPS. + +Note that certain commands are not usable without first getting necessary +rights on the NUT data server. + +The result is presented as a dictionary of "command"-"English description" +pairs. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='server', login='upsadmin', password='upspass' ) + result = ups.GetRWVars( ups='ups1' ) + print( result ) + + >> {'test.battery.start' : 'Start a battery test', + 'calibrate.stop' : 'Stop run time calibration', + 'shutdown.stayoff' : 'Turn off the load and remain off', + 'test.battery.stop' : 'Stop the battery test', + 'test.panel.start' : 'Start testing the UPS panel', + 'calibrate.start' : 'Start run time calibration', + 'load.off' : 'Turn off the load immediately', + 'test.failure.start' : 'Start a simulated power failure', + 'shutdown.return' : 'Turn off the load and return when power is back'} +----- + +See also: `RunUPSCommand()` + + +GetUPSList +~~~~~~~~~~ + +Returns the list of UPSes represented by the NUT server, as a dictionary of +"name"-"description" pairs. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='server', login='upsadmin', password='upspass' ) + result = ups.GetUPSList() + print( result ) + + >> {'UPS1': 'Smart UPS 3000 File server', + 'UPS2': 'Smart UPS 1000 Serveur de mail'} +----- + + +GetUPSNames +~~~~~~~~~~~ + +Returns just the list of available UPS names from the NUT server. + +The result is a set of `str` objects (comparable with `ups="somename"` and +useful as arguments to other methods). Helps work around Python2/Python3 +string API changes (where `b'string' != 'string'` and not even a type +comparable to it), and is primarily used as a helper internally in some +methods. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='Serveur', login='upsadmin', password='upspass' ) + result = ups.GetUPSNames() + print( result ) + + >> ['UPS1', 'UPS2'] +----- + + +GetUPSVars +~~~~~~~~~~ + +Returns a list of all variables on the specified UPS, as a dictionary +of "variable"-"current value" pairs. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='Serveur', login='upsadmin', password='upspass' ) + result = ups.GetUPSVars( ups='UPS1' ) + print( result ) + + >> {'input.transfer.high' : '253', + 'battery.charge' : '100.0', + 'ups.mfr' : 'APC', + 'battery.voltage.nominal' : '024', + 'input.transfer.reason' : 'S', + 'ups.test.interval' : '1209600', + 'input.transfer.low' : '208', + 'output.voltage' : '234.0', + 'driver.version' : '2.2.1-', + 'battery.charge.restart' : '00', + 'ups.id' : 'test', + 'driver.parameter.pollinterval' : '2', + 'driver.parameter.port' : '/dev/ttyS0', + 'battery.voltage' : '27.10', + 'ups.test.result' : 'NO', + 'ups.status' : 'OL', + 'battery.date' : '10/25/07', + 'ups.model' : 'Smart-UPS SC1000', + 'ups.serial' : 'XXXXXXXXXXXX', + 'output.voltage.nominal' : '230', + 'ups.mfr.date' : '10/25/07', + 'driver.version.internal' : '1.99.8', + 'input.voltage' : '234.0', + 'battery.runtime.low' : '120', + 'input.sensitivity' : 'H', + 'ups.load' : '001.9', + 'driver.name' : 'apcsmart', + 'input.voltage.maximum' : '234.0', + 'input.frequency' : '50.00', + 'ups.delay.shutdown' : '060', + 'ups.delay.start' : '000', + 'input.voltage.minimum' : '232.0', + 'input.quality' : 'FF', + 'battery.runtime' : '29040', + 'ups.firmware' : '737.3.I', + 'battery.alarm.threshold' : '0'} +----- + +See also: `GetRWVars()` + + +ListClients +~~~~~~~~~~~ + +Returns the list of connected clients (such as the `NUT-Monitor` application +or an `upsmon` service) from the NUT server, for a particular UPS or all of +them by default. + +The result is a dictionary containing "upsname"-"client list" pairing 'UPSName' +and a list of clients for each device if the information was retrieved from +the NUT data server successfully, or an exception is raised otherwise. + + +RunUPSCommand +~~~~~~~~~~~~~ + +Executes the specified command on the specified UPS. It should be one of the +commands returned by the `GetUPSCommands()` function. + +Note that certain commands are not usable without first getting necessary +rights on the NUT data server. + +The command should return `OK` if everything went well, or raise an exception +in case of failure. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='Serveur', login='upsadmin', password='upspass' ) + result = ups.RunUPSCommand( ups='UPS1', command='test.panel.start' ) + print( result ) + + >> OK +----- + +See also: `GetUPSCommands()` + + +SetRWVar +~~~~~~~~ + +This method adjusts the value of a writable NUT variable on the specified UPS. +It should be one of the variables listed by the `GetRWVars()` method. + +Note that you may need to first get necessary rights on the NUT data server. + +The command should return `OK` if everything went well, or raise an exception +in case of failure. + +.Example +----- + import PyNUT + + ups = PyNUT.PyNUTClient( host='Serveur', login='upsadmin', password='upspass' ) + result = ups.SetRWVar( ups='UPS1', var='battery.date', value='06/17/08' ) + print( result ) + + >> OK +----- + +See also: `GetRWVars()` diff --git a/scripts/python/module/setup.py.in b/scripts/python/module/setup.py.in new file mode 100644 index 0000000000..18f956d20c --- /dev/null +++ b/scripts/python/module/setup.py.in @@ -0,0 +1,46 @@ +""" +The setup.py file for PyNUTClient +""" +# Based on https://medium.com/@VersuS_/automate-pypi-releases-with-github-actions-4c5a9cfe947d +# See also .github/workflows/PyNUTClient.yml for active packaging steps + +from setuptools import setup, find_packages +import codecs +import os + +here = os.path.abspath(os.path.dirname(__file__)) + +# README.txt appears from README.adoc during package or CI build +with codecs.open(os.path.join(here, "README.txt"), encoding="utf-8") as fh: + long_description = "\n" + fh.read() + +setup( + name = "PyNUTClient", + version = '@NUT_SOURCE_GITREV_NUMERIC@', + author = "The Network UPS Tools project", + license_files = ('LICENSE-GPL3',), + author_email = "jimklimov+nut@gmail.com", + description = "Python client bindings for NUT", + url = "https://github.com/networkupstools/nut/tree/master/scripts/python/module", + long_description_content_type = "text/plain", # NOTE: No asciidoc so far, see https://packaging.python.org/en/latest/specifications/core-metadata/ + long_description = long_description, + packages = find_packages(), + #py_modules = ['PyNUT'], + package_dir = {'PyNUT': 'PyNUTClient'}, + #data_files = [('', ['tox.ini'])], + #scripts = ['PyNUTClient/test_nutclient.py', 'PyNUTClient/__init__.py'], + python_requires = '>=2.6', + # install_requires = ['telnetlib'], # NOTE: telnetlib.py is part of Python core for tested 2.x and 3.x versions, not something 'pip' can download + keywords = ['pypi', 'cicd', 'python', 'nut', 'Network UPS Tools'], + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Operating System :: Unix", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows" + ] +) + diff --git a/scripts/python/module/test_nutclient.py.in b/scripts/python/module/test_nutclient.py.in index fbfd0249e5..c9b1431447 100755 --- a/scripts/python/module/test_nutclient.py.in +++ b/scripts/python/module/test_nutclient.py.in @@ -5,12 +5,22 @@ import PyNUT import sys +import os if __name__ == "__main__" : + NUT_HOST = os.getenv('NUT_HOST', '127.0.0.1') + NUT_PORT = int(os.getenv('NUT_PORT', '3493')) + NUT_USER = os.getenv('NUT_USER', None) + NUT_PASS = os.getenv('NUT_PASS', None) + + # Account "unexpected" failures (more due to coding than circumstances) + # e.g. lack of protected access when no credentials were passed is okay + failed = [] 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, host=NUT_HOST, port=NUT_PORT ) + #nut = PyNUT.PyNUTClient( login="upsadmin", password="upsadmin", debug=True, port=NUT_PORT ) print( 80*"-" + "\nTesting 'GetUPSList' :") result = nut.GetUPSList( ) @@ -24,6 +34,10 @@ if __name__ == "__main__" : result = nut.GetUPSVars( "dummy" ) print( "\033[01;33m%s\033[0m\n" % result ) + print( 80*"-" + "\nTesting 'CheckUPSAvailable' :") + result = nut.CheckUPSAvailable( "dummy" ) + print( "\033[01;33m%s\033[0m\n" % result ) + print( 80*"-" + "\nTesting 'GetUPSCommands' :") result = nut.GetUPSCommands( "dummy" ) print( "\033[01;33m%s\033[0m\n" % result ) @@ -35,13 +49,100 @@ if __name__ == "__main__" : print( 80*"-" + "\nTesting 'RunUPSCommand' (Test front panel) :") try : result = nut.RunUPSCommand( "UPS1", "test.panel.start" ) + if (NUT_USER is None): + raise AssertionError("Secure operation should have failed due to lack of credentials, but did not") except : - result = sys.exc_info()[1] + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + if (NUT_USER is None and ex == 'ERR USERNAME-REQUIRED'): + result = result + "\n(anticipated error: no credentials were provided)" + else: + if (ex != 'ERR CMD-NOT-SUPPORTED' and (NUT_USER is not None and ex != 'ERR ACCESS-DENIED') ): + result = result + "\nTEST-CASE FAILED" + failed.append('RunUPSCommand') print( "\033[01;33m%s\033[0m\n" % result ) print( 80*"-" + "\nTesting 'SetUPSVar' (set ups.id to test):") try : result = nut.SetRWVar( "UPS1", "ups.id", "test" ) + if (NUT_USER is None): + raise AssertionError("Secure operation should have failed due to lack of credentials, but did not") + except : + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + if (NUT_USER is None and ex == 'ERR USERNAME-REQUIRED'): + result = result + "\n(anticipated error: no credentials were provided)" + else: + if (ex != 'ERR VAR-NOT-SUPPORTED' and (NUT_USER is not None and ex != 'ERR ACCESS-DENIED') ): + result = result + "\nTEST-CASE FAILED" + failed.append('SetUPSVar') + print( "\033[01;33m%s\033[0m\n" % result ) + + # testing who has an upsmon-like log-in session to a device + print( 80*"-" + "\nTesting 'ListClients' for 'dummy' (should be registered in upsd.conf) before test client is connected :") + try : + result = nut.ListClients( "dummy" ) + except : + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + result = result + "\nTEST-CASE FAILED" + failed.append('ListClients-dummy-before') + print( "\033[01;33m%s\033[0m\n" % result ) + + print( 80*"-" + "\nTesting 'ListClients' for missing device (should raise an exception) :") + try : + result = nut.ListClients( "MissingBogusDummy" ) except : - result = sys.exc_info()[1] + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + if (ex == 'ERR UNKNOWN-UPS'): + result = result + "\n(anticipated error: bogus device name was tested)" + else: + result = result + "\nTEST-CASE FAILED" + failed.append('ListClients-MissingBogusDummy') print( "\033[01;33m%s\033[0m\n" % result ) + + loggedIntoDummy = False + print( 80*"-" + "\nTesting 'DeviceLogin' for 'dummy' (should be registered in upsd.conf; current credentials should have an upsmon role in upsd.users) :") + try : + result = nut.DeviceLogin( "dummy" ) + if (NUT_USER is None): + raise AssertionError("Secure operation should have failed due to lack of credentials, but did not") + loggedIntoDummy = True + except : + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + if (NUT_USER is None and ex == 'ERR USERNAME-REQUIRED'): + result = result + "\n(anticipated error: no credentials were provided)" + else: + if (NUT_USER is not None and ex != 'ERR ACCESS-DENIED'): + result = result + "\nTEST-CASE FAILED" + failed.append('DeviceLogin-dummy') + print( "\033[01;33m%s\033[0m\n" % result ) + + print( 80*"-" + "\nTesting 'ListClients' for None (should list all devices and sessions to them, if any -- e.g. one established above) :") + try : + result = nut.ListClients( ) + if (type(result) is not dict): + raise TypeError("ListClients() did not return a dict") + else: + if (loggedIntoDummy): + if (len(result) < 1): + raise ValueError("ListClients() returned an empty dict where at least one client was expected on b'dummy'") + if (type(result[b'dummy']) is not list): + raise TypeError("ListClients() did not return a dict whose b'dummy' keyed value is a list") + if (len(result[b'dummy']) < 1): + raise ValueError("ListClients() returned a dict where at least one client was expected on b'dummy' but none were reported") + except : + ex = str(sys.exc_info()[1]) + result = "EXCEPTION: " + ex + result = result + "\nTEST-CASE FAILED" + failed.append('ListClients-dummy-after') + print( "\033[01;33m%s\033[0m\n" % result ) + + print( 80*"-" + "\nTesting 'PyNUT' instance teardown (end of test script)" ) + # No more tests AFTER this line; add them above the teardown message + + if (len(failed) > 0): + print ( "SOME TEST CASES FAILED in an unexpected manner: %s" % failed ) + sys.exit(1) diff --git a/scripts/python/module/tox.ini b/scripts/python/module/tox.ini new file mode 100644 index 0000000000..f24499a631 --- /dev/null +++ b/scripts/python/module/tox.ini @@ -0,0 +1,27 @@ +# Configuration for Python test environment manager +# https://tox.wiki/en/latest/user_guide.html +# +# Note that to run `test_nutclient.py` you must prepare a running NUT data +# server (upsd) with a connected driver. You can use a dummy-ups driver for +# that, see e.g. NUT tests/NIT/nit.sh for how the test beds are prepared. +# Further you may need to export `NUT_HOST` and `NUT_PORT` (if not default), +# and a `NUT_USER` and `NUT_PASS` for tests with logged-in session behaviors. +# Then just run `tox` (may have to `pip install tox` first though). + +[tox] +envlist = + py2{6,7} + py3{5,5,6,7,8,9,10,11,12,13} + +[testenv] +setenv = + PYTHONPATH = {toxinidir}:{toxinidir}/PyNUTClient + +# On my system, some but not all Python versions complained about lack of +# "distutils.cmd" etc. in the prepared virtual environments. Can this help? +commands_pre = + python -m pip install -U pip + +commands = + python PyNUTClient/test_nutclient.py + #py.test --basetemp={envtmpdir} diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 29f59d3d4b..52b100ca84 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -1,16 +1,16 @@ -#!/bin/bash +#!/usr/bin/env bash # # 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.16-dmf # # See also: docs/snmp-subdrivers.txt # # Copyright (C) # 2011 - 2012 Arnaud Quette -# 2015 - 2019 Arnaud Quette -# 2011 Jim Klimov +# 2015 - 2022 Eaton (author: Arnaud Quette ) +# 2011 - 2024 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 @@ -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 @@ -204,16 +208,16 @@ generate_C() { #include "${HFILE}" - #define ${UDRIVER}_MIB_VERSION "0.1" + #define ${UDRIVER}_MIB_VERSION "0.01" #define ${UDRIVER}_SYSOID "${DEVICE_SYSOID}" /* To create a value lookup structure (as needed on the 2nd line of the example * below), use the following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { - * { 1, "OB" }, - * { 2, "OL" }, - * { 0, NULL } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ @@ -221,7 +225,7 @@ generate_C() { static snmp_info_t ${LDRIVER}_mib[] = { /* Data format: - * { info_type, info_flags, info_len, OID, dfl, flags, oid2info }, + * snmp_info_default(info_type, info_flags, info_len, OID, dfl, flags, oid2info), * * info_type: NUT INFO_ or CMD_ element name * info_flags: flags to set in addinfo @@ -232,26 +236,42 @@ generate_C() { * oid2info: lookup table between OID and NUT values * * Example: - * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, - * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info }, + * snmp_info_default("input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL), + * snmp_info_default("ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info), * * To create a value lookup structure (as needed on the 2nd line), use the * following kind of declaration, outside of the present snmp_info_t[]: * static info_lkp_t onbatt_info[] = { - * { 1, "OB" }, - * { 2, "OL" }, - * { 0, NULL } + * info_lkp_default(1, "OB"), + * info_lkp_default(2, "OL"), + * info_lkp_sentinel * }; */ + + /* 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,92 +280,97 @@ generate_C() { SU_INFOSIZE="1" fi # get the matching numeric OID - NUM_OID="`sed -n ${LINENB}p ${NUMWALKFILE} | cut -d' ' -f1`" - printf "\t/* ${FULL_STR_OID} */\n\t{ \"unmapped.${STR_OID}\", ${ST_FLAG_TYPE}, ${SU_INFOSIZE}, \"${NUM_OID}\", NULL, SU_FLAG_OK, NULL },\n" - done < ${STRWALKFILE} >> ${CFILE} + NUM_OID="`sed -n "${LINENB}p" "${NUMWALKFILE}" | cut -d' ' -f1`" + printf "\t/* ${FULL_STR_OID} */\n\tsnmp_info_default(\"unmapped.${STR_OID}\", ${ST_FLAG_TYPE}, ${SU_INFOSIZE}, \"${NUM_OID}\", NULL, SU_FLAG_OK, NULL),\n" + done < "${STRWALKFILE}" >> "${CFILE}" - # append footer - cat >> "$CFILE" <<-EOF + # append footer (TABs not stripped): + cat >> "$CFILE" < replace - by _ + # but replace with underscores for the structures and defines + LDRIVER="`echo "$LDRIVER" | tr - _`" + UDRIVER="`echo "$UDRIVER" | tr - _`" # generate DMF file echo "Creating $DMFFILE" - printf "\n" > ${DMFFILE} - printf "\n" >> ${DMFFILE} - printf "\n\n\t\n" >> ${DMFFILE} - printf "\t\t\n" >> ${DMFFILE} - printf "\t\t\n\n" >> ${DMFFILE} - printf "\t\t\n" >> ${DMFFILE} - - printf "\n\t\t\n" >> ${DMFFILE} + printf "\n" > "${DMFFILE}" + printf "\n" >> "${DMFFILE}" + printf "\n\n\t\n" >> "${DMFFILE}" + printf "\t\t\n" >> "${DMFFILE}" + printf "\t\t\n\n" >> "${DMFFILE}" + printf "\t\t\n" >> "${DMFFILE}" + + printf "\n\t\t\n" >> "${DMFFILE}" # extract OID string paths, one by one LINENB="0" while IFS= read -r line; do LINENB="`expr $LINENB + 1`" FULL_STR_OID="$line" - STR_OID="`echo $line | cut -d'.' -f1`" + STR_OID="`echo "$line" | cut -d'.' -f1`" echo $line | grep STRING > /dev/null if [ $? -eq 0 ]; then ST_FLAG_TYPE="ST_FLAG_STRING" @@ -355,27 +380,27 @@ generate_DMF() { 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\t\n\t\t\n" - done < ${STRWALKFILE} >> ${DMFFILE} + done < "${STRWALKFILE}" >> "${DMFFILE}" # append footer # FIXME: missing license field in mib2nut - printf "\t\n\t\n" >> "$DMFFILE" - - printf "\n\t\n" >> ${DMFFILE} - printf "\t\n" >> ${DMFFILE} + printf "\t\n\t\n" >> "$DMFFILE" + + printf "\n\t\n" >> "${DMFFILE}" + printf "\t\n" >> "${DMFFILE}" printf "\n" >> "$DMFFILE" @@ -430,8 +455,8 @@ if [ -z "$NUMWALKFILE" ]; then # mode 1: directly get SNMP data from a real agent echo "Mode 1 selected" MODE=1 - NUMWALKFILE=$DFL_NUMWALKFILE - STRWALKFILE=$DFL_STRWALKFILE + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" # check if Net SNMP is available if [ -z "`command -v snmpget`" -o -z "`command -v snmpwalk`" ] && \ @@ -443,8 +468,8 @@ if [ -z "$NUMWALKFILE" ]; then while [ -z "$HOSTNAME" ]; do printf "\n\tPlease enter the SNMP host IP address or name.\n" read -p "SNMP host IP name or address: " HOSTNAME < /dev/tty - if echo $HOSTNAME | egrep -q '[^a-zA-Z0-9]'; then - echo "Please use only letters and digits" + if echo "$HOSTNAME" | grep -E -q '[^a-zA-Z0-9.-]'; then + echo "Please use only letters, digits, dash and period character" HOSTNAME="" fi done @@ -459,9 +484,9 @@ else # then use snmptranslate to get the string OIDs and generated the string SNMP walk echo "Mode 3 selected" MODE=3 - RAWWALKFILE=$NUMWALKFILE - NUMWALKFILE=$DFL_NUMWALKFILE - STRWALKFILE=$DFL_STRWALKFILE + RAWWALKFILE="$NUMWALKFILE" + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" # check for actual file existence if [ ! -f "$RAWWALKFILE" ]; then @@ -470,7 +495,7 @@ else fi # Extract the sysOID # Format is "1.3.6.1.2.1.1.2.0 = OID: 1.3.6.1.4.1.4555.1.1.1" - DEVICE_SYSOID=`grep 1.3.6.1.2.1.1.2.0 $RAWWALKFILE | cut -d' ' -f4` + DEVICE_SYSOID="`grep 1.3.6.1.2.1.1.2.0 "$RAWWALKFILE" | cut -d' ' -f4`" if [ -n "$DEVICE_SYSOID" ]; then echo "Found sysOID $DEVICE_SYSOID" else @@ -481,16 +506,19 @@ else # Switch to the entry point, and extract the subtree # Extract the numeric walk echo -n "Extracting numeric SNMP walk..." - grep $DEVICE_SYSOID $RAWWALKFILE | egrep -v "1.3.6.1.2.1.1.2.0" 2>/dev/null 1> $NUMWALKFILE + grep "$DEVICE_SYSOID" "$RAWWALKFILE" | grep -E -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 +531,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" | grep -E -q '[^0-9.]'; then echo "Please use only the numeric form, with dots and digits" SYSOID="" fi @@ -530,23 +558,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" | grep -E -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} +grep -E -e "^[[:space:]]?$" -e "End of MIB" -v "${NUMWALKFILE}" > "${TMP_NUMWALKFILE}" +grep -E -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/subdriver/gen-usbhid-subdriver.sh b/scripts/subdriver/gen-usbhid-subdriver.sh index 956891eb1f..75f3ec269b 100755 --- a/scripts/subdriver/gen-usbhid-subdriver.sh +++ b/scripts/subdriver/gen-usbhid-subdriver.sh @@ -1,21 +1,24 @@ -#!/bin/bash +#!/usr/bin/env bash # an auxiliary script to produce a "stub" usbhid-ups subdriver from # the output of # -# drivers/usbhid-ups -DD -u root -x generic -x vendorid=XXXX auto +# drivers/usbhid-ups -s ups -DD -u root -x explore -x vendorid=XXXX -x productid=XXXX -x port=auto -d1 > debuginfo 2>&1 # # Usage: cat debuginfo | gen-usbhid-subdriver.sh # # See also: docs/hid-subdrivers.txt usage() { - echo "Usage: $0 [options] [file]" + echo "Usage: $0 [options] [file] < debuginfo" + echo "with data prepared by a driver walk:" + echo " drivers/usbhid-ups -s ups -DD -u root -x vendorid=XXXX -x productid=XXXX \\" + echo " -x port=auto -x explore -d1 > debuginfo 2>&1" echo "Options:" echo " -h, --help -- show this message and quit" echo " -n name -- driver name (use natural capitalization)" - echo " -v XXXX -- vendor id" - echo " -p XXXX -- product id" + echo " -v XXXX -- vendor id (learned from debuginfo by default)" + echo " -p XXXX -- product id (learned from debuginfo by default)" echo " -k -- keep temporary files (for debugging)" echo " file -- read from file instead of stdin" } @@ -79,15 +82,15 @@ 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 | grep -E -q '[^a-zA-Z0-9]'; then echo "Please use only letters and digits" DRIVER="" fi done -# try to determine product and vendor id -VENDORID=`cat "$FILE" | sed -n 's/.*- VendorID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` -PRODUCTID=`cat "$FILE" | sed -n 's/.*- ProductID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` +# try to determine product and vendor id, if not specified by user +[ -n "$VENDORID" ] || VENDORID=`cat "$FILE" | sed -n 's/.*- VendorID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` +[ -n "$PRODUCTID" ] || PRODUCTID=`cat "$FILE" | sed -n 's/.*- ProductID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` # prompt for productid, vendorid if necessary if [ -z "$VENDORID" ]; then @@ -110,7 +113,7 @@ cat "$UTABLE" | tr '.' $'\n' | sort -u > "$USAGES" # make up dummy names for unknown usages count=0 -cat "$USAGES" | egrep '[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]' |\ +cat "$USAGES" | grep -E '[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]' |\ while read U; do count=`expr $count + 1` echo "$U $UDRIVER$count" @@ -236,6 +239,10 @@ static usage_tables_t ${LDRIVER}_utab[] = { static hid_info_t ${LDRIVER}_hid2nut[] = { +/* Please revise values discovered by data walk for mappings to + * docs/nut-names.txt and group the rest under the ifdef below: + */ +#if WITH_UNMAPPED_DATA_POINTS EOF cat "$NEWUTABLE" | sort -u | while read U; do @@ -246,6 +253,7 @@ EOF done cat >> "$CFILE" < -Updated 2016-2018 by Michal Hrusecky and Jim Klimov - diff --git a/scripts/systemd/README.adoc b/scripts/systemd/README.adoc new file mode 100644 index 0000000000..8c66601dce --- /dev/null +++ b/scripts/systemd/README.adoc @@ -0,0 +1,34 @@ +NUT resources for systemd integration +===================================== + +Overview +-------- + +This directory contains the NUT support files for `systemd`, the System and +Service Manager used in many Linux distributions. + +These files are automatically installed, upon detection (at `configure` time) +of a systemd enabled target system, or if corresponding configuration options +were requested. + +This also uses the `nut-driver-enumerator.sh` (service and implementation +method) and `upsdrvsvcctl` (tool) to manage NUT drivers as service instances +located in `../upsdrvsvcctl/` source subdirectory. + +Two service bundles are provided for this: + +* a set of `nut-driver-enumerator-daemon*` units starts the script as a + daemon to regularly inspect and apply the NUT configuration to OS service + unit wrappings (mainly intended for monitoring systems with a dynamic + set of monitored power devices, or for systems where filesystem events + monitoring is not a clockwork-reliable mechanism to 100% rely on); +* the other `nut-driver-enumerator.*` units run the script once per triggering + of the service (usually during boot-up; configuration file changes can be + detected and propagated by systemd most of the time). + +Credits +------- + +* Contributed by Michal Hlavinka +* Updated 2016-2018 by Michal Hrusecky and Jim Klimov +* Maintained since 2020 by Jim Klimov diff --git a/scripts/systemd/nut-driver-enumerator-daemon-activator.path.in b/scripts/systemd/nut-driver-enumerator-daemon-activator.path.in index d02f0d43ec..02070bafba 100644 --- a/scripts/systemd/nut-driver-enumerator-daemon-activator.path.in +++ b/scripts/systemd/nut-driver-enumerator-daemon-activator.path.in @@ -1,8 +1,14 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2024 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + +[Unit] # Trigger restart of nut-driver-enumerator-daemon-activator.service # whenever ups.conf is edited, which would cause reload-or-restart # of the nut-driver-enumerator-daemon.service for actual reconfig. -[Unit] -Description=Network UPS Tools - Trigger restart of nut-driver-enumerator-daemon.service whenever ups.conf is edited +Description=Network UPS Tools - Trigger reload-or-restart of nut-driver-enumerator-daemon.service whenever ups.conf is edited Conflicts=nut-driver-enumerator.service nut-driver-enumerator.path After=local-fs.target Before=nut-driver.target diff --git a/scripts/systemd/nut-driver-enumerator-daemon-activator.service.in b/scripts/systemd/nut-driver-enumerator-daemon-activator.service.in index e114d42ac3..c7af5be654 100644 --- a/scripts/systemd/nut-driver-enumerator-daemon-activator.service.in +++ b/scripts/systemd/nut-driver-enumerator-daemon-activator.service.in @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] # This unit starts via nut-driver-enumerator-daemon-activator.path # activation due to changes in ups.conf file, and triggers a reload diff --git a/scripts/systemd/nut-driver-enumerator-daemon.service.in b/scripts/systemd/nut-driver-enumerator-daemon.service.in index db3aacbc7e..af13ab4e29 100644 --- a/scripts/systemd/nut-driver-enumerator-daemon.service.in +++ b/scripts/systemd/nut-driver-enumerator-daemon.service.in @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] # This unit starts early in system lifecycle to set up nut-driver instances # and stays running as a loop to pick up any subsequent changes maybe more diff --git a/scripts/systemd/nut-driver-enumerator.path.in b/scripts/systemd/nut-driver-enumerator.path.in index b1326b44ac..09b9fdddb4 100644 --- a/scripts/systemd/nut-driver-enumerator.path.in +++ b/scripts/systemd/nut-driver-enumerator.path.in @@ -1,4 +1,11 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] +# Trigger restart of nut-driver-enumerator.service whenever ups.conf is edited Description=Network UPS Tools - Trigger restart of nut-driver-enumerator.service whenever ups.conf is edited Conflicts=nut-driver-enumerator-daemon.service nut-driver-enumerator-daemon-activator.path nut-driver-enumerator-daemon-activator.service After=local-fs.target diff --git a/scripts/systemd/nut-driver-enumerator.service.in b/scripts/systemd/nut-driver-enumerator.service.in index e68f911fc0..bab6feec95 100644 --- a/scripts/systemd/nut-driver-enumerator.service.in +++ b/scripts/systemd/nut-driver-enumerator.service.in @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] # This unit starts early in system lifecycle to set up nut-driver instances. # End-user may also restart this unit after editing ups.conf to automatically @@ -24,6 +30,7 @@ Type=oneshot # don't want it to fail the unit (when it can't restart). Environment=REPORT_RESTART_42=no EnvironmentFile=-@CONFPATH@/nut.conf +ExecStartPre=-@SYSTEMD_TMPFILES_PROGRAM@ --create @systemdtmpfilesdir@/nut-common-tmpfiles.conf ExecStart=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh ExecReload=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh diff --git a/scripts/systemd/nut-driver.target b/scripts/systemd/nut-driver.target index d994ab9882..0055e469d4 100644 --- a/scripts/systemd/nut-driver.target +++ b/scripts/systemd/nut-driver.target @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] Description=Network UPS Tools - target for power device drivers on this system After=local-fs.target diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in index f5db0fbb43..1a1a04a4f0 100644 --- a/scripts/systemd/nut-driver@.service.in +++ b/scripts/systemd/nut-driver@.service.in @@ -1,7 +1,26 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [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 @@ -14,8 +33,10 @@ PartOf=nut-driver.target # and typically by the time the box gets an IP address, the driver # is still retrying to start and will succeed. # Extending the unit does not require *this* file to be edited, you -# can instead drop in an additional piece of configuration, e.g. add -# a `/etc/systemd/system/nut-driver@.service.d/network.conf` with: +# can instead drop in an additional piece of configuration, e.g. to +# require that a NUT driver only starts after external networking +# is configured (usable IP addresses appear in the system) you can +# add a `/etc/systemd/system/nut-driver@.service.d/network.conf` with: # [Unit] # Requires=network-online.target # After=network-online.target @@ -25,13 +46,34 @@ PartOf=nut-driver.target # Finally note that "nut-driver-enumerator.service" should take care of this. [Service] +Environment=NUT_IGNORE_NOWAIT=true 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"' +ExecStartPre=-@SYSTEMD_TMPFILES_PROGRAM@ --create @systemdtmpfilesdir@/nut-common-tmpfiles.conf +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 ; } ; exec @SBINDIR@/upsdrvctl @SYSTEMD_DAEMON_ARGS_DRIVER@ start "$NUTDEV"' +# SIGHUP: simple reload (ignore values we can not change on the fly) +# SIGUSR1: reload-or-exit (let systemd resuscitate the service then) +ExecReload=/bin/kill -USR1 $MAINPID 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"' -Type=forking -Restart=on-failure -RestartSec=5min +# 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 rapidly 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=@SYSTEMD_DAEMON_TYPE_DRIVER@ +@SYSTEMD_DAEMON_NOTIFYACCESS_DRIVER@ +# Note: if you set up with systemd notification support, you can take +# advantage of watchdog mechanism. Timeouts involved are deployment +# dependent (how many devices you monitor, how stressed are they and +# the monitoring system, protocol used -- e.g. SNMP walks can take long), +# so distributions should not pre-define this (at least not to a small +# value): +@SYSTEMD_DAEMON_WATCHDOG_DRIVER@ # Note: If you customize the "maxstartdelay" in ups.conf or in your # NUT compilation defaults, so it exceeds the default systemd unit # startup timeout (typically 90 sec), then make sure to set a slightly diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 74394f1af0..25747fd30b 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -1,5 +1,12 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] Description=Network UPS Tools - power device monitor and shutdown controller +# Note: do not mistake this historic misnomer for "NUT-Monitor" Python GUI client After=local-fs.target network.target nut-server.service # Note: We do not specify Requires nut-server.service because # the `upsd` daemon(s) may be running on a different machine @@ -8,8 +15,10 @@ After=local-fs.target network.target nut-server.service # would not abort if that attempt fails for whatever reason. Wants=nut-server.service # Extending the unit does not require *this* file to be edited, you -# can instead drop in an additional piece of configuration, e.g. add -# a `/etc/systemd/system/nut-monitor.service.d/network.conf` with: +# can instead drop in an additional piece of configuration, e.g. to +# require the monitoring client to only start after external networking +# is configured (usable IP addresses appear in the system) you can +# add a `/etc/systemd/system/nut-monitor.service.d/network.conf` with: # [Unit] # Requires=network-online.target # After=network-online.target @@ -18,9 +27,19 @@ PartOf=nut.target [Service] EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsmon +ExecStartPre=-@SYSTEMD_TMPFILES_PROGRAM@ --create @systemdtmpfilesdir@/nut-common-tmpfiles.conf +ExecStart=@SBINDIR@/upsmon @SYSTEMD_DAEMON_ARGS_UPSMON@ +ExecReload=@SBINDIR@/upsmon -c reload PIDFile=@PIDPATH@/upsmon.pid -Type=forking +# If "-FF" or background-daemon mode is used, so that PID file exists +# and "upsmon -c stop" in particular can be used from command-line or +# legacy scripts, it causes a clean and intentional exit of the daemon. +# Then systemd should not revive it - hence restart it only on failure: +Restart=on-failure +Type=@SYSTEMD_DAEMON_TYPE_UPSMON@ +@SYSTEMD_DAEMON_WATCHDOG_UPSMON@ +@SYSTEMD_DAEMON_NOTIFYACCESS_UPSMON@ [Install] WantedBy=nut.target +Alias=upsmon.service diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index f9e4c6f88a..c153c5d307 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] Description=Network UPS Tools - power devices information server After=local-fs.target network.target nut-driver.target @@ -8,8 +14,10 @@ Wants=nut-driver.target # The `upsd` is a networked service (even if bound to a `localhost`) # so it requires that the OS has some notion of networking already. # Extending the unit does not require *this* file to be edited, you -# can instead drop in an additional piece of configuration, e.g. add -# a `/etc/systemd/system/nut-server.service.d/network.conf` with: +# can instead drop in an additional piece of configuration, e.g. to +# require that NUT data server only starts after external networking +# is configured (usable IP addresses appear in the system) you can +# add a `/etc/systemd/system/nut-server.service.d/network.conf` with: # [Unit] # Requires=network-online.target # After=network-online.target @@ -35,10 +43,24 @@ PartOf=nut.target LimitNOFILE=1048576 EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsd -ExecReload=@SBINDIR@/upsd -c reload +# Note: foreground mode "-F" by default skips writing a PID file (and +# needs default Type=simple); we can use "-FF" here to create the file +# anyway, so that old "upsd -c reload" works rather than systemd action: +ExecStartPre=-@SYSTEMD_TMPFILES_PROGRAM@ --create @systemdtmpfilesdir@/nut-common-tmpfiles.conf +ExecStart=@SBINDIR@/upsd @SYSTEMD_DAEMON_ARGS_UPSD@ +ExecReload=@SBINDIR@/upsd -c reload -P $MAINPID ExecStartPost=-/bin/grep -E 'Units|Max open files' /proc/${MAINPID}/limits -Type=forking +# No tracking for PIDFile path and service attribute here (it might not +# even exist). +# If "-FF" or background-daemon mode is used, so that PID file exists +# and "upsd -c stop" in particular can be used from command-line or +# legacy scripts, it causes a clean and intentional exit of the daemon. +# Then systemd should not revive it - hence restart it only on failure: +Restart=on-failure +Type=@SYSTEMD_DAEMON_TYPE_UPSD@ +@SYSTEMD_DAEMON_WATCHDOG_UPSD@ +@SYSTEMD_DAEMON_NOTIFYACCESS_UPSD@ [Install] WantedBy=nut.target +Alias=upsd.service diff --git a/scripts/systemd/nut.target b/scripts/systemd/nut.target index f744d29c55..3974fed4bf 100644 --- a/scripts/systemd/nut.target +++ b/scripts/systemd/nut.target @@ -1,3 +1,9 @@ +# Network UPS Tools (NUT) systemd integration +# Copyright (C) 2011-2023 by NUT contirbutors +# Distributed under the terms of GPLv2+ +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ + [Unit] Description=Network UPS Tools - target for power device drivers, data server and monitoring client (if enabled) on this system After=local-fs.target nut-driver.target nut-server.service nut-monitor.service diff --git a/scripts/systemd/nutshutdown.in b/scripts/systemd/nutshutdown.in index ace2485b35..8625e167d9 100755 --- a/scripts/systemd/nutshutdown.in +++ b/scripts/systemd/nutshutdown.in @@ -1,23 +1,89 @@ #!/bin/sh -# This script requires both nut-server (drivers) -# and nut-client (upsmon) to be present locally -# and on mounted filesystems -[ -x "@SBINDIR@/upsmon" ] && [ -x "@SBINDIR@/upsdrvctl" ] || exit +# Network UPS Tools (NUT) systemd-shutdown integration handler. +# +# NOTE: This script requires both nut-server package (or more specifically, +# the drivers for your device, which may be in further packages grouped +# by media/protocol and third-party dependencies), nut-client (upsmon), +# and their configuration files to be present locally and on still-mounted +# filesystems (may be read-only). +# +# Copyright (C) 2011-2023 by NUT contirbutors +# Michal Hlavinka, Laurent Bigonville, Arnaud Quette, Jim Klimov et al. +# +# See https://networkupstools.org/ +# and https://github.com/networkupstools/nut/ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +if [ -s "@CONFPATH@/nut.conf" ]; then + . "@CONFPATH@/nut.conf" || true +fi + +[ x"${POWEROFF_QUIET-}" = xtrue ] \ +|| POWEROFF_QUIET="false" + +[ -x "@SBINDIR@/upsmon" ] || { + $POWEROFF_QUIET || echo "$0: SKIP: could not locate '@SBINDIR@/upsmon''" >&2 + exit 1 +} + +# Will at most run the optional power-race avoidance sleep part +[ -x "@SBINDIR@/upsdrvctl" ] || { + $POWEROFF_QUIET || echo "$0: WARNING: could not locate '@SBINDIR@/upsdrvctl' - will not command any UPS devices to shut down" >&2 +} if @SBINDIR@/upsmon -K >/dev/null 2>&1; then - # The argument may be anything compatible with sleep - # (not necessarily a non-negative integer) - wait_delay="`/bin/sed -ne 's#^ *POWEROFF_WAIT= *\(.*\)$#\1#p' @CONFPATH@/nut.conf`" || wait_delay="" - - @SBINDIR@/upsdrvctl shutdown - - if [ -n "$wait_delay" ] ; then - /bin/sleep $wait_delay - # We need to pass --force twice here to bypass systemd and execute the - # reboot directly ourself. - /bin/systemctl reboot --force --force - fi + if [ -x "@SBINDIR@/upsdrvctl" ] ; then + $POWEROFF_QUIET || { + echo "$0: Commanding UPSes (if any) to shutdown" >&2 + [ -w /dev/console ] && echo "`TZ=UTC LANG=C date`: $0: Commanding UPSes (if any) to shutdown" >/dev/console || true + } + @SBINDIR@/upsdrvctl shutdown || echo "$0: Something failed about UPS shutdown commands" >&2 + fi + + if [ -n "$POWEROFF_WAIT" ] ; then + # Avoid the power-race condition (if wall power returned + # while we were shutting down, so some UPSes would not + # shutdown and/or powercycle the load as commanded above). + # Sleep "long enough" to drain the battery if the UPS is + # in fact on battery, or reboot if it became alive, so + # this computer is not in limbo forever. + $POWEROFF_QUIET || { + echo "$0: Power-race avoidance: sleeping $POWEROFF_WAIT" >&2 + [ -w /dev/console ] && echo "`TZ=UTC LANG=C date`: $0: Power-race avoidance: sleeping $POWEROFF_WAIT" >/dev/console || true + } + + # The argument may be anything compatible with /bin/sleep + # (on OSes with systemd - assuming GNU coreutils or compatible, + # so not necessarily a non-negative integer) + /bin/sleep $POWEROFF_WAIT + + $POWEROFF_QUIET || { + echo "$0: Power-race avoidance: sleep finished, rebooting..." >&2 + [ -w /dev/console ] && echo "`TZ=UTC LANG=C date`: $0: Power-race avoidance: sleep finished, rebooting..." >/dev/console || true + } + + # We need to pass --force twice here to bypass systemd + # and execute the reboot directly ourself. + /bin/systemctl reboot --force --force + else + $POWEROFF_QUIET || echo "$0: Power-race avoidance: POWEROFF_WAIT is not configured at this time, proceeding to shutdown" >&2 + fi +else + $POWEROFF_QUIET || echo "$0: SKIP: Not in FSD (killpower) mode at this time" >&2 fi exit 0 diff --git a/scripts/udev/Makefile.am b/scripts/udev/Makefile.am index 5d299a711d..7a6e13fcd6 100644 --- a/scripts/udev/Makefile.am +++ b/scripts/udev/Makefile.am @@ -1,3 +1,4 @@ +# Network UPS Tools: scripts/udev if WITH_UDEV udevrulesdir = $(udevdir)/rules.d @@ -10,7 +11,7 @@ if WITH_IPMI endif endif -EXTRA_DIST = README +EXTRA_DIST = README.adoc 62-nut-usbups.rules: nut-usbups.rules cp nut-usbups.rules $@ @@ -25,10 +26,36 @@ DISTCLEANFILES = nut-usbups.rules nut-ipmipsu.rules CLEANFILES = 62-nut-usbups.rules 52-nut-ipmipsu.rules -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = nut-usbups.rules.in - +# We should never remove this one, apart from a distclean-check +# or stronger... # Generated by autogen.sh and needed to run the configure script # (technically, generated by tools/nut-usbinfo.pl script among # GENERATED_USB_OS_FILES): MAINTAINERCLEANFILES += nut-usbups.rules.in nut-usbups.rules.in.AUTOGEN_WITHOUT + +# Part of dist tarball, regardless of use for current build: +EXTRA_DIST += nut-usbups.rules.in + +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFILE) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFILE) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFILE) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFILE) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES += *-spellchecked diff --git a/scripts/udev/README b/scripts/udev/README deleted file mode 100644 index ff6a1a0978..0000000000 --- a/scripts/udev/README +++ /dev/null @@ -1,44 +0,0 @@ -Desc: Udev script for NUT USB and IPMI drivers -File: scripts/udev/README -Date: 31 July 2014 -Auth: Arnaud Quette - -This document introduces the Linux udev script for NUT USB -drivers (usbhid-ups, bcmxcp_usb, tripplite_usb, ...) and IPMI driver -(nut-ipmipsu). - -These are needed on Linux systems running udev (recommended -as of kernel 2.6.3, and mandatory as of 2.6.14 and higher). - -This script ensure that the right privileges are set on the -USB and IPMI devices files to allow the NUT driver to operate (ie -allowing the nut user to read AND write to the device). - -Note that the old style hotplug files, available in the -scripts/hotplug directory, are not needed if your kernel supports -udev. - -Installation ------------- - -For most users, these files will be automatically installed in -/etc/udev (or /lib/udev) upon "make install", if that directory exists and if -the feature (USB and / or IPMI) has been enabled at configure time. You can -specify an alternate directory with ./configure --with-udev-dir=DIR. - -Manual installation -------------------- - -To install them manually, copy the rules file(s) to /etc/udev/rules.d -(or /lib/udev/rules.d on newer systems) using the command(s): - -$ cp -f nut-usbups.rules /etc/udev/rules.d/62-nut-usbups.rules -$ cp -f nut-ipmipsu.rules /etc/udev/rules.d/52-nut-ipmipsu.rules - -You will need to refresh the bus to avoid a reboot for these rules to be -active. You can do so using: - -$ udevadm trigger --subsystem-match=usb_device - -For USB devices, you can then plug your UPS USB cord, or unplug / replug -it to refresh the device permission, and start NUT. diff --git a/scripts/udev/README.adoc b/scripts/udev/README.adoc new file mode 100644 index 0000000000..62b503b003 --- /dev/null +++ b/scripts/udev/README.adoc @@ -0,0 +1,49 @@ +Udev script for NUT USB and IPMI drivers +======================================== +Arnaud Quette +v1.0, 31 July 2014 (start date) + +This document introduces the Linux `udev` script for NUT USB drivers +(`usbhid-ups`, `nutdrv_qx`, `bcmxcp_usb`, `tripplite_usb`, ...) and +IPMI driver (`nut-ipmipsu`). + +These are needed on Linux systems running `udev` (recommended as of Linux +kernel 2.6.3, and mandatory as of 2.6.14 and higher). + +This script ensures that the right privileges are set on the USB and IPMI +device nodes to allow the NUT driver to operate (i.e. allowing the `nut` +user to read AND write to the hardware device). + +Note that the old style `hotplug` files, available in the `scripts/hotplug` +directory, are not needed anymore if your kernel supports `udev`. + +Installation +------------ + +For most users, these files will be automatically installed in `/etc/udev` +(or `/lib/udev`) upon `make install`, if that directory exists and if the +feature (USB and/or IPMI driver support) has been enabled at `configure` time. + +You can specify an alternate directory with: +---- +:; ./configure --with-udev-dir=DIR +---- + +Manual installation +------------------- + +To install them manually, copy the rules file(s) to `/etc/udev/rules.d` +(or `/lib/udev/rules.d` on newer systems) using the following command(s): +---- +:; cp -f nut-usbups.rules /etc/udev/rules.d/62-nut-usbups.rules +:; cp -f nut-ipmipsu.rules /etc/udev/rules.d/52-nut-ipmipsu.rules +---- + +You will need to refresh the bus to avoid a reboot for these rules to be +active. You can do so using: +---- +:; udevadm trigger --subsystem-match=usb_device +---- + +For USB devices, you can then plug your UPS USB cord, or unplug/re-plug +it to refresh the device permission, and start NUT. diff --git a/scripts/ufw/.gitignore b/scripts/ufw/.gitignore index c56f2fa8f4..999163b622 100644 --- a/scripts/ufw/.gitignore +++ b/scripts/ufw/.gitignore @@ -1 +1,2 @@ /nut.ufw.profile +/README diff --git a/scripts/ufw/Makefile.am b/scripts/ufw/Makefile.am index dc72ff3b89..24cc6e15bb 100644 --- a/scripts/ufw/Makefile.am +++ b/scripts/ufw/Makefile.am @@ -1,2 +1,10 @@ +# Network UPS Tools: scripts/ufw + +EXTRA_DIST = README.adoc nut.ufw.profile.in + +# Note: spellchecking is currently ensured by docs/Makefile.am +# due to inclusion into docs/security.txt, and the heading level +# is also dictated by that. + MAINTAINERCLEANFILES = Makefile.in .dirstamp -CLEANFILES = *-spellchecked +CLEANFILES = *-spellchecked README diff --git a/scripts/ufw/README b/scripts/ufw/README.adoc similarity index 99% rename from scripts/ufw/README rename to scripts/ufw/README.adoc index 701f32386d..5400fe13e8 100644 --- a/scripts/ufw/README +++ b/scripts/ufw/README.adoc @@ -26,5 +26,3 @@ For more information, refer to: - link:https://launchpad.net/ufw[UFW project page], - link:https://wiki.ubuntu.com/UncomplicatedFirewall[UFW wiki], - UFW manual page, section APPLICATION INTEGRATION - - diff --git a/scripts/upower/95-upower-hid.hwdb b/scripts/upower/95-upower-hid.hwdb new file mode 100644 index 0000000000..41da8c059f --- /dev/null +++ b/scripts/upower/95-upower-hid.hwdb @@ -0,0 +1,208 @@ +############################################################################################################## +# 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* +usb:v0483pA430* + 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* +usb:v051Dp0004* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=APC + +# Powerware +usb:v0592p0004* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Powerware + +# Delta UPS +usb:v05DDp041B* +usb:v05DDpA011* +usb:v05DDpA0A0* + 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:v10AFp0000* +usb:v10AFp0001* +usb:v10AFp0002* +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:v2E66p0101* +usb:v2E66p0201* +usb:v2E66p0202* +usb:v2E66p0203* +usb:v2E66p0300* +usb:v2E66p0302* + 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/scripts/upsdrvsvcctl/Makefile.am b/scripts/upsdrvsvcctl/Makefile.am index 77313cae94..03adf8e1c4 100644 --- a/scripts/upsdrvsvcctl/Makefile.am +++ b/scripts/upsdrvsvcctl/Makefile.am @@ -1,4 +1,6 @@ -EXTRA_DIST = README +# Network UPS Tools: scripts/upsdrvsvcctl + +EXTRA_DIST = README.adoc if HAVE_SYSTEMD EXTRA_DIST += nut-driver-enumerator.sh upsdrvsvcctl @@ -10,4 +12,28 @@ endif EXTRA_DIST += nut-driver-enumerator.sh.in upsdrvsvcctl.in +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked + MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/upsdrvsvcctl/README b/scripts/upsdrvsvcctl/README deleted file mode 100644 index b2a9db64a8..0000000000 --- a/scripts/upsdrvsvcctl/README +++ /dev/null @@ -1,11 +0,0 @@ -This directory contains the shared NUT support files for Linux systemd (the -System and Service Manager) and Solaris SMF (Service Management Framework). -It includes the nut-driver-enumerator.sh (service and implementation method) -and upsdrvsvcctl (tool) to manage NUT drivers as service instances. - -These files are automatically installed into SBINDIR/upsdrvsvcctl and -LIBEXECDIR/nut-driver-enumerator.sh, upon detection (at configure time) -of a systemd or SMF enabled system, with Makefiles of the ../systemd/ and -../Solaris/ source directories respectively. - -Contributed 2016-2018 by Jim Klimov diff --git a/scripts/upsdrvsvcctl/README.adoc b/scripts/upsdrvsvcctl/README.adoc new file mode 100644 index 0000000000..cd596428ad --- /dev/null +++ b/scripts/upsdrvsvcctl/README.adoc @@ -0,0 +1,16 @@ +Shared resources for NUT service unit integrations +================================================== + +This directory contains the shared NUT support files for Linux systemd (the +System and Service Manager) and Solaris SMF (Service Management Framework). +It includes the `nut-driver-enumerator.sh` (service and implementation method) +and `upsdrvsvcctl` (tool) to manage NUT drivers as service instances. + +These files are automatically installed into `SBINDIR/upsdrvsvcctl` and +`LIBEXECDIR/nut-driver-enumerator.sh`, upon detection (at `configure` time) +of a systemd or SMF enabled system, with Makefiles of the `../systemd/` and +`../Solaris/` source directories respectively. + +Contributed 2016-2018 by Jim Klimov + +Maintained since 2020 by Jim Klimov diff --git a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in old mode 100755 new mode 100644 index 68e5e61afb..7b13dc4eec --- a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in +++ b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in @@ -1,14 +1,19 @@ #!/bin/sh +# BIG NOTE: Not bash, not any other predetermined shell implementation # # NOTE: This script is intentionally written with portable shell constructs # with the aim and hope to work in different interpreters, so it is a # bit dumber and less efficient than could be achieved with the more -# featured shells in the spectrum. +# featured shells in the spectrum. Also, to minimize the in-memory and +# debug-console traffics, tests for (non-)emptiness of anticipated large +# strings are not done by `test -n/-z`, but by counting the size of the +# string (zero or not). # NOTE ALSO: The configuration parser in this script is not meant to be a # reference or 100% compliant with what the binary code uses; its aim # is to just pick out some strings relevant for tracking config changes. # # Copyright (C) 2016-2020 Eaton +# Copyright (C) 2020-2024 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 @@ -92,7 +97,7 @@ if [ -n "$ZSH_VERSION" ]; then # IFS="`printf ' \t\r\n'`" ; export IFS fi -if set | egrep '^(shell|version|t?csh)' | egrep 't?csh' >/dev/null ; then +if set | grep -E '^(shell|version|t?csh)' | grep -E 't?csh' >/dev/null ; then echo "FATAL: csh or tcsh is not supported in this script" >&2 exit 1 fi @@ -170,7 +175,7 @@ fi # Cache needed bits of ups.conf to speed up later parsing. Note that these # data are needed for most operations, and populated by upslist_readFile() UPSCONF_DATA="" -# Subset of normalized data above that only has sections, drivers and ports +# Subset of normalized data above that only has sections, drivers and ports (SDP) UPSCONF_DATA_SDP="" # List of configured UPSes in the config-file @@ -187,6 +192,9 @@ hook_listInstances_raw="" hook_validInstanceName="" hook_validFullUnitName="" hook_validInstanceSuffixName="" +hook_getSavedDeviceName="" +hook_findSavedDeviceName="" +hook_setSavedDeviceName="" hook_getSavedMD5="" hook_setSavedMD5="" hook_restart_upsd="" @@ -202,6 +210,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="smf_validInstanceName" hook_validFullUnitName="smf_validFullUnitName" hook_validInstanceSuffixName="smf_validInstanceSuffixName" + hook_getSavedDeviceName="smf_getSavedDeviceName" + hook_findSavedDeviceName="smf_findSavedDeviceName" + hook_setSavedDeviceName="smf_setSavedDeviceName" hook_getSavedMD5="smf_getSavedMD5" hook_setSavedMD5="smf_setSavedMD5" hook_restart_upsd="smf_restart_upsd" @@ -216,6 +227,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="systemd_validInstanceName" hook_validFullUnitName="systemd_validFullUnitName" hook_validInstanceSuffixName="systemd_validInstanceSuffixName" + hook_getSavedDeviceName="systemd_getSavedDeviceName" + hook_findSavedDeviceName="systemd_findSavedDeviceName" + hook_setSavedDeviceName="systemd_setSavedDeviceName" hook_getSavedMD5="systemd_getSavedMD5" hook_setSavedMD5="systemd_setSavedMD5" hook_restart_upsd="systemd_restart_upsd" @@ -230,6 +244,9 @@ case "${SERVICE_FRAMEWORK-}" in hook_validInstanceName="selftest_NOOP" hook_validFullUnitName="selftest_NOOP" hook_validInstanceSuffixName="selftest_NOOP" + hook_getSavedDeviceName="selftest_NOOP" + hook_findSavedDeviceName="selftest_NOOP" + hook_setSavedDeviceName="selftest_NOOP" hook_getSavedMD5="selftest_NOOP" hook_setSavedMD5="selftest_NOOP" hook_restart_upsd="selftest_NOOP" @@ -251,7 +268,7 @@ selftest_NOOP() { } common_isFiled() { - [ -n "$UPSLIST_FILE" ] && \ + [ "${#UPSLIST_FILE}" != 0 ] && \ for UPSF in $UPSLIST_FILE ; do [ "$1" = "$UPSF" ] && return 0 [ "`$hook_validInstanceName "$UPSF"`" = "$1" ] && return 0 @@ -260,7 +277,7 @@ common_isFiled() { } common_isRegistered() { - [ -n "$UPSLIST_SVCS" ] && \ + [ "${#UPSLIST_SVCS}" != 0 ] && \ for UPSS in $UPSLIST_SVCS ; do [ "$1" = "$UPSS" ] && return 0 [ "`$hook_validInstanceName "$1"`" = "$UPSS" ] && return 0 @@ -279,8 +296,8 @@ upslist_equals() { # Trivial case 0: one string is empty, another is not # Note: `echo '' | wc -l` == "1" not "0"! - [ -n "$1" -a -z "$2" ] && return 1 - [ -z "$1" -a -n "$2" ] && return 1 + [ "${#1}" != 0 -a "${#2}" = 0 ] && return 1 + [ "${#1}" = 0 -a "${#2}" != 0 ] && return 1 # Trivial case 1: equal strings [ "$1" = "$2" ] && return 0 @@ -289,22 +306,22 @@ upslist_equals() { _TMP_DEV_SVC="" for _DEV in $1 ; do - DEVINST="`$hook_validInstanceName "$_DEV"`" + DEVINST="`$hook_validInstanceName "${_DEV}"`" for _SVC in $2 ; do - [ "$_DEV" = "$_SVC" ] \ - || [ "$DEVINST" = "$_SVC" ] \ - && { [ -z "$_TMP_DEV_SVC" ] \ - && _TMP_DEV_SVC="$_DEV = $_SVC" \ - || _TMP_DEV_SVC="$_TMP_DEV_SVC -$_DEV = $_SVC" ; } + [ "${_DEV}" = "${_SVC}" ] \ + || [ "$DEVINST" = "${_SVC}" ] \ + && { [ "${#_TMP_DEV_SVC}" = 0 ] \ + && _TMP_DEV_SVC="${_DEV} = ${_SVC}" \ + || _TMP_DEV_SVC="${_TMP_DEV_SVC} +${_DEV} = ${_SVC}" ; } done done # Input was not empty; did anything in output fit? - [ -z "$_TMP_DEV_SVC" ] && return 1 + [ "${#_TMP_DEV_SVC}" = 0 ] && return 1 # Exit code : is the built mapping as long as the source list(s)? - [ "`echo "$1" | wc -l`" = "`echo "$_TMP_DEV_SVC" | wc -l`" ] + [ "`echo "$1" | wc -l`" = "`echo "${_TMP_DEV_SVC}" | wc -l`" ] } upssvcconf_checksum_unchanged() { @@ -320,28 +337,72 @@ upslist_checksums_unchanged() { # configuration section from the file and the stashed configuration in # a service instance. Prints a list of mismatching service names that # should get reconfigured. - [ -z "$1" -o -z "$2" ] && return 1 + [ "${#1}" = 0 -o "${#2}" = 0 ] && return 1 _TMP_SVC="" for _DEV in $1 ; do - DEVINST="`$hook_validInstanceName "$_DEV"`" + DEVINST="`$hook_validInstanceName "${_DEV}"`" for _SVC in $2 ; do - if [ "$_DEV" = "$_SVC" ] \ - || [ "$DEVINST" = "$_SVC" ] \ + if [ "${_DEV}" = "${_SVC}" ] \ + || [ "$DEVINST" = "${_SVC}" ] \ ; then - upssvcconf_checksum_unchanged "$_DEV" "$_SVC" || \ - { [ -z "$_TMP_SVC" ] \ - && _TMP_SVC="$_SVC" \ - || _TMP_SVC="$_TMP_SVC -$_SVC" ; } + upssvcconf_checksum_unchanged "${_DEV}" "${_SVC}" || \ + { [ "${#_TMP_SVC}" = 0 ] \ + && _TMP_SVC="${_SVC}" \ + || _TMP_SVC="${_TMP_SVC} +${_SVC}" ; } fi done done - [ -z "$_TMP_SVC" ] && return 0 - echo "$_TMP_SVC" + [ "${#_TMP_SVC}" = 0 ] && return 0 + echo "${_TMP_SVC}" return 1 } +upslist_savednames_find_missing() { + # Verify that all existing service units have a saved DEVICE name + # Report those that do not have a value there (any value) so we can + # amend those quickly after an upgrade. Otherwise we trust these. + + # Get full instance names from system and from props + SVCINSTS="`$hook_listInstances_raw`" && [ "${#SVCINSTS}" != 0 ] || return 1 + # If no props were found, (over)write them all + SVCINST_PROPS="`$hook_findSavedDeviceName`" && [ "${#SVCINST_PROPS}" != 0 ] \ + || { echo $SVCINSTS ; return 2; } + + # Find services which do not have saved names in props + for SVCINST in $SVCINSTS ; do + echo "$SVCINST_PROPS" | grep -E "^${SVCINST_PROPS}\t"'$' >/dev/null || echo "$SVCINST" + done +} + +upslist_savednames_find_mismatch() { + # TODO: Make use of this to fsck the enumerator configs + # + # Verify that all existing service units have a saved DEVICE name + # and that such name does match the unit instance's name (original + # or MD5 normalized version). If something does not match, returns + # the unit name so it can be redefined by caller. This does not + # inspect whether such DEVICE is defined in NUT configuration. + # This situation might occur in some errors, but the likely case + # is updating from versions that did not track this info yet (but + # upslist_savednames_find_missing() should have handled those). + + # Get full instance names from system and from props + SVCINSTS="`$hook_listInstances_raw`" && [ "${#SVCINSTS}" != 0 ] || return 1 + SVCINST_PROPS="`$hook_findSavedDeviceName`" && [ "${#SVCINST_PROPS}" != 0 ] || return 2 + + # Find services whose props exist but services do not + echo "$SVCINST_PROPS" | while read SVCINST_PROP DEVNAME_PROP ; do + echo "$SVCINSTS" | grep -E "^${SVCINST_PROP}"'$' >/dev/null || echo "$SVCINST_PROP" + done + + # Find services which do not have saved names in props + for SVCINST in $SVCINSTS ; do + echo "$SVCINST_PROPS" | grep -E "^${SVCINST_PROP}\t"'$' >/dev/null || echo "$SVCINST" + done +} + upsconf_getSection_content() { # "$1" = name of ups.conf section to display in whole, from whatever # comes on stdin (file or a pre-made normalized variable) @@ -386,16 +447,16 @@ upsconf_getSection_content() { *) ;; # Fall through to add the line to contents of existing section esac if [ "$CURR_SECTION" = "$1" ]; then - if [ -n "$SECTION_CONTENT" ]; then + if [ "${#SECTION_CONTENT}" = 0 ]; then + SECTION_CONTENT="$LINE" + else SECTION_CONTENT="$SECTION_CONTENT $LINE" - else - SECTION_CONTENT="$LINE" fi fi done - if [ -n "$SECTION_CONTENT" ]; then + if [ "${#SECTION_CONTENT}" != 0 ]; then echo "$SECTION_CONTENT" fi @@ -405,7 +466,9 @@ $LINE" upsconf_getSection() { # Use the whole output of normalization parser - upslist_normalizeFile_once || return # Propagate errors upwards + if [ x"${AVOID_REPARSE}" != xyes ] ; then + upslist_normalizeFile_once || return # Propagate errors upwards + fi upsconf_getSection_content "$@" << EOF ${UPSCONF_DATA} EOF @@ -417,7 +480,9 @@ upsconf_getSection_MD5() { upsconf_getSection_SDP() { # Use the section-driver-port subset - upslist_normalizeFile_once || return # Propagate errors upwards + if [ x"${AVOID_REPARSE}" != xyes ] ; then + upslist_normalizeFile_once || return # Propagate errors upwards + fi upsconf_getSection_content "$@" << EOF ${UPSCONF_DATA_SDP} EOF @@ -442,8 +507,8 @@ upsconf_getValue() { RES_L=0 VALUE="" - LINE="`echo "$SECTION_CONTENT" | egrep '(^'"$1"'=|^'"$1"'$)'`" \ - && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'$,\1,")" \ + LINE="`echo "$SECTION_CONTENT" | grep -E '(^'"$1"'=|^'"$1"'$)'`" \ + && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'\$,\1,")" \ || RES_L=$? [ "$RES_L" = 0 ] || { RES="$RES_L" ; echo "ERROR: Section [$CURR_SECTION] or key '$1' in it was not found in the '$UPSCONF' file" >&2 ; } @@ -478,8 +543,45 @@ upsconf_getDriverMedia() { # particular system's physics, both serial and network media may need USB). CURR_DRV="`upsconf_getDriver "$1"`" || return $? case "$CURR_DRV" in - *netxml*|*snmp*|*ipmi*|*powerman*|*-mib*|*avahi*|*apcupsd*) + *netxml*|*snmp*|*ipmi*|*powerman*|*-mib*|*avahi*) printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *apcupsd-ups*) + # Relay from a nearby apcupsd network server into NUT ecosystem: + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + *localhost*|*127.0.0.1*|*::1*) + printf '%s\n%s\n' "$CURR_DRV" "network-localhost" ; return ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + esac + ;; + *apc_modbus*) + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + CURR_PORTTYPE="`upsconf_getValue "$1" 'porttype'`" || CURR_PORTTYPE="" + case "$CURR_PORTTYPE" in + *usb*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + *serial*) + printf '%s\n%s\n' "$CURR_DRV" "serial" ; return ;; + *) # default depends on driver build (against libmodbus + # version with or without support for usb) + # TOTHINK: Check for presence of config values like + # vendorid (USB) or baud (Serial)? They are optional + # with reasonable defaults anyway... + case "$CURR_PORT" in + *auto*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + /*) + printf '%s\n%s\n' "$CURR_DRV" "serial" ; return ;; + *localhost*|*127.0.0.1*|*::1*) + printf '%s\n%s\n' "$CURR_DRV" "network-localhost" ; return ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + esac + # returns are above, but just in case - have a fallback: + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; *usb*) printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; nutdrv_qx) # May be direct serial or USB @@ -489,7 +591,7 @@ upsconf_getDriverMedia() { printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; /dev/*) # See drivers/nutdrv_qx.c :: upsdrv_initups() for a list - if [ -n "`upsconf_getValue "$1" 'subdriver' 'vendorid' 'productid' 'vendor' 'product' 'serial' 'bus' 'langid_fix'`" ] \ + if [ -n "`upsconf_getValue "$1" 'subdriver' 'vendorid' 'productid' 'vendor' 'product' 'serial' 'bus' 'busport' 'langid_fix'`" ] \ ; then printf '%s\n%s\n' "$CURR_DRV" "usb" ; return else @@ -517,7 +619,7 @@ upsconf_getDriverMedia() { upsconf_getMedia() { _DRVMED="`upsconf_getDriverMedia "$1"`" || return - echo "$_DRVMED" | tail -n +2 + echo "${_DRVMED}" | tail -n +2 return 0 } @@ -527,27 +629,27 @@ upsconf_debug() { _MED="`upsconf_getMedia "$1"`" _MD5="`upsconf_getSection_MD5 "$1"`" NAME_MD5="`calc_md5 "$1"`" - echo "INST: ${NAME_MD5}~[$1]: DRV='$_DRV' PORT='$_PRT' MEDIA='$_MED' SECTIONMD5='$_MD5'" + echo "INST: ${NAME_MD5}~[$1]: DRV='${_DRV}' PORT='${_PRT}' MEDIA='${_MED}' SECTIONMD5='${_MD5}'" } calc_md5() { # Tries several ways to produce an MD5 of the "$1" argument - _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "$_MD5" ] || \ - { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "${_MD5}" ] || \ + { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "${_MD5}" ]; } || \ return 1 - echo "$_MD5" + echo "${_MD5}" } calc_md5_file() { # Tries several ways to produce an MD5 of the file named by "$1" argument [ -s "$1" ] || return 2 - _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "$_MD5" ] || \ - { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "${_MD5}" ] || \ + { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "${_MD5}" ]; } || \ return 1 - echo "$_MD5" + echo "${_MD5}" } smf_validFullUnitName() { @@ -568,15 +670,21 @@ smf_validInstanceSuffixName() { smf_registerInstance() { DEVICE="$1" SVCINST="$1" + if /usr/bin/svcs "nut-driver:$SVCINST" >/dev/null 2>&1 ; then + smf_unregisterInstance "$SVCINST" + fi /usr/sbin/svccfg -s nut-driver add "$DEVICE" || \ - { SVCINST="`smf_validInstanceName "$1"`" && \ + { SVCINST="`smf_validInstanceName "$1"`" || return + if /usr/bin/svcs "nut-driver:$SVCINST" >/dev/null 2>&1 ; then + smf_unregisterInstance "$SVCINST" + fi /usr/sbin/svccfg -s nut-driver add "$SVCINST" || return ; } echo "Added instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 DEPSVC="" DEPREQ="" _MED="`upsconf_getMedia "$DEVICE"`" - case "$_MED" in + case "${_MED}" in usb) DEPSVC="$DEPSVC_USB_SMF" DEPREQ="$DEPREQ_USB_SMF" ;; @@ -588,7 +696,7 @@ smf_registerInstance() { DEPREQ="$DEPREQ_NET_FULL_SMF" ;; serial) ;; '') ;; - *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + *) echo "WARNING: Unexpected NUT media type ignored: '${_MED}'" >&2 ;; esac TARGET_FMRI="nut-driver:$SVCINST" @@ -607,6 +715,8 @@ smf_registerInstance() { fi smf_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + # Save original device (config section) name to speed up some searches + smf_setSavedDeviceName "$SVCINST" "$DEVICE" /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return if [ "$AUTO_START" = yes ] ; then @@ -625,10 +735,11 @@ smf_refreshSupervizor() { } smf_listInstances_raw() { # Newer versions have pattern matching; older SMF might not have this luxury - /usr/bin/svcs -a -H -o fmri | egrep '/nut-driver:' + /usr/bin/svcs -a -H -o fmri | grep -E '/nut-driver:' } smf_listInstances() { - smf_listInstances_raw | sed 's/^.*://' | sort -n + # Chop twice, in case there is a leading "svc:/...." + smf_listInstances_raw | sed -e 's/^.*://' -e 's/^.*://' | sort -n } smf_getSavedMD5() { # Query service instance $1 @@ -644,36 +755,91 @@ smf_getSavedMD5() { fi # Note: lookups for GLOBAL cause each service instance to show up - /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" | head -1 | awk '{print $NF}' + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" 2>/dev/null | head -1 | awk '{print $NF}' } -smf_setSavedMD5() { - # Save checksum value $2 into service instance $1 - PG="nut-driver-enumerator-generated-checksum" - PROP="SECTION_CHECKSUM" +smf_findSavedDeviceName() { + # Returns long service FMRI which has DEVICE=="$1" + # For empty "$1" returns a list of all recorded "FMRIDEVICE" + if [ -z "$1" ]; then + /usr/bin/svcprop -p "nut-driver-enumerator-generated-devicename/DEVICE" \ + 'svc:/system/power/nut-driver:*' 2>/dev/null \ + | sed 's|^\(svc:/[^:]*:[^/:]*\)/:properties/nut-driver-enumerator-generated-devicename/DEVICE astring \(.*\)$|\1\t\2|' + else + /usr/bin/svcprop -p "nut-driver-enumerator-generated-devicename/DEVICE" \ + "svc:/system/power/nut-driver:$1" 2>/dev/null \ + | sed 's|^\(svc:/[^:]*:[^/:]*\)/:prop.*$|\1|' + fi +} +smf_getSavedDeviceName() { + # Query service instance $1 + PG="nut-driver-enumerator-generated-devicename" + PROP="DEVICE" if [ -n "$1" ]; then TARGET_FMRI="nut-driver:$1" else # Global section - TARGET_FMRI="nut-driver" - PROP="SECTION_CHECKSUM_GLOBAL" + echo "" + return 0 fi - /usr/sbin/svccfg -s "$TARGET_FMRI" delprop "$PG" || true - /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$PG" application && \ - /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$PG/$PROP" = astring: "$2" - [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } - /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return + # Note: lookups for GLOBAL cause each service instance to show up + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" 2>/dev/null | head -1 | awk '{print $NF}' +} +smf_setSavedUniq() { + # Save data value $5 of type $4 into service FMRI $1 + # under (scrapped and) newly created property group $2 + # and property name $3 + __TARGET_FMRI="$1" + __PG="$2" + __PROP="$3" + __TYPE="$4" + case "${__TYPE}" in + *:) ;; + *) __TYPE="${__TYPE}:" ;; + esac + __VAL="$5" + /usr/sbin/svccfg -s "${__TARGET_FMRI}" delprop "${__PG}" 2>/dev/null || true + /usr/sbin/svccfg -s "${__TARGET_FMRI}" addpg "${__PG}" application && \ + /usr/sbin/svccfg -s "${__TARGET_FMRI}" setprop "${__PG}/${__PROP}" = "${__TYPE}" "${__VAL}" + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the service property ${__PG}/${__PROP}">&2 ; return 1 ; } + + case "${__TARGET_FMRI}" in + svc:/*:*) ;; # A service instance by full FMRI, refresh + svc:/*/nut-driver|nut-driver) return 0 ;; # A base non-instance service item for nut-driver (known multi-instance only) + svc:/*) ;; # A base non-instance service item (not nut-driver) + *:*) ;; # A service instance by short FMRI, refresh + *) ;; # A base non-instance service item (not nut-driver) + esac + /usr/sbin/svcadm refresh "${__TARGET_FMRI}" || return +} +smf_setSavedMD5() { + # Save checksum value $2 into service instance $1 + _PG="nut-driver-enumerator-generated-checksum" + _PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + _TARGET_FMRI="nut-driver:$1" + else + # Global section + _TARGET_FMRI="nut-driver" + _PROP="SECTION_CHECKSUM_GLOBAL" + fi + smf_setSavedUniq "${_TARGET_FMRI}" "${_PG}" "${_PROP}" astring "$2" +} +smf_setSavedDeviceName() { + [ -n "$1" ] || return # No-op for global section + smf_setSavedUniq "nut-driver:$1" "nut-driver-enumerator-generated-devicename" "DEVICE" astring "$2" } smf_restart_upsd() { - echo "Restarting NUT data server to make sure it knows new configuration..." + echo "Reloading or restarting NUT data server to make sure it knows new configuration..." /usr/sbin/svcadm enable "nut-server" 2>/dev/null /usr/sbin/svcadm clear "nut-server" 2>/dev/null /usr/sbin/svcadm refresh "nut-server" || \ /usr/sbin/svcadm restart "nut-server" } smf_restart_drv() { - echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + echo "Reloading or restarting NUT driver instance '$1' to make sure it knows new configuration..." /usr/sbin/svcadm enable "nut-driver:$1" 2>/dev/null /usr/sbin/svcadm clear "nut-driver:$1" 2>/dev/null /usr/sbin/svcadm refresh "nut-driver:$1" || \ @@ -705,7 +871,7 @@ systemd_registerInstance() { DEPSVC="" DEPREQ="" _MED="`upsconf_getMedia "$DEVICE"`" - case "$_MED" in + case "${_MED}" in usb) DEPSVC="$DEPSVC_USB_SYSTEMD" DEPREQ="$DEPREQ_USB_SYSTEMD" ;; @@ -717,7 +883,7 @@ systemd_registerInstance() { DEPREQ="$DEPREQ_NET_FULL_SYSTEMD" ;; serial) ;; '') ;; - *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + *) echo "WARNING: Unexpected NUT media type ignored: '${_MED}'" >&2 ;; esac if [ -n "$DEPSVC" ]; then [ -n "$DEPREQ" ] || DEPREQ="#Wants" @@ -735,6 +901,7 @@ EOF fi systemd_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + systemd_setSavedDeviceName "$SVCINST" "$DEVICE" if [ "$AUTO_START" = yes ] ; then systemd_refreshSupervizor || echo "WARNING: Somehow managed to fail systemd_refreshSupervizor()" >&2 @@ -756,7 +923,7 @@ systemd_refreshSupervizor() { /bin/systemctl daemon-reload } systemd_listInstances_raw() { - /bin/systemctl show 'nut-driver@*' -p Id | egrep '=nut-driver' | sed 's,^Id=,,' + /bin/systemctl show 'nut-driver@*' -p Id | grep -E '=nut-driver' | sed 's,^Id=,,' } systemd_listInstances() { systemd_listInstances_raw | sed -e 's/^.*@//' -e 's/\.service$//' | sort -n @@ -765,16 +932,53 @@ systemd_getSavedMD5() { # Query service instance $1 or global section PROP="SECTION_CHECKSUM" [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" - [ -s "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" ] \ - && grep "Environment='$PROP=" "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ - || { echo "Did not find '${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf' with a $PROP" ; return 1; } + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" + [ -s "${PROPFILE}" ] \ + && grep "Environment='$PROP=" "${PROPFILE}" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ + || { echo "Did not find '${PROPFILE}' with a $PROP" ; return 1; } +} +systemd_findSavedDeviceName() { + # Returns long service instance name which has DEVICE=="$1" + # For empty "$1" returns a list of all recorded "SVCDEVICE" + if [ -z "$1" ]; then + grep -H "Environment='DEVICE=" \ + "${SYSTEMD_CONFPATH}"/nut-driver@*.service.d/nut-driver-enumerator-generated-devicename.conf \ + | sed 's|^'"${SYSTEMD_CONFPATH}"'/\(nut-driver@[^/]*\.service\)\.d/.*DEVICE='"[\"']*\([^\"']*\)[\"']*"'$|\1\t\2|' + else + grep -E -H "Environment='DEVICE=($1|\"$1\")'" \ + "${SYSTEMD_CONFPATH}"/nut-driver@*.service.d/nut-driver-enumerator-generated-devicename.conf \ + | sed 's|^'"${SYSTEMD_CONFPATH}"'/\(nut-driver@[^/]*\.service\)\.d/.*$|\1|' + fi +} +systemd_getSavedDeviceName() { + # Query service instance "$1" (quiet NO-OP if empty, for mis-use + # in global sections context) to get the unquoted saved DEVICE + # section name corresponding to this service instance + [ -n "$1" ] || { echo ""; return 0; } + PROP="DEVICE" + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-devicename.conf" + [ -s "${PROPFILE}" ] \ + && grep "Environment='$PROP=" "${PROPFILE}" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," -e 's,^"\(.*\)"$,\1,' \ + || { echo "Did not find '${PROPFILE}' with a $PROP" ; return 1; } +} +systemd_setSavedDeviceName() { + # Save device (config section) name $2 into service instance $1 + [ -n "$1" ] || return # No-op for global section + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-devicename.conf" + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ + cat > "${PROPFILE}" << EOF +[Service] +Environment='DEVICE="$2"' +EOF + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the device name">&2 ; return 1 ; } } systemd_setSavedMD5() { # Save checksum value $2 into service instance $1 PROP="SECTION_CHECKSUM" [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + PROPFILE="${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ - cat > "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" << EOF + cat > "${PROPFILE}" << EOF [Service] Environment='$PROP=$2' EOF @@ -788,7 +992,7 @@ systemd_restart_upsd() { *) return 0 ;; esac - echo "Restarting NUT data server to make sure it knows new configuration..." + echo "Reloading or restarting NUT data server to make sure it knows new configuration..." # Note: reload is a better way to go about this, so the # data service is not interrupted by re-initialization # of the daemon. But systemd/systemctl sometimes stalls... @@ -804,9 +1008,12 @@ systemd_restart_drv() { *) return 0 ;; esac - echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + echo "Reloading or restarting NUT driver instance '$1' to make sure it knows new configuration..." # Full restart, e.g. in case we changed the user= in configs + # however let "reload" try, in case we changed something that + # can be updated "on the fly", like the "debug_min" setting. + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl reload-or-restart "nut-driver@$1" || \ $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl restart "nut-driver@$1" } @@ -824,15 +1031,15 @@ upslist_normalizeFile_filter() { # are dropped. Note that brackets with spaces inside, and brackets # that do not start the non-whitespace payload of the line, are not # sections. - egrep -v '(^$|^#)' | \ + grep -E -v '(^$|^#)' | \ sed -e 's,^['"$TABCHAR"'\ ]*,,' \ -e 's,^\#.*$,,' \ -e 's,['"$TABCHAR"'\ ]*$,,' \ -e 's,^\([^=\ '"$TABCHAR"']*\)['"$TABCHAR"'\ ]*=['"$TABCHAR"'\ ]*,\1=,g' \ -e 's,=\"\([^\ '"$TABCHAR"']*\)\"$,=\1,' \ -e 's,^\(\[[^]'"$TABCHAR"'\ ]*\]\)['"$TABCHAR"'\ ]*\(#.*\)*$,\1,' \ - | egrep -v '^$' \ - | egrep '([\[\=]|^[^ '"$TABCHAR"']*$|^[^ '"$TABCHAR"']*[ '"$TABCHAR"']*\#.*$)' + | grep -E -v '^$' \ + | grep -E '([\[\=]|^[^ '"$TABCHAR"']*$|^[^ '"$TABCHAR"']*[ '"$TABCHAR"']*#.*$)' } upslist_normalizeFile() { @@ -858,8 +1065,8 @@ upslist_normalizeFile() { # Also use a SDP subset with just section, driver and port info # for faster parsing when determining driver-required media etc. UPSCONF_DATA="$(upslist_normalizeFile_filter < "$UPSCONF")" \ - && [ -n "$UPSCONF_DATA" ] \ - && UPSCONF_DATA_SDP="`egrep '^(\[.*\]|driver=|port=)' << EOF + && [ "${#UPSCONF_DATA}" != 0 ] \ + && UPSCONF_DATA_SDP="`grep -E '^(\[.*\]|driver=|port=)' << EOF $UPSCONF_DATA EOF`" \ || { echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: nothing left after normalization" >&2 @@ -872,7 +1079,7 @@ upslist_normalizeFile_once() { # Wrapper that ensures that the parsing is only done once # (will re-parse if there were no devices listed on the # first time, though) - [ -z "$UPSCONF_DATA" ] && [ -z "$UPSCONF_DATA_SDP" ] || return 0 + [ "${#UPSCONF_DATA}" = 0 ] && [ "${#UPSCONF_DATA_SDP}" = 0 ] || return 0 upslist_normalizeFile } @@ -888,11 +1095,11 @@ upslist_readFile() { upslist_normalizeFile || return # Propagate errors upwards fi - if [ -n "$UPSCONF_DATA" ] ; then + if [ "${#UPSCONF_DATA}" != 0 ] ; then # Note that section-name brackets should contain a single token - UPSLIST_FILE="$(echo "$UPSCONF_DATA_SDP" | egrep '^\[[^'"$TABCHAR"'\ ]*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ + UPSLIST_FILE="$(echo "$UPSCONF_DATA_SDP" | grep -E '^\[[^'"$TABCHAR"'\ ]*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ || UPSLIST_FILE="" - if [ -z "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" = 0 ] ; then echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: no section declarations in parsed normalized contents" >&2 fi fi @@ -903,13 +1110,13 @@ upslist_readFile_once() { # Wrapper that ensures that the parsing is only done once # (will re-parse if there were no devices listed on the # first time, though) - [ -z "$UPSLIST_FILE" ] || return 0 + [ "${#UPSLIST_FILE}" = 0 ] || return 0 DO_NORMALIZE_ONCE=yes upslist_readFile } upslist_readSvcs() { UPSLIST_SVCS="`$hook_listInstances`" || UPSLIST_SVCS="" - if [ -z "$UPSLIST_SVCS" ] && [ "$1" != "-" ] ; then + if [ "${#UPSLIST_SVCS}" = 0 ] && [ "$1" != "-" ] ; then EXPLAIN="" [ -z "$1" ] || EXPLAIN=" - $1" echo "Error reading the list of ${SERVICE_FRAMEWORK-} service instances for UPS drivers, or none are defined${EXPLAIN}" >&2 @@ -987,11 +1194,15 @@ nut_driver_enumerator_main() { upslist_readSvcs "before manipulations" # Test if global config has changed since last run + # Note that we have upslist_normalizeFile called from upslist_readFile + # just above, so even if it came empty (e.g. new NUT installation, no + # device sections yet) we do not want to spend time and log storage to + # parse again and complain again. RESTART_ALL=no - upssvcconf_checksum_unchanged "" || { echo "`date -u` : Detected changes in global section of '$UPSCONF', will restart all drivers"; RESTART_ALL=yes; } + AVOID_REPARSE=yes upssvcconf_checksum_unchanged "" || { echo "`date -u` : Detected changes in global section of '$UPSCONF', will restart all drivers"; RESTART_ALL=yes; } - # Quickly exit if there's nothing to do; note the lists are pre-sorted - # Otherwise a non-zero exit will be done below + # Quickly exit if there's nothing to do (both lists empty or equal); note + # the lists are pre-sorted. Otherwise a non-zero exit will be done below. # Note: We implement testing in detail whether section definitions were # changed since last run, as a first step before checking that name # lists are still equivalent, because we need to always have the result @@ -999,23 +1210,38 @@ nut_driver_enumerator_main() { # while the check for no new device section definitions is just boolean. # We can only exit quickly if both there are no changed sections and no # new or removed sections since last run. - NEW_CHECKSUM="`upslist_checksums_unchanged "$UPSLIST_FILE" "$UPSLIST_SVCS"`" \ - && [ -z "$NEW_CHECKSUM" ] \ - && upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" \ + ( [ -z "$UPSLIST_FILE" -a -z "$UPSLIST_SVCS" ] || ( \ + NEW_CHECKSUM="`upslist_checksums_unchanged "$UPSLIST_FILE" "$UPSLIST_SVCS"`" \ + && [ "${#NEW_CHECKSUM}" = 0 ] \ + && upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" \ + ) ) \ && if [ -z "$DAEMON_SLEEP" -o "${VERBOSE_LOOP}" = yes ] ; then \ echo "`date -u` : OK: No changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" ; \ fi \ && [ "$RESTART_ALL" = no ] && return 0 - if [ -n "$NEW_CHECKSUM" ]; then + if [ "${#NEW_CHECKSUM}" != 0 ]; then for UPSS in $NEW_CHECKSUM ; do - echo "Dropping old ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed..." >&2 - $hook_unregisterInstance "$UPSS" + CURR_DRV="`upsconf_getDriver "$UPSS"`" || CURR_DRV="" + DO_UNREGISTER=yes + if [ -n "$CURR_DRV" ] ; then + # If reload is handled and does not complain, + # we are OK to proceed without re-defining + # and restarting the driver service instance. + "@DRVPATH@/$CURR_DRV" -a "$UPSS" -c reload-or-error >/dev/null 2>/dev/null \ + && DO_UNREGISTER=no + fi + if [ "$DO_UNREGISTER" = yes ] ; then + echo "Dropping old ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed..." >&2 + $hook_unregisterInstance "$UPSS" + else + echo "Keeping ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed: live reload sufficed" >&2 + fi done upslist_readSvcs "after updating for new config section checksums" fi - if [ -n "$UPSLIST_SVCS" ]; then + if [ "${#UPSLIST_SVCS}" != 0 ]; then # Drop services that are not in config file (any more?) upslist_delSvcs @@ -1031,7 +1257,7 @@ nut_driver_enumerator_main() { $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" fi - if [ -n "$UPSLIST_FILE" ]; then + if [ "${#UPSLIST_FILE}" != 0 ]; then # Add services for sections that are in config file but not yet wrapped upslist_addSvcs $hook_refreshSupervizor @@ -1039,12 +1265,12 @@ nut_driver_enumerator_main() { fi upslist_readSvcs - if [ -n "$UPSLIST_SVCS" ] ; then + if [ "${#UPSLIST_SVCS}" != 0 ] ; then echo "=== The currently defined service instances are:" echo "$UPSLIST_SVCS" fi - if [ -n "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" != 0 ] ; then echo "=== The currently defined configurations in '$UPSCONF' are:" echo "$UPSLIST_FILE" fi @@ -1085,6 +1311,240 @@ nut_driver_enumerator_main() { return 13 } +nut_driver_enumerator_full_reconfigure() { + # Similar to the main routine for reconciling data, + # but this one removes all service instances and + # defines current mappings from scratch after that + upslist_readFile_once || return $? + upslist_readSvcs "before manipulations" + + if [ "${#UPSLIST_SVCS}" != 0 ]; then + for UPSS in $UPSLIST_SVCS ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after dropping" + fi + if [ "${#UPSLIST_FILE}" != 0 ]; then + upslist_addSvcs + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + + # Service units were manipulated, including saving of checksums; + # refresh the service management daemon if needed + $hook_refreshSupervizor + + if [ "${#UPSLIST_SVCS}" != 0 ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ "${#UPSLIST_FILE}" != 0 ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up and reconcile the late-coming changes" + nut_driver_enumerator_main ; return $? + # The "main" routine will do REPORT_RESTART_42 logic too + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && return 0 || return 42 + fi + + return 13 +} + +list_services_for_devices() { + FINAL_RES=0 + upslist_readFile_once && [ "${#UPSLIST_FILE}" != 0 ] \ + || { echo "No devices detected in '$UPSCONF'" >&2 ; return 1 ; } + upslist_readSvcs "by user request" && [ "${#UPSLIST_SVCS}" != 0 ] \ + || { echo "No service instances detected" >&2 ; return 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ "${#UPSLIST_SVCS_RAW}" != 0 ] \ + || { echo "No service units detected" >&2 ; return 1; } + for DEV in $UPSLIST_FILE ; do + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + echo "WARNING: no service instances detected that match device '$DEV'" >&2 + FINAL_RES=1 + done + return $FINAL_RES +} + +SVCS_DEVS_LIST="" +list_services_for_devices_once() { + # On first call, caches the system reponse + # On next calls returns what it got earlier + # Does not return any text, just the exit code + # (0 = data avail, even if empty, in SVCS_DEVS_LIST) + [ "${#SVCS_DEVS_LIST}" != 0 ] && return + + # Pre-cache config file data, if nobody read it yet, + # and keep in main script context for reuse (no subshell) + upslist_readFile_once && \ + SVCS_DEVS_LIST="`list_services_for_devices "$@"`" || return $? +} + +get_device_for_service() { + [ -z "$1" ] && echo "Service (instance) name argument required" >&2 && return 1 + + # Instance name can be a hash or "native" NUT section name + SVC="`$hook_validInstanceSuffixName "$1"`" && [ -n "$SVC" ] \ + || { echo "Error getting SVC name from the inputs" >&2 ; return 1; } + + # Reading the config is too expensive to do for every + # driver management attempt when there are many devices + if [ "${USE_SAVEDINST-}" != false ]; then + # Try to use last-stashed values from service properties first + # (NOTE: saved value is assumed to be valid if present) + SAVEDINST="`$hook_getSavedDeviceName "$SVC"`" || SAVEDINST="" + [ "${#SAVEDINST}" = 0 ] || { echo "$SAVEDINST" ; return 0 ; } + fi + + case "$SVC" in + MD5_*) ;; # fall through to the bulk of code + *) upslist_readFile_once || return $? + echo "$UPSLIST_FILE" | grep -E "^$SVC\$" + return $? + ;; + esac + + # Inspect SVC=MD5_* usecase + FINAL_RES=0 + list_services_for_devices_once && [ "${#SVCS_DEVS_LIST}" != 0 ] || FINAL_RES=$? + if [ "$FINAL_RES" = 0 ]; then + echo "$SVCS_DEVS_LIST" | grep "$SVC" | ( \ + while read _SVC _DEV ; do + _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit + [ "${_SVC}" = "${SVC}" ] && echo "${_DEV}" && exit 0 + done ; exit 1 ) && return 0 + fi + echo "No service instance '$1' was detected that matches a NUT device" >&2 + return 1 +} + +get_service_for_device() { + DEV="$1" + [ -z "$DEV" ] && echo "Device name argument required" >&2 && return 1 + + # Cheap check in service instance metadata, if saved + # (NOTE: saved value is assumed to be valid if present) + if [ "${USE_SAVEDSVC-}" != false ]; then + SAVEDSVC="`$hook_findSavedDeviceName "$DEV"`" || SAVEDSVC="" + [ "${#SAVEDSVC}" = 0 ] || { echo "$SAVEDSVC" ; return 0 ; } + fi + + # Trawl all the data we have... + # TODO: Reorder to avoid extra parsing if we have an early hit? + upslist_readSvcs "by user request" && [ "${#UPSLIST_SVCS}" != 0 ] \ + || { echo "No service instances detected" >&2 ; return 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ "${#UPSLIST_SVCS_RAW}" != 0 ] \ + || { echo "No service units detected" >&2 ; return 1; } + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + echo "$UNIT" + return 0 + fi + done + fi + done + # Second pass over other options + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + echo "$UNIT" + return 0 + fi + done + fi + done + echo "No service instances detected that match device '$DEV'" >&2 + return 1 +} + +update_upslist_savednames_find_missing() { + # Runs once in production modes that inspect and reconcile + # configs, to handle upgraded NUT deployments + SVCINSTS_NO_DEVICE="`upslist_savednames_find_missing`" || \ + case "$?" in + 1) return 0 ;; # No services defined yet + 2) ;; # All service units do not have DEVICE values + esac + # Something found... or not? Or all is populated? + [ "${#SVCINSTS_NO_DEVICE}" != 0 ] || return 0 + + # Make a list of what devices are known in config matched + # to service instances defined in the system, if any + # Note to not check the service instance properties which + # we are validating and currently to not quite trust. + USE_SAVEDINST=false list_services_for_devices_once \ + && [ "${#SVCS_DEVS_LIST}" != 0 ] || return 0 + + # Go over services with no device value saved into properties, + # and write the values learned from mapping above + _MISSING_RES=0 + for SVCINST in $SVCINSTS_NO_DEVICE ; do + _DEV="`printf '%s\n' "$SVCS_DEVS_LIST" | awk '( \$1 == "'"${SVCINST}"'" ) {print \$NF}'`" + echo "Service instance '$SVCINST' did not have a device recorded into properties, setting to '${_DEV}'" + [ -n "${_DEV}" ] || { echo "The device name value for '$SVCINST' is empty, skipping" >&2 ; _MISSING_RES=1 ; continue ; } + $hook_setSavedDeviceName "`$hook_validInstanceSuffixName "$SVCINST"`" "${_DEV}" || _MISSING_RES=$? + done + return ${_MISSING_RES} +} + RECONFIGURE_ASAP=false trap_handle_hup_main() { echo "`date -u` : Received a HUP during processing, scheduling reconfiguration to repeat ASAP (after the current iteration is done)" >&2 @@ -1118,6 +1578,8 @@ daemonize() { RECONFIGURE_ASAP=false trap 'trap_handle_hup_main' $RECONFIGURATION_SIGNALS + update_upslist_savednames_find_missing + # Note: this loop would die on errors with config file or # inability to ensure that it matches the list of services. # If caller did not `export REPORT_RESTART_42=no` then the @@ -1154,6 +1616,7 @@ UPSCONF_CHECKSUM_START="`calc_md5_file "$UPSCONF"`" || true # By default, update wrapping of devices into services if [ $# = 0 ]; then + update_upslist_savednames_find_missing nut_driver_enumerator_main ; exit $? fi @@ -1186,9 +1649,10 @@ $0 --daemon(=freq) Update wrapping of devices into services in an infinite loop Default freq is 60 sec $0 --daemon-after(=freq) - Update wrapping of devices into services in an infinite loop - First do one run of the loop though - Default freq is 60 sec + Update wrapping of devices into services in an infinite loop; + first do one run of the loop though, then daemonize (this way + service unit is deemed started only when NUT config and driver + instances are in sync). Default freq is 60 sec. $0 --reconfigure Stop and un-register all service instances and recreate them (e.g. if new dependency template was defined in a new @@ -1232,71 +1696,12 @@ while [ $# -gt 0 ]; do --help|-h|-help) usage; exit 0 ;; --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; --reconfigure) - upslist_readFile_once || exit $? - upslist_readSvcs "before manipulations" - - if [ -n "$UPSLIST_SVCS" ]; then - for UPSS in $UPSLIST_SVCS ; do - echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 - $hook_unregisterInstance "$UPSS" - done - upslist_readSvcs "after dropping" - fi - - if [ -n "$UPSLIST_FILE" ]; then - upslist_addSvcs - upslist_readSvcs "after checking for new config sections to define service instances" - fi - - # Save new checksum of global config - $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" - - # Service units were manipulated, including saving of checksums; - # refresh the service management daemon if needed - $hook_refreshSupervizor - - if [ -n "$UPSLIST_SVCS" ] ; then - echo "=== The currently defined service instances are:" - echo "$UPSLIST_SVCS" - fi - - if [ -n "$UPSLIST_FILE" ] ; then - echo "=== The currently defined configurations in '$UPSCONF' are:" - echo "$UPSLIST_FILE" - fi - - # We had some changes to the config file; upsd must be made aware - if [ "$AUTO_START" = yes ] ; then - $hook_restart_upsd - fi - - # Return 42 if there was a change applied succesfully - # (but e.g. some services should restart - upsd, maybe upsmon) - UPSLIST_EQ_RES=0 - upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? - - # File processing and service startups take a while; - # make sure upsconf did not change while we worked... - # NOTE: Check this at the last moment to minimize - # the chance of still not noticing the change made - # at just the wrong moment. - UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true - if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then - echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" - $0 ; exit $? - # The "main" routine will do REPORT_RESTART_42 logic too - fi - - if [ "$UPSLIST_EQ_RES" = 0 ] ; then - echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" - [ "${REPORT_RESTART_42-}" = no ] && exit 0 || exit 42 - fi - - exit 13 + nut_driver_enumerator_full_reconfigure "$@" + exit $? ;; --list-devices) upslist_readFile_once && \ - if [ -n "$UPSLIST_FILE" ] ; then + if [ "${#UPSLIST_FILE}" != 0 ] ; then echo "=== The currently defined configurations in '$UPSCONF' are:" >&2 echo "$UPSLIST_FILE" exit 0 @@ -1306,7 +1711,7 @@ while [ $# -gt 0 ]; do ;; --list-services) UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && \ - if [ -n "$UPSLIST_SVCS_RAW" ] ; then + if [ "${#UPSLIST_SVCS_RAW}" != 0 ] ; then echo "=== The currently defined service units are:" >&2 echo "$UPSLIST_SVCS_RAW" exit 0 @@ -1316,7 +1721,7 @@ while [ $# -gt 0 ]; do ;; --list-instances) upslist_readSvcs "by user request" && \ - if [ -n "$UPSLIST_SVCS" ] ; then + if [ "${#UPSLIST_SVCS}" != 0 ] ; then echo "=== The currently defined service instances are:" >&2 echo "$UPSLIST_SVCS" exit 0 @@ -1324,104 +1729,26 @@ while [ $# -gt 0 ]; do echo "No service instances detected" >&2 exit 1 ;; - --get-service-for-device) [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 - DEV="$2" - upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ - || { echo "No service instances detected" >&2 ; exit 1; } - UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ - || { echo "No service units detected" >&2 ; exit 1; } - vINST="`$hook_validInstanceName "$DEV"`" - vUNITD="`$hook_validFullUnitName "$DEV"`" - vUNITI="`$hook_validFullUnitName "$vINST"`" - # First pass over simple verbatim names - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$DEV" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITD" ] ; then - echo "$UNIT" - exit 0 - fi - done - fi - done - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$vINST" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITI" ] ; then - echo "$UNIT" - exit 0 - fi - done - fi - done - echo "No service instances detected that match device '$2'" >&2 - exit 1 + --get-service-for-device) + shift + get_service_for_device "$@" + exit $? ;; - --get-device-for-service) [ -z "$2" ] && echo "Service (instance) name argument required" >&2 && exit 1 - # Instance name can be a hash or "native" NUT section name - SVC="`$hook_validInstanceSuffixName "$2"`" - case "$SVC" in - MD5_*) ;; # fall through to the bulk of code - *) upslist_readFile_once || exit $? - echo "$UPSLIST_FILE" | egrep "^$SVC\$" - exit $? - ;; - esac - FINAL_RES=0 - OUT="`"$0" --list-services-for-devices`" && [ -n "$OUT" ] || FINAL_RES=$? - if [ "$FINAL_RES" = 0 ]; then - echo "$OUT" | grep "$SVC" | ( \ - while read _SVC _DEV ; do - _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit - [ "${_SVC}" = "${SVC}" ] && echo "$_DEV" && exit 0 - done ; exit 1 ) && exit 0 - fi - echo "No service instance '$2' was detected that matches a NUT device" >&2 - exit 1 + --get-device-for-service) + shift + get_device_for_service "$@" + exit $? ;; --list-services-for-devices) - FINAL_RES=0 - upslist_readFile_once && [ -n "$UPSLIST_FILE" ] \ - || { echo "No devices detected in '$UPSCONF'" >&2 ; exit 1 ; } - upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ - || { echo "No service instances detected" >&2 ; exit 1; } - UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ - || { echo "No service units detected" >&2 ; exit 1; } - for DEV in $UPSLIST_FILE ; do - vINST="`$hook_validInstanceName "$DEV"`" - vUNITD="`$hook_validFullUnitName "$DEV"`" - vUNITI="`$hook_validFullUnitName "$vINST"`" - # First pass over simple verbatim names - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$DEV" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITD" ] ; then - printf '%s\t%s\n' "$UNIT" "$DEV" - continue 3 - fi - done - fi - done - for INST in $UPSLIST_SVCS ; do - if [ "$INST" = "$vINST" ] ; then - for UNIT in $UPSLIST_SVCS_RAW ; do - if [ "$UNIT" = "$vUNITI" ] ; then - printf '%s\t%s\n' "$UNIT" "$DEV" - continue 3 - fi - done - fi - done - echo "WARNING: no service instances detected that match device '$DEV'" >&2 - FINAL_RES=1 - done - exit $FINAL_RES + shift + list_services_for_devices "$@" + exit $? ;; --show-configs|--show-device-configs|--show-all-configs|--show-all-device-configs) RES=0 upslist_readFile_once || RES=$? [ "$RES" != 0 ] && { echo "ERROR: upslist_readFile_once () failed with code $RES" >&2; exit $RES; } - [ -n "$UPSLIST_FILE" ] \ + [ "${#UPSLIST_FILE}" != 0 ] \ || { echo "WARNING: No devices detected in '$UPSCONF'" >&2 ; RES=1 ; } echo "$UPSCONF_DATA" exit $RES @@ -1450,6 +1777,20 @@ while [ $# -gt 0 ]; do upslist_debug exit $? ;; + upslist_savednames_find_missing) # Not public, not in usage() + upslist_savednames_find_missing + exit $? + ;; + upslist_savednames_find_mismatch) # Not public, not in usage() + upslist_savednames_find_mismatch + exit $? + ;; + update_upslist_savednames_find_missing) # Not public, not in usage() + update_upslist_savednames_find_missing + exit $? + ;; + hook_findSavedDeviceName) shift ; $hook_findSavedDeviceName "$@" ; exit $? ;; + hook_getSavedDeviceName) shift ; $hook_getSavedDeviceName "$@" ; exit $? ;; *) echo "Unrecognized argument: $1" >&2 ; exit 1 ;; esac shift diff --git a/scripts/usb_resetter/README.adoc b/scripts/usb_resetter/README.adoc new file mode 100644 index 0000000000..56a65127fe --- /dev/null +++ b/scripts/usb_resetter/README.adoc @@ -0,0 +1,97 @@ +Method for resetting unreliable USB UPS interfaces (on Linux) +============================================================= +Orsiris de Jong - NetInvent SASU +v1.0, 30 Mar 2023 (start date) + +Some cheaper USB UPS have the same kind of unreliable USB to serial interface, +often being a "Cypress Semiconductor USB to Serial" or "INNO TECH USB to Serial" +(often with the `0665:5161` VendorID/ProductID seen in examples below). +Most of them use `blazer_usb` or `nutdrv_qx` NUT driver, and sometimes the +driver can't start because it can't communicate with the UPS. + +NOTE: It is believed that in some cases the chip on UPS side can go into +a sort of power-saving mode after some quiet time, so increasing the NUT +driver polling frequency may help avoid that particular situation. + +Unplugging and plugging the USB port usually fixes this, but that's not +convenient. + +That's where `usb_resetter` comes in handy (see the +https://github.com/netinvent/usb_resetter project page for more info). +You would need a Python environment to run the script, and it is limited +to Linux platforms as of this writing. + +Grab a copy via `pip` with `pip install usb_resetter`, or make a plain +install directly from GitHub with e.g.: +---- +:; curl -o /usr/local/bin/usb_resetter -L \ + https://raw.githubusercontent.com/netinvent/usb_resetter/main/usb_resetter/usb_resetter.py \ + && chmod +x /usr/local/bin/usb_resetter +---- + +Once you have got the script, identify the USB UPS with: +---- +:; usb_resetter --list +---- + +In our case, we could see something like: +---- +Found device 0665:5161 at /dev/bus/usb/001/002 Manufacturer=INNO TECH, Product=USB to Serial +---- + +The `usb_resetter` can work in three different ways: + +- Reset device itself +- Reset the hub the device is attached to +- Reset all USB controllers + +A simple USB device reset typically isn't sufficient for those UPS devices, +so we would need to reset the hub it's attached to. + +The command for doing that is: +---- +:; usb_resetter --reset-hub --device 0665:5161 +---- + +Bear in mind that this will reset other devices connected to the same hub. +While this isn't a problem for a keyboard/mouse, it might be for a USB +storage device. On some hardware, each USB plug gets its own hub. +On others, two or more USB plugs share one hub. + +A good practice would be to isolate the USB UPS on a hub without any other +device in order to not interfere with other hardware, or to associate it +on a hub where a non-critical device is already plugged. + +Getting the hub your device is attached to can be done with: +---- +:; usb_resetter --list-hubs --device 0665:5161 +---- + +The easiest way to integrate this activity with the `nut-driver` service +is to modify the systemd service file (or ideally use a separate "drop-in" +snippet file) with the following line: +---- +ExecStartPre=/usr/local/bin/usb_reset.py --reset-hub --device 0665:5161 +---- + +An example modified `nut-driver.service` file which may be applicable to a +NUT v2.7.4 or older release (modulo the paths and the particular VID:PID) +is provided in this directory. Those releases packaged a single service unit +for all the drivers you have, managed as one bundle. + +With current NUT releases (2.8.0+), a `nut-driver@.service` template is used +to run each driver in a dedicated instance, declared manually or often by the +`nut-driver-enumerator` script or service (tracking `ups.conf` sections). +The added call to `usb_resetter` can then be a systemd drop-in file +tailored for that particular device and named like +`/etc/systemd/system/nut-driver@myups.service.d/usbreset.conf`, +so it does not impact others (unless they use the same USB hub). + +This way, every time the nut-driver service is restarted, the USB UPS link +is reset. + +NOTE: In author's testing, there were no additional delays required after +the `usb_resetter` before starting the driver. Generally however, keep in +mind that after a (re-)connection, the OS re-discovers the device, then it +gets owned by kernel, then the udev/upower/... subsystem hands it off to a +NUT run-time account, and only then can it be opened by a driver. diff --git a/scripts/usb_resetter/nut-driver.service b/scripts/usb_resetter/nut-driver.service new file mode 100644 index 0000000000..45018a0c67 --- /dev/null +++ b/scripts/usb_resetter/nut-driver.service @@ -0,0 +1,14 @@ +[Unit] +Description=Network UPS Tools - power device driver controller +After=local-fs.target network.target +StopWhenUnneeded=no + + +[Service] +ExecStartPre=-/usr/bin/systemd-tmpfiles --create /usr/lib/tmpfiles.d/nut-client.conf +ExecStartPre=/usr/local/bin/usb_resetter --reset-hub --device 0665:5161 +ExecStart=/usr/sbin/upsdrvctl start +ExecStop=/usr/sbin/upsdrvctl stop +Type=forking +Restart=on-failure +RestartSec=5s diff --git a/server/Makefile.am b/server/Makefile.am index 1ab6393023..878f16f588 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -1,27 +1,24 @@ # Network UPS Tools: server +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + # Make sure out-of-dir dependencies exist (especially when dev-building parts): $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libparseconf.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include -if WITH_WRAP - AM_CFLAGS += $(LIBWRAP_CFLAGS) -endif -if WITH_SSL - AM_CFLAGS += $(LIBSSL_CFLAGS) -endif LDADD = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la $(NETLIBS) -if WITH_WRAP - LDADD += $(LIBWRAP_LIBS) -endif -if WITH_SSL - LDADD += $(LIBSSL_LIBS) -endif sbin_PROGRAMS = upsd EXTRA_PROGRAMS = sockdebug @@ -31,8 +28,29 @@ upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ conf.h nut_ctype.h desc.h netcmds.h neterr.h netget.h netinstcmd.h \ netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h +upsd_CFLAGS = $(AM_CFLAGS) +upsd_LDADD = $(LDADD) + +if WITH_WRAP + upsd_CFLAGS += $(LIBWRAP_CFLAGS) + upsd_LDADD += $(LIBWRAP_LIBS) +endif +if WITH_SSL + upsd_CFLAGS += $(LIBSSL_CFLAGS) + upsd_LDADD += $(LIBSSL_LIBS) +endif -sockdebug_SOURCES = sockdebug.c +# Developer, troubleshooting, or odd automation aid tool: +if HAVE_WINDOWS + sockdebug_SOURCES = pipedebug.c +else !HAVE_WINDOWS + sockdebug_SOURCES = sockdebug.c +endif !HAVE_WINDOWS + +if WITH_DEV +# Have it installed properly +libexec_PROGRAMS = sockdebug +endif dummy: @@ -40,5 +58,6 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! -#clean-local: -# rm -rf $(builddir)/.deps +clean-local: + $(AM_V_at)rm -rf $(EXTRA_PROGRAMS) +# $(AM_V_at)rm -rf $(builddir)/.deps diff --git a/server/conf.c b/server/conf.c index 639b750220..3fa8393258 100644 --- a/server/conf.c +++ b/server/conf.c @@ -23,11 +23,24 @@ #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; +/* Debug level specified via command line - we revert to + * it when reloading if there was no DEBUG_MIN in upsd.conf + */ +int nut_debug_level_args = 0; + /* add another UPS for monitoring from ups.conf */ static void ups_create(const char *fn, const char *name, const char *desc) { @@ -51,6 +64,19 @@ static void ups_create(const char *fn, const char *name, const char *desc) temp->stale = 1; temp->retain = 1; +#ifdef WIN32 + memset(&temp->read_overlapped,0,sizeof(temp->read_overlapped)); + memset(temp->buf,0,sizeof(temp->buf)); + temp->read_overlapped.hEvent = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset*/ + FALSE, /* initial state = non signaled */ + NULL /* no name */); + if(temp->read_overlapped.hEvent == NULL ) { + upslogx(LOG_ERR, "Can't create event for UPS [%s]", + name); + return; + } +#endif temp->sock_fd = sstate_connect(temp); /* preload this to the current time to avoid false staleness */ @@ -91,8 +117,12 @@ static void ups_update(const char *fn, const char *name, const char *desc) sstate_cmdfree(temp); pconf_finish(&temp->sock_ctx); +#ifndef WIN32 close(temp->sock_fd); - temp->sock_fd = -1; +#else + CloseHandle(temp->sock_fd); +#endif + temp->sock_fd = ERROR_FD; temp->dumpdone = 0; /* now redefine the filename and wrap up */ @@ -136,9 +166,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 +192,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; } @@ -160,9 +202,9 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) } } - /* ALLOW_NO_DEVICE */ + /* 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; } @@ -173,9 +215,22 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) return 0; } + /* ALLOW_NOT_ALL_LISTENERS */ + if (!strcmp(arg[0], "ALLOW_NOT_ALL_LISTENERS")) { + if (isdigit((size_t)arg[1][0])) { + allow_not_all_listeners = (atoi(arg[1]) != 0); /* non-zero arg is true here */ + return 1; + } + if (parse_boolean(arg[1], &allow_not_all_listeners)) + return 1; + + upslogx(LOG_ERR, "ALLOW_NOT_ALL_LISTENERS has non numeric and non boolean value (%s)!", arg[1]); + return 0; + } + /* 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; @@ -188,8 +243,16 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* STATEPATH */ if (!strcmp(arg[0], "STATEPATH")) { + const char *sp = getenv("NUT_STATEPATH"); + if (sp && strcmp(sp, arg[1])) { + /* Only warn if the two strings are not equal */ + upslogx(LOG_WARNING, + "Ignoring STATEPATH='%s' from configuration file, " + "in favor of NUT_STATEPATH='%s' environment variable", + NUT_STRARG(arg[1]), NUT_STRARG(sp)); + } free(statepath); - statepath = xstrdup(arg[1]); + statepath = xstrdup(sp ? sp : arg[1]); return 1; } @@ -217,7 +280,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; } @@ -296,6 +359,7 @@ void load_upsdconf(int reloading) { char fn[SMALLBUF]; PCONF_CTX_t ctx; + int numerrors = 0; snprintf(fn, sizeof(fn), "%s/upsd.conf", confpath()); @@ -313,10 +377,18 @@ 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", fn, ctx.linenum, ctx.errmsg); + numerrors++; continue; } @@ -334,11 +406,35 @@ void load_upsdconf(int reloading) snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); + numerrors++; upslogx(LOG_WARNING, "%s", errmsg); } } + 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; + } else { + /* DEBUG_MIN is absent or commented-away in ups.conf */ + upslogx(LOG_INFO, + "Applying debug level %d from " + "original command line arguments", + nut_debug_level_args); + nut_debug_level = nut_debug_level_args; + } + } + + /* FIXME: Per legacy behavior, we silently went on. + * Maybe should abort on unusable configs? + */ + if (numerrors) { + upslogx(LOG_ERR, "Encountered %d config errors, those entries were ignored", numerrors); + } + pconf_finish(&ctx); } @@ -454,8 +550,12 @@ static void delete_ups(upstype_t *target) else last->next = ptr->next; - if (ptr->sock_fd != -1) + if (VALID_FD(ptr->sock_fd)) +#ifndef WIN32 close(ptr->sock_fd); +#else + CloseHandle(ptr->sock_fd); +#endif /* release memory */ sstate_infofree(ptr); @@ -516,7 +616,7 @@ void conf_reload(void) } /* reload from ups.conf */ - read_upsconf(); + read_upsconf(1); /* 1 = may abort upon fundamental errors */ upsconf_add(1); /* 1 = reloading */ /* now reread upsd.conf */ diff --git a/server/conf.h b/server/conf.h index 40341389fe..46bc5dbadb 100644 --- a/server/conf.h +++ b/server/conf.h @@ -51,6 +51,8 @@ void delete_acls(void); void delete_access(void); extern int num_ups; +extern int nut_debug_level_global; +extern int nut_debug_level_args; #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/netget.c b/server/netget.c index a5f822a569..c2a31fa354 100644 --- a/server/netget.c +++ b/server/netget.c @@ -165,7 +165,7 @@ static void get_var_server(nut_ctype_t *client, const char *upsname, const char if (!strcasecmp(var, "server.info")) { sendback(client, "VAR %s server.info " "\"Network UPS Tools upsd %s - " - "http://www.networkupstools.org/\"\n", + "https://www.networkupstools.org/\"\n", upsname, UPS_VERSION); return; } diff --git a/server/netmisc.c b/server/netmisc.c index 106d828709..ec4e8d28df 100644 --- a/server/netmisc.c +++ b/server/netmisc.c @@ -37,7 +37,7 @@ void net_ver(nut_ctype_t *client, size_t numarg, const char **arg) return; } - sendback(client, "Network UPS Tools upsd %s - http://www.networkupstools.org/\n", + sendback(client, "Network UPS Tools upsd %s - https://www.networkupstools.org/\n", UPS_VERSION); } diff --git a/server/netssl.c b/server/netssl.c index 6360117de5..d92fb1ba89 100644 --- a/server/netssl.c +++ b/server/netssl.c @@ -23,9 +23,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ +#include "common.h" /* for upsdebugx() etc */ + #include +#ifndef WIN32 #include #include +#else +#include "wincompat.h" +#endif #include "upsd.h" #include "neterr.h" @@ -136,7 +143,7 @@ static int ssl_error(SSL *ssl, ssize_t ret) int e; if (ret >= INT_MAX) { - upslogx(LOG_ERR, "ssl_error() ret=%zd would not fit in an int", ret); + upslogx(LOG_ERR, "ssl_error() ret=%" PRIiSIZE " would not fit in an int", ret); return -1; } e = SSL_get_error(ssl, (int)ret); @@ -144,23 +151,23 @@ static int ssl_error(SSL *ssl, ssize_t ret) switch (e) { case SSL_ERROR_WANT_READ: - upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_READ", ret); + upsdebugx(1, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_WANT_READ", ret); break; case SSL_ERROR_WANT_WRITE: - upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_WRITE", ret); + upsdebugx(1, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_WANT_WRITE", ret); break; case SSL_ERROR_SYSCALL: if (ret == 0 && ERR_peek_error() == 0) { upsdebugx(1, "ssl_error() EOF from client"); } else { - upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_SYSCALL", ret); + upsdebugx(1, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR_SYSCALL", ret); } break; default: - upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR %d", ret, e); + upsdebugx(1, "ssl_error() ret=%" PRIiSIZE " SSL_ERROR %d", ret, e); ssl_debug(); } @@ -189,9 +196,16 @@ static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry, static void nss_error(const char* text) { char buffer[SMALLBUF]; - PRInt32 length = PR_GetErrorText(buffer); - if (length > 0 && length < SMALLBUF) { - upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), text, buffer); + PRErrorCode err_num = PR_GetError(); + PRInt32 err_len = PR_GetErrorTextLength(); + + if (err_len > 0) { + if (err_len < SMALLBUF) { + PR_GetErrorText(buffer); + upsdebugx(1, "nss_error %ld in %s : %s", (long)err_num, text, buffer); + }else{ + upsdebugx(1, "nss_error %ld in %s : Internal error buffer too small, needs %ld bytes", (long)err_num, text, (long)err_len); + } }else{ upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), text); } @@ -200,17 +214,21 @@ static void nss_error(const char* text) static int ssl_error(PRFileDesc *ssl, ssize_t ret) { char buffer[256]; + PRErrorCode err_num = PR_GetError(); + PRInt32 err_len = PR_GetErrorTextLength(); PRInt32 length; - PRErrorCode e; NUT_UNUSED_VARIABLE(ssl); NUT_UNUSED_VARIABLE(ret); - e = PR_GetError(); - length = PR_GetErrorText(buffer); - if (length > 0 && length < 256) { - upsdebugx(1, "ssl_error() ret=%d %*s", e, length, buffer); - } else { - upsdebugx(1, "ssl_error() ret=%d", e); + if (err_len > 0) { + if (err_len < SMALLBUF) { + length = PR_GetErrorText(buffer); + upsdebugx(1, "ssl_error %ld : %*s", (long)err_num, length, buffer); + }else{ + upsdebugx(1, "ssl_error %ld : Internal error buffer too small, needs %ld bytes", (long)err_num, (long)err_len); + } + }else{ + upsdebugx(1, "ssl_error %ld", (long)err_num); } return -1; @@ -323,6 +341,7 @@ void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg) upslog_with_errno(LOG_ERR, "SSL_accept do not accept handshake."); ssl_error(client->ssl, ret); break; + case -1: upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept"); ssl_error(client->ssl, ret); @@ -332,59 +351,66 @@ void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg) #elif defined(WITH_NSS) /* WITH_OPENSSL */ socket = PR_ImportTCPSocket(client->sock_fd); - if (socket == NULL){ - upslogx(LOG_ERR, "Can not inialize SSL connection"); + if (socket == NULL) { + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / PR_ImportTCPSocket"); return; } client->ssl = SSL_ImportFD(NULL, socket); - if (client->ssl == NULL){ - upslogx(LOG_ERR, "Can not inialize SSL connection"); + if (client->ssl == NULL) { + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_ImportFD"); return; } - if (SSL_SetPKCS11PinArg(client->ssl, client) == -1){ - upslogx(LOG_ERR, "Can not inialize SSL connection"); + if (SSL_SetPKCS11PinArg(client->ssl, client) == -1) { + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_SetPKCS11PinArg"); return; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" +#endif /* Note cast to SSLAuthCertificate to prevent warning due to * bad function prototype in NSS. */ status = SSL_AuthCertificateHook(client->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB()); if (status != SECSuccess) { - upslogx(LOG_ERR, "Can not inialize SSL connection"); + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_AuthCertificateHook"); return; } status = SSL_BadCertHook(client->ssl, (SSLBadCertHandler)BadCertHandler, client); if (status != SECSuccess) { - upslogx(LOG_ERR, "Can not inialize SSL connection"); + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_BadCertHook"); return; } status = SSL_HandshakeCallback(client->ssl, (SSLHandshakeCallback)HandshakeCallback, client); if (status != SECSuccess) { - upslogx(LOG_ERR, "Can not inialize SSL connection"); + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_HandshakeCallback"); return; } status = SSL_ConfigSecureServer(client->ssl, cert, privKey, NSS_FindCertKEAType(cert)); if (status != SECSuccess) { - upslogx(LOG_ERR, "Can not inialize SSL connection"); + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_ConfigSecureServer"); return; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_FUNCTION_TYPE_STRICT) +#pragma GCC diagnostic pop +#endif status = SSL_ResetHandshake(client->ssl, PR_TRUE); if (status != SECSuccess) { - upslogx(LOG_ERR, "Can not inialize SSL connection"); + upslogx(LOG_ERR, "Can not initialize SSL connection"); nss_error("net_starttls / SSL_ResetHandshake"); return; } @@ -494,24 +520,21 @@ void ssl_init(void) PK11_SetPasswordFunc(nss_password_callback); - if (certfile) - /* Note: this call can generate memory leaks not resolvable - * by any release function. - * Probably NSS key module object allocation and - * probably NSS key db object allocation too. */ - status = NSS_Init(certfile); - else - status = NSS_NoDB_Init(NULL); + /* Note: this call can generate memory leaks not resolvable + * by any release function. + * Probably NSS key module object allocation and + * probably NSS key db object allocation too. */ + status = NSS_Init(certfile); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL context"); - nss_error("upscli_init / NSS_[NoDB]_Init"); + nss_error("ssl_init / NSS_Init"); return; } status = NSS_SetDomesticPolicy(); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL policy"); - nss_error("upscli_init / NSS_SetDomesticPolicy"); + nss_error("ssl_init / NSS_SetDomesticPolicy"); return; } @@ -519,7 +542,7 @@ void ssl_init(void) status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not initialize SSL server cache"); - nss_error("upscli_init / SSL_ConfigServerSessionIDCache"); + nss_error("ssl_init / SSL_ConfigServerSessionIDCache"); return; } @@ -527,13 +550,13 @@ void ssl_init(void) status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable SSLv3"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)"); return; } status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable TLSv1"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); return; } } else { @@ -541,7 +564,7 @@ void ssl_init(void) status = SSL_VersionRangeGetSupported(ssl_variant_stream, &range); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not get versions supported"); - nss_error("upscli_init / SSL_VersionRangeGetSupported"); + nss_error("ssl_init / SSL_VersionRangeGetSupported"); return; } range.min = SSL_LIBRARY_VERSION_TLS_1_1; @@ -551,7 +574,7 @@ void ssl_init(void) status = SSL_VersionRangeSetDefault(ssl_variant_stream, &range); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not set versions supported"); - nss_error("upscli_init / SSL_VersionRangeSetDefault"); + nss_error("ssl_init / SSL_VersionRangeSetDefault"); return; } /* Disable old/weak ciphers */ @@ -563,13 +586,13 @@ void ssl_init(void) status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not disable SSLv3"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_DISABLE_SSL3)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_DISABLE_SSL3)"); return; } status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable TLSv1"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); return; } #endif @@ -587,7 +610,7 @@ void ssl_init(void) status = SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable certificate request"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)"); return; } } @@ -596,7 +619,7 @@ void ssl_init(void) status = SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE, PR_TRUE); if (status != SECSuccess) { upslogx(LOG_ERR, "Can not enable certificate requirement"); - nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)"); + nss_error("ssl_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)"); return; } } @@ -605,14 +628,14 @@ void ssl_init(void) cert = PK11_FindCertFromNickname(certname, NULL); if(cert==NULL) { upslogx(LOG_ERR, "Can not find server certificate"); - nss_error("upscli_init / PK11_FindCertFromNickname"); + nss_error("ssl_init / PK11_FindCertFromNickname"); return; } privKey = PK11_FindKeyByAnyCert(cert, NULL); if(privKey==NULL){ upslogx(LOG_ERR, "Can not find private key associate to server certificate"); - nss_error("upscli_init / PK11_FindKeyByAnyCert"); + nss_error("ssl_init / PK11_FindKeyByAnyCert"); return; } @@ -634,6 +657,9 @@ void ssl_init(void) ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen) { ssize_t ret = -1; +#ifdef WITH_OPENSSL + int iret; +#endif if (!client->ssl_connected) { return -1; @@ -646,7 +672,7 @@ ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen) * but smaller systems with 16-bits might be endangered :) */ assert(buflen <= INT_MAX); - int iret = SSL_read(client->ssl, buf, (int)buflen); + iret = SSL_read(client->ssl, buf, (int)buflen); assert(iret <= SSIZE_MAX); ret = (ssize_t)iret; #elif defined(WITH_NSS) /* WITH_OPENSSL */ @@ -667,6 +693,9 @@ ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen) ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) { ssize_t ret = -1; +#ifdef WITH_OPENSSL + int iret; +#endif if (!client->ssl_connected) { return -1; @@ -679,7 +708,7 @@ ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) * but smaller systems with 16-bits might be endangered :) */ assert(buflen <= INT_MAX); - int iret = SSL_write(client->ssl, buf, (int)buflen); + iret = SSL_write(client->ssl, buf, (int)buflen); assert(iret <= SSIZE_MAX); ret = (ssize_t)iret; #elif defined(WITH_NSS) /* WITH_OPENSSL */ @@ -689,7 +718,7 @@ ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) ret = PR_Write(client->ssl, buf, (PRInt32)buflen); #endif /* WITH_OPENSSL | WITH_NSS */ - upsdebugx(5, "ssl_write ret=%zd", ret); + upsdebugx(5, "ssl_write ret=%" PRIiSIZE, ret); return ret; } 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/nut_ctype.h b/server/nut_ctype.h index db9f4dfb32..25cead0754 100644 --- a/server/nut_ctype.h +++ b/server/nut_ctype.h @@ -48,7 +48,7 @@ extern "C" { /* client structure */ typedef struct nut_ctype_s { char *addr; - int sock_fd; + TYPE_FD_SOCK sock_fd; time_t last_heard; char *loginups; char *password; @@ -71,6 +71,9 @@ typedef struct nut_ctype_s { /* doubly linked list */ struct nut_ctype_s *prev; struct nut_ctype_s *next; +#ifdef WIN32 + HANDLE Event; +#endif } nut_ctype_t; #ifdef __cplusplus diff --git a/server/pipedebug.c b/server/pipedebug.c new file mode 100644 index 0000000000..53007449ff --- /dev/null +++ b/server/pipedebug.c @@ -0,0 +1,179 @@ +/* pipe.c - Network UPS Tools driver-server pipe debugger (WIN32 builds) + + Copyright (C) 2012 Frederic Bohe + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include "common.h" +#include "parseconf.h" + + +PCONF_CTX_t pipe_ctx; + +static void pipe_arg(int numarg, char **arg) +{ + int i; + + printf("numarg=%d : ", numarg); + + for (i = 0; i < numarg; i++) + printf("[%s] ", arg[i]); + + printf("\n"); + fflush(stdout); +} + +static HANDLE pipe_connect(const char *pipefn) +{ + HANDLE fd; + char pipename[SMALLBUF]; + BOOL result = FALSE; + + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s", pipefn); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if( result == FALSE ) { + printf("WaitNamedPipe : %d\n",GetLastError()); + exit(EXIT_FAILURE); + } + + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + printf("CreateFile : %d\n",GetLastError()); + exit(EXIT_FAILURE); + } + + return fd; +} + +static void read_buf(char * buf, DWORD num) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + + switch (pconf_char(&pipe_ctx, buf[i])) { + case 1: + pipe_arg(pipe_ctx.numargs, pipe_ctx.arglist); + break; + + case -1: + printf("Parse error: [%s]\n", pipe_ctx.errmsg); + break; + } + } +} + +DWORD WINAPI ReadThread( LPVOID lpParameter ) +{ + HANDLE pipefd = *((HANDLE *)lpParameter); + DWORD bytes_read; + char pipe_buf[SMALLBUF]; + OVERLAPPED pipe_overlapped; + + pconf_init(&pipe_ctx, NULL); + + memset(&pipe_overlapped,0,sizeof(pipe_overlapped)); + pipe_overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + + for (;;) { + memset(pipe_buf,0,sizeof(pipe_buf)); + ReadFile(pipefd,pipe_buf,sizeof(pipe_buf),NULL,&pipe_overlapped); + GetOverlappedResult(pipefd,&pipe_overlapped,&bytes_read,TRUE); + read_buf(pipe_buf,bytes_read); + } +} + +DWORD WINAPI WriteThread( LPVOID lpParameter ) +{ + HANDLE pipefd = *((HANDLE *)lpParameter); + HANDLE hStdin; + DWORD bytes_read; + char stdin_buf[SMALLBUF]; + OVERLAPPED pipe_overlapped; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + + memset(&pipe_overlapped,0,sizeof(pipe_overlapped)); + pipe_overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); + + for (;;) { + ReadFile(hStdin,stdin_buf,sizeof(stdin_buf),&bytes_read,NULL); + WriteFile(pipefd,stdin_buf,bytes_read,NULL,&pipe_overlapped); + } +} +int main(int argc, char **argv) +{ + const char *prog = xbasename(argv[0]); + HANDLE pipefd; + HANDLE thread[2]; + + if (argc != 2 + || (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) + ) { + fprintf(stderr, "usage: %s \n", prog); + fprintf(stderr, " %s apcsmart-com1\n", + argv[0]); + exit(EXIT_SUCCESS); + } + + pipefd = pipe_connect(argv[1]); + + printf("connected: fd %d\n", pipefd); + fflush(stdout); + + thread[0] = CreateThread( + NULL, /* security */ + 0, /* stack size */ + ReadThread, /* func */ + &pipefd,/* func param */ + 0, /* flags */ + NULL ); /* thread id */ + + if(thread[0] == NULL) { + fprintf(stderr, "CreateThread ReadThread failed\n"); + exit(EXIT_FAILURE); + } + + thread[1] = CreateThread( + NULL, /* security */ + 0, /* stack size */ + WriteThread, /* func */ + &pipefd,/* func param */ + 0, /* flags */ + NULL ); /* thread id */ + + if(thread[1] == NULL) { + fprintf(stderr, "CreateThread WriteThread failed\n"); + exit(EXIT_FAILURE); + } + + WaitForMultipleObjects(2,thread,TRUE,INFINITE); + + /* NOTREACHED */ + exit(EXIT_FAILURE); +} diff --git a/server/sockdebug.c b/server/sockdebug.c index 031e02cc36..a86868b6bc 100644 --- a/server/sockdebug.c +++ b/server/sockdebug.c @@ -1,6 +1,8 @@ /* sockdebug.c - Network UPS Tools driver-server socket debugger + Source variant for POSIX-compliant builds of NUT Copyright (C) 2003 Russell Kroll + Copyright (C) 2023 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "common.h" #include #include @@ -26,16 +29,16 @@ #include #include -#include "common.h" #include "parseconf.h" +#include "nut_stdint.h" - PCONF_CTX_t sock_ctx; +static PCONF_CTX_t sock_ctx; static void sock_arg(size_t numarg, char **arg) { size_t i; - printf("numarg=%zu : ", numarg); + printf("numarg=%" PRIuSIZE " : ", numarg); for (i = 0; i < numarg; i++) printf("[%s] ", arg[i]); @@ -48,6 +51,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); @@ -61,6 +66,12 @@ static int socket_connect(const char *sockfn) ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); + if (ret < 0 && !strchr(sockfn, '/')) { + snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", + dflt_statepath(), sockfn); + ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); + } + if (ret < 0) { perror("connect"); exit(EXIT_FAILURE); @@ -121,10 +132,15 @@ int main(int argc, char **argv) const char *prog = xbasename(argv[0]); int ret, sockfd; - if (argc != 2) { + if (argc != 2 + || (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) + ) { fprintf(stderr, "usage: %s \n", prog); - fprintf(stderr, " %s /var/state/ups/apcsmart-ttyS1.newsock\n", + fprintf(stderr, " %s /var/state/ups/apcsmart-ttyS1\n", + argv[0]); + fprintf(stderr, " or %s apcsmart-ttyS1\n", argv[0]); + fprintf(stderr, " for socket files placed in the standard location\n"); exit(EXIT_SUCCESS); } @@ -156,7 +172,10 @@ int main(int argc, char **argv) if (FD_ISSET(fileno(stdin), &rfds)) { char buf[SMALLBUF]; - fgets(buf, sizeof(buf), stdin); + if (!fgets(buf, sizeof(buf), stdin)) { + perror("fgets from stdin"); + exit(EXIT_FAILURE); + } ret = write(sockfd, buf, strlen(buf)); @@ -167,6 +186,20 @@ int main(int argc, char **argv) } } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +#endif /* NOTREACHED */ exit(EXIT_FAILURE); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +#endif } diff --git a/server/sstate.c b/server/sstate.c index f33df625ba..5da0834e29 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -34,8 +34,10 @@ #include #include #include +#ifndef WIN32 #include #include +#endif static int parse_args(upstype_t *ups, size_t numargs, char **arg) { @@ -54,11 +56,13 @@ static int parse_args(upstype_t *ups, size_t numargs, char **arg) } if (!strcasecmp(arg[0], "DATASTALE")) { + upsdebugx(3, "UPS [%s]: data is STALE now", ups->name); ups->data_ok = 0; return 1; } if (!strcasecmp(arg[0], "DATAOK")) { + upsdebugx(3, "UPS [%s]: data is NOT STALE now", ups->name); ups->data_ok = 1; return 1; } @@ -155,12 +159,27 @@ static void sendping(upstype_t *ups) const char *cmd = "PING\n"; size_t cmdlen = strlen(cmd); - if ((!ups) || (ups->sock_fd < 0)) { + if ((!ups) || INVALID_FD(ups->sock_fd)) { return; } upsdebugx(3, "Pinging UPS [%s]", ups->name); + +#ifndef WIN32 ret = write(ups->sock_fd, cmd, cmdlen); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (ups->sock_fd, cmd, cmdlen, &bytesWritten, NULL); + if( result == 0 ) { + /* Write failed */ + ret = 0; + } + else { + ret = (ssize_t)bytesWritten; + } +#endif if ((ret < 1) || (ret != (ssize_t)cmdlen)) { upslog_with_errno(LOG_NOTICE, "Send ping to UPS [%s] failed", ups->name); @@ -173,23 +192,27 @@ static void sendping(upstype_t *ups) /* interface */ -int sstate_connect(upstype_t *ups) +TYPE_FD sstate_connect(upstype_t *ups) { - int fd; + TYPE_FD fd; +#ifndef WIN32 const char *dumpcmd = "DUMPALL\n"; size_t dumpcmdlen = strlen(dumpcmd); ssize_t ret; struct sockaddr_un sa; + upsdebugx(2, "%s: preparing UNIX socket %s", __func__, NUT_STRARG(ups->fn)); + 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); fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { + if (INVALID_FD(fd)) { upslog_with_errno(LOG_ERR, "Can't create socket for UPS [%s]", ups->name); - return -1; + return ERROR_FD; } ret = connect(fd, (struct sockaddr *) &sa, sizeof(sa)); @@ -197,18 +220,20 @@ int sstate_connect(upstype_t *ups) if (ret < 0) { time_t now; + upsdebugx(2, "%s: failed to connect() UNIX socket %s (%s)", + __func__, NUT_STRARG(ups->fn), sa.sun_path); close(fd); /* rate-limit complaints - don't spam the syslog */ time(&now); if (difftime(now, ups->last_connfail) < SS_CONNFAIL_INT) - return -1; + return ERROR_FD; ups->last_connfail = now; upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", ups->name, ups->fn); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_GETFL, 0); @@ -216,7 +241,7 @@ int sstate_connect(upstype_t *ups) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl get on UPS [%s] failed", ups->name); close(fd); - return -1; + return ERROR_FD; } ret = fcntl(fd, F_SETFL, ret | O_NDELAY); @@ -224,7 +249,7 @@ int sstate_connect(upstype_t *ups) if (ret < 0) { upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on UPS [%s] failed", ups->name); close(fd); - return -1; + return ERROR_FD; } /* get a dump started so we have a fresh set of data */ @@ -233,9 +258,58 @@ int sstate_connect(upstype_t *ups) if ((ret < 1) || (ret != (ssize_t)dumpcmdlen)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", ups->name); close(fd); - return -1; + return ERROR_FD; + } + +#else + char pipename[SMALLBUF]; + const char *dumpcmd = "DUMPALL\n"; + BOOL result = FALSE; + + upsdebugx(2, "%s: preparing Windows pipe %s", __func__, NUT_STRARG(ups->fn)); + snprintf(pipename, sizeof(pipename), "\\\\.\\pipe\\%s", ups->fn); + + result = WaitNamedPipe(pipename,NMPWAIT_USE_DEFAULT_WAIT); + + if (result == FALSE) { + upsdebugx(2, "%s: failed to WaitNamedPipe(%s)", + __func__, pipename); + return ERROR_FD; } + fd = CreateFile( + pipename, /* pipe name */ + GENERIC_READ | /* read and write access */ + GENERIC_WRITE, + 0, /* no sharing */ + NULL, /* default security attributes FIXME */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* enable async IO */ + NULL); /* no template file */ + + if (fd == INVALID_HANDLE_VALUE) { + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", ups->name, ups->fn); + return ERROR_FD; + } + + /* get a dump started so we have a fresh set of data */ + DWORD bytesWritten = 0; + + result = WriteFile (fd,dumpcmd,strlen(dumpcmd),&bytesWritten,NULL); + if (result == 0 || bytesWritten != strlen(dumpcmd)) { + upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", ups->name); + CloseHandle(fd); + return ERROR_FD; + } + + /* Start a read IO so we could wait on the event associated with it */ + ReadFile(fd, ups->buf, + sizeof(ups->buf) - 1, /*-1 to be sure to have a trailling 0 */ + NULL, &(ups->read_overlapped)); +#endif + + /* sstate_connect() continued for both platforms: */ + pconf_init(&ups->sock_ctx, NULL); ups->dumpdone = 0; @@ -254,7 +328,7 @@ int sstate_connect(upstype_t *ups) void sstate_disconnect(upstype_t *ups) { - if ((!ups) || (ups->sock_fd < 0)) { + if ((!ups) || INVALID_FD(ups->sock_fd)) { return; } @@ -263,16 +337,23 @@ void sstate_disconnect(upstype_t *ups) pconf_finish(&ups->sock_ctx); +#ifndef WIN32 close(ups->sock_fd); - ups->sock_fd = -1; +#else + CloseHandle(ups->sock_fd); +#endif + + ups->sock_fd = ERROR_FD; } void sstate_readline(upstype_t *ups) { ssize_t i, ret; + +#ifndef WIN32 char buf[SMALLBUF]; - if ((!ups) || (ups->sock_fd < 0)) { + if ((!ups) || INVALID_FD(ups->sock_fd)) { return; } @@ -291,6 +372,17 @@ void sstate_readline(upstype_t *ups) return; } } +#else + if ((!ups) || INVALID_FD(ups->sock_fd)) { + return; + } + + /* FIXME? I do not see either buf filled below */ + char *buf = ups->buf; + DWORD bytesRead; + GetOverlappedResult(ups->sock_fd, &ups->read_overlapped, &bytesRead, FALSE); + ret = bytesRead; +#endif for (i = 0; i < ret; i++) { @@ -312,6 +404,12 @@ void sstate_readline(upstype_t *ups) return; } } + +#ifdef WIN32 + /* Restart async read */ + memset(ups->buf,0,sizeof(ups->buf)); + ReadFile( ups->sock_fd, ups->buf, sizeof(ups->buf)-1,NULL, &(ups->read_overlapped)); /* -1 to be sure to have a trailing 0 */ +#endif } const char *sstate_getinfo(const upstype_t *ups, const char *var) @@ -350,22 +448,18 @@ int sstate_dead(upstype_t *ups, int arg_maxage) double elapsed; /* an unconnected ups is always dead */ - if (ups->sock_fd < 0) { + if ((!ups) || INVALID_FD(ups->sock_fd)) { upsdebugx(3, "sstate_dead: connection to driver socket for UPS [%s] lost", ups->name); return 1; /* dead */ } time(&now); - /* ignore DATAOK/DATASTALE unless the dump is done */ - if ((ups->dumpdone) && (!ups->data_ok)) { - upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", ups->name); - return 1; /* dead */ - } - elapsed = difftime(now, ups->last_heard); - /* somewhere beyond a third of the maximum time - prod it to make it talk */ + /* Somewhere beyond a third of the maximum time - prod it to make it talk + * Note this helps detect drivers that died without closing the connection + */ if ((elapsed > (arg_maxage / 3)) && (difftime(now, ups->last_ping) > (arg_maxage / 3))) sendping(ups); @@ -375,6 +469,12 @@ int sstate_dead(upstype_t *ups, int arg_maxage) return 1; /* dead */ } + /* ignore DATAOK/DATASTALE unless the dump is done */ + if ((ups->dumpdone) && (!ups->data_ok)) { + upsdebugx(3, "sstate_dead: driver for UPS [%s] says data is stale", ups->name); + return 1; /* dead */ + } + return 0; } @@ -398,7 +498,7 @@ int sstate_sendline(upstype_t *ups, const char *buf) ssize_t ret; size_t buflen; - if ((!ups) ||(ups->sock_fd < 0)) { + if ((!ups) || INVALID_FD(ups->sock_fd)) { return 0; /* failed */ } @@ -409,7 +509,21 @@ int sstate_sendline(upstype_t *ups, const char *buf) return 0; /* failed */ } +#ifndef WIN32 ret = write(ups->sock_fd, buf, buflen); +#else + DWORD bytesWritten = 0; + BOOL result = FALSE; + + result = WriteFile (ups->sock_fd, buf, buflen, &bytesWritten, NULL); + + if (result == 0) { + ret = 0; + } + else { + ret = (ssize_t)bytesWritten; + } +#endif if (ret == (ssize_t)buflen) { return 1; diff --git a/server/sstate.h b/server/sstate.h index 7a7fff6bbd..dde724a035 100644 --- a/server/sstate.h +++ b/server/sstate.h @@ -23,6 +23,7 @@ #ifndef NUT_SSTATE_H_SEEN #define NUT_SSTATE_H_SEEN 1 +#include "common.h" /* TYPE_FD */ #include "state.h" #include "upstype.h" @@ -35,7 +36,7 @@ extern "C" { /* *INDENT-ON* */ #endif -int sstate_connect(upstype_t *ups); +TYPE_FD sstate_connect(upstype_t *ups); void sstate_disconnect(upstype_t *ups); void sstate_readline(upstype_t *ups); const char *sstate_getinfo(const upstype_t *ups, const char *var); diff --git a/server/stype.h b/server/stype.h index 54e5ff29e5..a4942dcd45 100644 --- a/server/stype.h +++ b/server/stype.h @@ -21,7 +21,11 @@ #ifndef NUT_STYPE_H_SEEN #define NUT_STYPE_H_SEEN 1 +#include "common.h" + +#ifndef WIN32 #include +#endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025 @@ -40,7 +44,10 @@ extern "C" { typedef struct stype_s { char *addr; char *port; - int sock_fd; + TYPE_FD_SOCK sock_fd; +#ifdef WIN32 + HANDLE Event; +#endif struct stype_s *next; } stype_t; diff --git a/server/upsd.c b/server/upsd.c index 85249a3993..bd3a1543f7 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -30,15 +30,28 @@ #include "netcmds.h" #include "upsconf.h" -#include -#include -#include - -#ifdef HAVE_SYS_SIGNAL_H -#include -#endif -#ifdef HAVE_SIGNAL_H -#include +#ifndef WIN32 +# include +# include +# include + +# ifdef HAVE_SYS_SIGNAL_H +# include +# endif +# ifdef HAVE_SIGNAL_H +# include +/* #include */ +# endif +#else +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +# include +# include +/* This override network system calls to adapt to Windows specificity */ +# define W32_NETWORK_CALL_OVERRIDE +# include "wincompat.h" +# undef W32_NETWORK_CALL_OVERRIDE +# include #endif #include "user.h" @@ -72,13 +85,19 @@ int tracking_delay = 3600; */ int allow_no_device = 0; +/* + * Preloaded to ALLOW_NOT_ALL_LISTENERS from upsd.conf or environment variable + * (with higher prio for envvar); defaults to disabled for legacy compat. + */ +int allow_not_all_listeners = 0; + /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ nfds_t maxconn = 0; /* preloaded to STATEPATH in main, can be overridden via upsd.conf */ char *statepath = NULL; -/* preloaded to DATADIR in main, can be overridden via upsd.conf */ +/* preloaded to NUT_DATADIR in main(), can be overridden via upsd.conf */ char *datapath = NULL; /* everything else */ @@ -96,6 +115,10 @@ typedef enum { DRIVER = 1, CLIENT, SERVER +#ifdef WIN32 + ,NAMED_PIPE +#endif + } handler_type_t; typedef struct { @@ -103,7 +126,6 @@ typedef struct { void *data; } handler_t; - /* Commands and settings status tracking */ /* general enable/disable status info for commands and settings @@ -124,9 +146,13 @@ typedef struct tracking_s { static tracking_t *tracking_list = NULL; - +#ifndef WIN32 /* pollfd */ static struct pollfd *fds = NULL; +#else +static HANDLE *fds = NULL; +static HANDLE mutex = INVALID_HANDLE_VALUE; +#endif static handler_t *handler = NULL; /* pid file */ @@ -139,6 +165,9 @@ static int reload_flag = 0, exit_flag = 0; /* Ref: RFC 4122 https://tools.ietf.org/html/rfc4122#section-4.1.2 */ #define UUID4_BYTESIZE 16 +#ifdef HAVE_SYSTEMD +# define SERVICE_UNIT_NAME "nut-server.service" +#endif static const char *inet_ntopW (struct sockaddr_storage *s) { @@ -213,21 +242,197 @@ void listen_add(const char *addr, const char *port) server = xcalloc(1, sizeof(*server)); server->addr = xstrdup(addr); server->port = xstrdup(port); - server->sock_fd = -1; - server->next = firstaddr; + server->sock_fd = ERROR_FD_SOCK; + server->next = NULL; - firstaddr = server; + if (firstaddr) { + stype_t *tmp; + for (tmp = firstaddr; tmp->next; tmp = tmp->next); + tmp->next = server; + } else { + firstaddr = server; + } upsdebugx(3, "listen_add: added %s:%s", server->addr, server->port); } +/* Close the connection if needed and free the allocated memory. + * WARNING: it is up to the caller to rewrite the "next" pointer + * in whoever points to this server instance (if needed)! */ +static void stype_free(stype_t *server) +{ + if (VALID_FD_SOCK(server->sock_fd)) { + close(server->sock_fd); + } + + free(server->addr); + free(server->port); + free(server); +} + /* create a listening socket for tcp connections */ static void setuptcp(stype_t *server) { +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif struct addrinfo hints, *res, *ai; int v = 0, one = 1; + if (VALID_FD_SOCK(server->sock_fd)) { + /* Already bound, e.g. thanks to 'LISTEN *' handling and injection + * into the list we loop over */ + upsdebugx(6, "setuptcp: SKIP bind to %s port %s: entry already initialized", + server->addr, server->port); + return; + } + upsdebugx(3, "setuptcp: try to bind to %s port %s", server->addr, server->port); + if (!strcmp(server->addr, "localhost")) { + /* Warn about possible surprises with IPv4 vs. IPv6 */ + upsdebugx(1, + "setuptcp: WARNING: requested to LISTEN on 'localhost' " + "by name - will use the first system-resolved " + "IP address for that"); + } + + /* Special handling note for `LISTEN * ` directive with the + * literal asterisk on systems with RFC-3493 (no relation!) support + * for "IPv4-mapped addresses": it is possible (and technically + * suffices) to LISTEN on "::" (aka "::0" or "0:0:0:0:0:0:0:0") and + * also get an IPv4 any-address listener automatically. More so, + * they would conflict and listening on one such socket precludes + * listening on the other. On other systems (or with disabled + * mapping so IPv6 really means "IPv6 only") we need both sockets. + * NUT asks the system for "IPv6 only" mode when listening on any + * sort of IPv6 addresses; it is however up to the system to implement + * that ability and comply with our request. + * Here we jump through some hoops: + * * Try to get IPv6 any-address (unless constrained by CLI to IPv4); + * * Try to get IPv4 any-address (unless constrained by CLI to IPv6), + * log information for the sysadmin that it might conflict with the + * IPv6 listener (IFF we have just opened one); + * * Remember the one or two linked-list entries used, to release later. + */ + if (!strcmp(server->addr, "*")) { + stype_t *serverAnyV4 = NULL, *serverAnyV6 = NULL; + int canhaveAnyV4 = 0, canhaveAnyV6 = 0; + + /* Note: default opt_af==AF_UNSPEC so not constrained to only one protocol */ + if (opt_af != AF_INET6) { + /* Not constrained to IPv6 */ + upsdebugx(1, "%s: handling 'LISTEN * %s' with IPv4 any-address support", + __func__, server->port); + serverAnyV4 = xcalloc(1, sizeof(*serverAnyV4)); + serverAnyV4->addr = xstrdup("0.0.0.0"); + serverAnyV4->port = xstrdup(server->port); + serverAnyV4->sock_fd = ERROR_FD_SOCK; + serverAnyV4->next = NULL; + } + + if (opt_af != AF_INET) { + /* Not constrained to IPv4 */ + upsdebugx(1, "%s: handling 'LISTEN * %s' with IPv6 any-address support", + __func__, server->port); + serverAnyV6 = xcalloc(1, sizeof(*serverAnyV6)); + serverAnyV6->addr = xstrdup("::0"); + serverAnyV6->port = xstrdup(server->port); + serverAnyV6->sock_fd = ERROR_FD_SOCK; + serverAnyV6->next = NULL; + } + + if (serverAnyV6) { + setuptcp(serverAnyV6); + if (VALID_FD_SOCK(serverAnyV6->sock_fd)) { + canhaveAnyV6 = 1; + } else { + upsdebugx(3, + "%s: Could not bind to %s:%s trying to handle a 'LISTEN *' directive", + __func__, serverAnyV6->addr, serverAnyV6->port); + } + } + + if (serverAnyV4) { + /* Try to get this listener if we can (no IPv4-mapped + * IPv6 support was in force on this platform or its + * configuration in some way that setsockopt(IPV6_V6ONLY) + * failed to cancel). + */ + upsdebugx(3, "%s: try taking IPv4 'ANY'%s", + __func__, + canhaveAnyV6 ? " (if dual-stack IPv6 'ANY' did not grab it)" : ""); + setuptcp(serverAnyV4); + if (VALID_FD_SOCK(serverAnyV4->sock_fd)) { + canhaveAnyV4 = 1; + } else { + upsdebugx(3, + "%s: Could not bind to IPv4 %s:%s%s", + __func__, serverAnyV4->addr, serverAnyV4->port, + canhaveAnyV6 ? (" after trying to bind to IPv6: " + "assuming dual-stack support on this " + "system could not be disabled") : ""); + } + } + + if (!canhaveAnyV4 && !canhaveAnyV6) { + fatalx(EXIT_FAILURE, + "Handling of 'LISTEN * %s' directive failed to bind to 'ANY' address", + server->port); + } + + /* Finalize our findings and reset to normal operation + * Note that at least one of these addresses is usable + * and we keep it (and replace original "server" entry + * keeping its place in the list). + */ + free(server->addr); + free(server->port); + if (canhaveAnyV4) { + upsdebugx(3, "%s: remembering IPv4 'ANY' instead of 'LISTEN *'", __func__); + server->addr = serverAnyV4->addr; + server->port = serverAnyV4->port; + server->sock_fd = serverAnyV4->sock_fd; + /* ...and keep whatever server->next there was */ + + /* Free the ghost, all needed info was relocated */ + free(serverAnyV4); + } else { + if (serverAnyV4) { + /* Free any contents there were too */ + stype_free(serverAnyV4); + } + } + serverAnyV4 = NULL; + + if (canhaveAnyV6) { + if (canhaveAnyV4) { + /* "server" already populated by excerpts from V4, attach to it */ + upsdebugx(3, "%s: also remembering IPv6 'ANY' instead of 'LISTEN *'", __func__); + serverAnyV6->next = server->next; + server->next = serverAnyV6; + } else { + /* Only retain V6 info */ + upsdebugx(3, "%s: remembering IPv6 'ANY' instead of 'LISTEN *'", __func__); + server->addr = serverAnyV6->addr; + server->port = serverAnyV6->port; + server->sock_fd = serverAnyV6->sock_fd; + /* ...and keep whatever server->next there was */ + + /* Free the ghost, all needed info was relocated */ + free(serverAnyV6); + } + } else { + if (serverAnyV6) { + /* Free any contents there were too */ + stype_free(serverAnyV6); + } + } + serverAnyV6 = NULL; + + return; + } memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; @@ -244,9 +449,9 @@ static void setuptcp(stype_t *server) } for (ai = res; ai; ai = ai->ai_next) { - int sock_fd; + TYPE_FD_SOCK sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if ((sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) { + if (INVALID_FD_SOCK(sock_fd)) { upsdebug_with_errno(3, "setuptcp: socket"); continue; } @@ -255,12 +460,26 @@ static void setuptcp(stype_t *server) fatal_with_errno(EXIT_FAILURE, "setuptcp: setsockopt"); } + /* Ordinarily we request that IPv6 listeners handle only IPv6 + * and not IPv4 mapped addresses - if the OS would honour that. + * TOTHINK: Does any platform need `#ifdef IPV6_V6ONLY` given + * that we apparently already have AF_INET6 OS support everywhere? + */ + if (ai->ai_family == AF_INET6) { + if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&one, sizeof(one)) != 0) { + upsdebug_with_errno(3, "setuptcp: setsockopt IPV6_V6ONLY"); + /* ack, ignore */ + } + } + if (bind(sock_fd, ai->ai_addr, ai->ai_addrlen) < 0) { upsdebug_with_errno(3, "setuptcp: bind"); close(sock_fd); continue; } +/* WSAEventSelect automatically set the socket to nonblocking mode */ +#ifndef WIN32 if ((v = fcntl(sock_fd, F_GETFL, 0)) == -1) { fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(get)"); } @@ -268,6 +487,7 @@ static void setuptcp(stype_t *server) if (fcntl(sock_fd, F_SETFL, v | O_NDELAY) == -1) { fatal_with_errno(EXIT_FAILURE, "setuptcp: fcntl(set)"); } +#endif if (listen(sock_fd, 16) < 0) { upsdebug_with_errno(3, "setuptcp: listen"); @@ -275,15 +495,39 @@ static void setuptcp(stype_t *server) continue; } + if (ai->ai_next) { + char ipaddrbuf[SMALLBUF]; + const char *ipaddr; + snprintf(ipaddrbuf, sizeof(ipaddrbuf), " as "); + ipaddr = inet_ntop(ai->ai_family, ai->ai_addr, + ipaddrbuf + strlen(ipaddrbuf), + sizeof(ipaddrbuf)); + upslogx(LOG_WARNING, + "setuptcp: bound to %s%s but there seem to be " + "further (ignored) addresses resolved for this name", + server->addr, + ipaddr == NULL ? "" : ipaddrbuf); + } + server->sock_fd = sock_fd; break; } +#ifdef WIN32 + server->Event = CreateEvent(NULL, /* Security */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( server->sock_fd, server->Event, FD_ACCEPT ); +#endif + freeaddrinfo(res); /* leave up to the caller, server_load(), to fail silently if there is * no other valid LISTEN interface */ - if (server->sock_fd < 0) { + if (INVALID_FD_SOCK(server->sock_fd)) { upslogx(LOG_ERR, "not listening on %s port %s", server->addr, server->port); } else { upslogx(LOG_INFO, "listening on %s port %s", server->addr, server->port); @@ -323,6 +567,10 @@ static void client_disconnect(nut_ctype_t *client) shutdown(client->sock_fd, 2); close(client->sock_fd); +#ifdef WIN32 + CloseHandle(client->Event); +#endif + if (client->loginups) { declogins(client->loginups); } @@ -388,7 +636,10 @@ int sendback(nut_ctype_t *client, const char *fmt, ...) res = write(client->sock_fd, ans, len); } - upsdebugx(2, "write: [destfd=%d] [len=%zu] [%s]", client->sock_fd, len, str_rtrim(ans, '\n')); + { /* scoping */ + char * s = str_rtrim(ans, '\n'); + upsdebugx(2, "write: [destfd=%d] [len=%" PRIuSIZE "] [%s]", client->sock_fd, len, s); + } if (res < 0 || len != (size_t)res) { upslog_with_errno(LOG_NOTICE, "write() failed for %s", client->addr); @@ -435,7 +686,15 @@ void kick_login_clients(const char *upsname) /* make sure a UPS is sane - connected, with fresh data */ int ups_available(const upstype_t *ups, nut_ctype_t *client) { - if (ups->sock_fd < 0) { + if (!ups) { + /* Should never happen, but handle this + * just in case instead of segfaulting */ + upsdebugx(1, "%s: ERROR, called with a NULL ups pointer", __func__); + send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED); + return 0; + } + + if (INVALID_FD(ups->sock_fd)) { send_err(client, NUT_ERR_DRIVER_NOT_CONNECTED); return 0; } @@ -453,17 +712,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 +737,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); } @@ -536,6 +805,16 @@ static void client_connect(stype_t *server) client->tracking = 0; +#ifdef WIN32 + client->Event = CreateEvent(NULL, /* Security, */ + FALSE, /* auto-reset */ + FALSE, /* initial state */ + NULL); /* no name */ + + /* Associate socket event to the socket via its Event object */ + WSAEventSelect( client->sock_fd, client->Event, FD_READ ); +#endif + pconf_init(&client->ctx, NULL); if (firstclient) { @@ -611,14 +890,27 @@ static void client_readline(nut_ctype_t *client) void server_load(void) { stype_t *server; - - /* default behaviour if no LISTEN addres has been specified */ + size_t listenersTotal = 0, listenersValid = 0, + listenersTotalLocalhost = 0, listenersValidLocalhost = 0, + listenersLocalhostName = 0, + listenersLocalhostName6 = 0, + listenersLocalhostIPv4 = 0, + listenersLocalhostIPv6 = 0, + listenersValidLocalhostName = 0, + listenersValidLocalhostName6 = 0, + listenersValidLocalhostIPv4 = 0, + listenersValidLocalhostIPv6 = 0; + + /* default behaviour if no LISTEN address has been specified */ if (!firstaddr) { + /* Note: default opt_af==AF_UNSPEC so not constrained to only one protocol */ if (opt_af != AF_INET) { + upsdebugx(1, "%s: No LISTEN configuration provided, will try IPv6 localhost", __func__); listen_add("::1", string_const(PORT)); } if (opt_af != AF_INET6) { + upsdebugx(1, "%s: No LISTEN configuration provided, will try IPv4 localhost", __func__); listen_add("127.0.0.1", string_const(PORT)); } } @@ -627,10 +919,115 @@ void server_load(void) setuptcp(server); } + /* Account separately from setuptcp() because it can edit the list, + * e.g. when handling `LISTEN *` lines. + */ + for (server = firstaddr; server; server = server->next) { + listenersTotal++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValid++; + } + + if (!strcmp(server->addr, "localhost")) { + listenersLocalhostName++; + listenersTotalLocalhost++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostName++; + listenersValidLocalhost++; + } + } + + if (!strcmp(server->addr, "localhost6")) { + listenersLocalhostName6++; + listenersTotalLocalhost++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostName6++; + listenersValidLocalhost++; + } + } + + if (!strcmp(server->addr, "127.0.0.1")) { + listenersLocalhostIPv4++; + listenersTotalLocalhost++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostIPv4++; + listenersValidLocalhost++; + } + } + + if (!strcmp(server->addr, "::1")) { + listenersLocalhostIPv6++; + listenersTotalLocalhost++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostIPv6++; + listenersValidLocalhost++; + } + } + } + + upsdebugx(1, "%s: tried to set up %" PRIuSIZE + " listening sockets, succeeded with %" PRIuSIZE, + __func__, listenersTotal, listenersValid); + upsdebugx(3, "%s: ...of those related to localhost: " + "overall: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " + "by name: %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by name(6): %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by IPv4 addr: %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by IPv6 addr: %" PRIuSIZE "T/%" PRIuSIZE "S", + __func__, + listenersTotalLocalhost, listenersValidLocalhost, + listenersLocalhostName, listenersValidLocalhostName, + listenersLocalhostName6, listenersValidLocalhostName6, + listenersLocalhostIPv4, listenersValidLocalhostIPv4, + listenersLocalhostIPv6, listenersValidLocalhostIPv6 + ); + /* check if we have at least 1 valid LISTEN interface */ - if (firstaddr->sock_fd < 0) { + if (!listenersValid) { fatalx(EXIT_FAILURE, "no listening interface available"); } + + /* is everything requested - handled okay? */ + if (listenersTotal == listenersValid) + return; + + /* check for edge cases we can let slide */ + if ( (listenersTotal - listenersValid) == + (listenersTotalLocalhost - listenersValidLocalhost) + ) { + /* Note that we can also get into this situation + * when "dual-stack" IPv6 listener also handles + * IPv4 connections, and precludes successful + * setup of the IPv4 listener later. + * + * FIXME? Can we get into this situation the other + * way around - an IPv4 listener precluding the + * IPv6 one, so end-user actually lacks one of the + * requested connection types? + */ + upsdebugx(1, "%s: discrepancy corresponds to " + "addresses related to localhost; assuming " + "that it was attempted under several names " + "which resolved to same IP:PORT socket specs " + "(so only the first one of each succeeded)", + __func__); + return; + } + + if (allow_not_all_listeners) { + upslogx(LOG_WARNING, + "WARNING: some listening interfaces were " + "not available, but the ALLOW_NOT_ALL_LISTENERS " + "setting is active"); + } else { + upsdebugx(0, + "Reconcile available NUT server IP addresses " + "and LISTEN configuration, or consider the " + "ALLOW_NOT_ALL_LISTENERS setting!"); + fatalx(EXIT_FAILURE, + "Fatal error: some listening interfaces were " + "not available"); + } } void server_free(void) @@ -640,14 +1037,7 @@ void server_free(void) /* cleanup server fds */ for (server = firstaddr; server; server = snext) { snext = server->next; - - if (server->sock_fd != -1) { - close(server->sock_fd); - } - - free(server->addr); - free(server->port); - free(server); + stype_free(server); } firstaddr = NULL; @@ -669,10 +1059,19 @@ 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) { + if (VALID_FD(ups->sock_fd)) { +#ifndef WIN32 close(ups->sock_fd); +#else + DisconnectNamedPipe(ups->sock_fd); + CloseHandle(ups->sock_fd); +#endif + ups->sock_fd = ERROR_FD; } sstate_infofree(ups); @@ -711,38 +1110,51 @@ static void upsd_cleanup(void) free(fds); free(handler); + +#ifdef WIN32 + if (mutex != INVALID_HANDLE_VALUE) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } +#endif } static void poll_reload(void) { +#ifndef WIN32 long ret; + size_t maxalloc; ret = sysconf(_SC_OPEN_MAX); if ((intmax_t)ret < (intmax_t)maxconn) { fatalx(EXIT_FAILURE, "Your system limits the maximum number of connections to %ld\n" - "but you requested %jd. The server won't start until this\n" + "but you requested %" PRIdMAX ". The server won't start until this\n" "problem is resolved.\n", ret, (intmax_t)maxconn); } if (1 > maxconn) { fatalx(EXIT_FAILURE, - "You requested %jd as maximum number of connections.\n" + "You requested %" PRIdMAX " as maximum number of connections.\n" "The server won't start until this problem is resolved.\n", (intmax_t)maxconn); } /* How many items can we stuff into the array? */ - size_t maxalloc = SIZE_MAX / sizeof(void *); + maxalloc = SIZE_MAX / sizeof(void *); if ((uintmax_t)maxalloc < (uintmax_t)maxconn) { fatalx(EXIT_FAILURE, - "You requested %jd as maximum number of connections, but we can only allocate %zu.\n" + "You requested %" PRIdMAX " as maximum number of connections, but we can only allocate %" PRIuSIZE ".\n" "The server won't start until this problem is resolved.\n", (intmax_t)maxconn, maxalloc); } /* The checks above effectively limit that maxconn is in size_t range */ fds = xrealloc(fds, (size_t)maxconn * sizeof(*fds)); handler = xrealloc(handler, (size_t)maxconn * sizeof(*handler)); +#else + fds = xrealloc(fds, (size_t)MAXIMUM_WAIT_OBJECTS * sizeof(*fds)); + handler = xrealloc(handler, (size_t)MAXIMUM_WAIT_OBJECTS * sizeof(*handler)); +#endif } /* instant command and setvar status tracking */ @@ -958,34 +1370,66 @@ int nut_uuid_v4(char *uuid_str) nut_uuid[12], nut_uuid[13], nut_uuid[14], nut_uuid[15]); } +static void set_exit_flag(int sig) +{ + exit_flag = sig; +} + +static void set_reload_flag(int sig) +{ + NUT_UNUSED_VARIABLE(sig); + reload_flag = 1; +} + /* service requests and check on new data */ static void mainloop(void) { +#ifndef WIN32 int ret; - nfds_t i, nfds = 0; + nfds_t i; +#else + DWORD ret; + pipe_conn_t * conn; +#endif + nfds_t nfds = 0; upstype_t *ups; nut_ctype_t *client, *cnext; stype_t *server; time_t now; + upsnotify(NOTIFY_STATE_WATCHDOG, NULL); + time(&now); if (reload_flag) { + upsnotify(NOTIFY_STATE_RELOADING, NULL); conf_reload(); poll_reload(); reload_flag = 0; + upsnotify(NOTIFY_STATE_READY, NULL); } /* cleanup instcmd/setvar status tracking entries if needed */ tracking_cleanup(); +#ifndef WIN32 /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { /* see if we need to (re)connect to the socket */ - if (ups->sock_fd < 0) { + if (INVALID_FD(ups->sock_fd)) { + upsdebugx(1, "%s: UPS [%s] is not currently connected, " + "trying to reconnect", + __func__, ups->name); ups->sock_fd = sstate_connect(ups); + if (INVALID_FD(ups->sock_fd)) { + upsdebugx(1, "%s: UPS [%s] is still not connected (FD %d)", + __func__, ups->name, ups->sock_fd); + } else { + upsdebugx(1, "%s: UPS [%s] is now connected as FD %d", + __func__, ups->name, ups->sock_fd); + } continue; } @@ -1047,7 +1491,7 @@ static void mainloop(void) nfds++; } - upsdebugx(2, "%s: polling %jd filedescriptors", __func__, (intmax_t)nfds); + upsdebugx(2, "%s: polling %" PRIdMAX " filedescriptors", __func__, (intmax_t)nfds); ret = poll(fds, nfds, 2000); @@ -1158,6 +1602,196 @@ static void mainloop(void) continue; } } +#else + /* scan through driver sockets */ + for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { + + /* see if we need to (re)connect to the socket */ + if (INVALID_FD(ups->sock_fd)) { + upsdebugx(1, "%s: UPS [%s] is not currently connected, " + "trying to reconnect", + __func__, ups->name); + ups->sock_fd = sstate_connect(ups); + if (INVALID_FD(ups->sock_fd)) { + upsdebugx(1, "%s: UPS [%s] is still not connected (FD %d)", + __func__, ups->name, ups->sock_fd); + } else { + upsdebugx(1, "%s: UPS [%s] is now connected as FD %d", + __func__, ups->name, ups->sock_fd); + } + continue; + } + + /* throw some warnings if it's not feeding us data any more */ + if (sstate_dead(ups, maxage)) { + ups_data_stale(ups); + } else { + ups_data_ok(ups); + } + + /* FIXME: Is the conditional needed? We got here... */ + if (VALID_FD(ups->sock_fd)) { + fds[nfds] = ups->read_overlapped.hEvent; + + handler[nfds].type = DRIVER; + handler[nfds].data = ups; + + nfds++; + } + } + + /* scan through client sockets */ + for (client = firstclient; client; client = cnext) { + + cnext = client->next; + + if (difftime(now, client->last_heard) > 60) { + /* shed clients after 1 minute of inactivity */ + client_disconnect(client); + continue; + } + + if (nfds >= maxconn) { + /* ignore clients that we are unable to handle */ + continue; + } + + fds[nfds] = client->Event; + + handler[nfds].type = CLIENT; + handler[nfds].data = client; + + nfds++; + } + + /* scan through server sockets */ + for (server = firstaddr; server && (nfds < maxconn); server = server->next) { + + if (INVALID_FD_SOCK(server->sock_fd)) { + continue; + } + + fds[nfds] = server->Event; + + handler[nfds].type = SERVER; + handler[nfds].data = server; + + nfds++; + } + + /* Wait on the read IO on named pipe */ + for (conn = pipe_connhead; conn; conn = conn->next) { + fds[nfds] = conn->overlapped.hEvent; + handler[nfds].type = NAMED_PIPE; + handler[nfds].data = (void *)conn; + nfds++; + } + /* Add the new named pipe connected event */ + fds[nfds] = pipe_connection_overlapped.hEvent; + handler[nfds].type = NAMED_PIPE; + handler[nfds].data = NULL; + nfds++; + + upsdebugx(2, "%s: wait for %d filedescriptors", __func__, nfds); + + /* https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects */ + ret = WaitForMultipleObjects(nfds,fds,FALSE,2000); + + upsdebugx(6, "%s: wait for filedescriptors done: %" PRIu64, __func__, ret); + + if (ret == WAIT_TIMEOUT) { + upsdebugx(2, "%s: no data available", __func__); + return; + } + + if (ret == WAIT_FAILED) { + DWORD err = GetLastError(); + err = err; /* remove compile time warning */ + upslog_with_errno(LOG_ERR, "%s", __func__); + upsdebugx(2, "%s: wait failed: code 0x%" PRIx64, __func__, err); + return; + } + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + nfds - 1) { + /* One abandoned mutex object that satisfied the wait? */ + ret = ret - WAIT_ABANDONED_0; + upsdebugx(5, "%s: got abandoned FD array item: %" PRIu64, __func__, nfds, ret); + /* FIXME: Should this be handled somehow? Cleanup? Abort?.. */ + } else + if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + nfds - 1) { + /* Which one handle was triggered this time? */ + /* Note: WAIT_OBJECT_0 may be currently defined as 0, + * but docs insist on checking and shifting the range */ + ret = ret - WAIT_OBJECT_0; + upsdebugx(5, "%s: got event on FD array item: %" PRIu64, __func__, nfds, ret); + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + if (ret >= nfds) { + /* Array indexes are [0..nfds-1] */ + upsdebugx(2, "%s: unexpected response to query about data available: %" PRIu64, __func__, ret); + return; + } + + upsdebugx(6, "%s: requesting handler[%" PRIu64 "]", __func__, ret); + upsdebugx(6, "%s: handler.type=%d handler.data=%p", __func__, handler[ret].type, handler[ret].data); + + switch(handler[ret].type) { + case DRIVER: + upsdebugx(4, "%s: calling sstate_readline() for DRIVER", __func__); + sstate_readline((upstype_t *)handler[ret].data); + break; + case CLIENT: + upsdebugx(4, "%s: calling client_readline() for CLIENT", __func__); + client_readline((nut_ctype_t *)handler[ret].data); + break; + case SERVER: + upsdebugx(4, "%s: calling client_connect() for SERVER", __func__); + client_connect((stype_t *)handler[ret].data); + break; + case NAMED_PIPE: + /* a new pipe connection has been signaled */ + if (fds[ret] == pipe_connection_overlapped.hEvent) { + upsdebugx(4, "%s: calling pipe_connect() for NAMED_PIPE", __func__); + pipe_connect(); + } + /* one of the read event handle has been signaled */ + else { + upsdebugx(4, "%s: calling pipe_ready() for NAMED_PIPE", __func__); + pipe_conn_t * conn = handler[ret].data; + if ( pipe_ready(conn) ) { + if (!strncmp(conn->buf, SIGCMD_STOP, sizeof(SIGCMD_STOP))) { + set_exit_flag(1); + } + else if (!strncmp(conn->buf, SIGCMD_RELOAD, sizeof(SIGCMD_RELOAD))) { + set_reload_flag(1); + } + else { + upslogx(LOG_ERR,"Unknown signal" + ); + } + + upsdebugx(4, "%s: calling pipe_disconnect() for NAMED_PIPE", __func__); + pipe_disconnect(conn); + } + } + break; + default: + upsdebugx(2, "%s: has data available", __func__); + break; + } +#endif } static void help(const char *arg_progname) @@ -1173,31 +1807,29 @@ 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(" -h display this help\n"); +#ifndef WIN32 + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); +#endif + 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 text\n"); + printf(" -V display the version of this software\n"); printf(" -r chroots to \n"); printf(" -q raise log level threshold\n"); printf(" -u switch to (if started as root)\n"); - printf(" -V display the version of this software\n"); printf(" -4 IPv4 only\n"); printf(" -6 IPv6 only\n"); - exit(EXIT_SUCCESS); -} - -static void set_reload_flag(int sig) -{ - NUT_UNUSED_VARIABLE(sig); - reload_flag = 1; -} + nut_report_config_flags(); -static void set_exit_flag(int sig) -{ - exit_flag = sig; + exit(EXIT_SUCCESS); } static void setup_signals(void) { +#ifndef WIN32 struct sigaction sa; sigemptyset(&sa.sa_mask); @@ -1224,10 +1856,14 @@ static void setup_signals(void) /* handle reloading */ sa.sa_handler = set_reload_flag; sigaction(SIGHUP, &sa, NULL); +#else + pipe_create(UPSD_PIPE_NAME); +#endif } void check_perms(const char *fn) { +#ifndef WIN32 int ret; struct stat st; @@ -1241,11 +1877,20 @@ void check_perms(const char *fn) if (st.st_mode & (S_IROTH | S_IXOTH)) { upslogx(LOG_WARNING, "%s is world readable", fn); } +#else + NUT_UNUSED_VARIABLE(fn); +#endif } int main(int argc, char **argv) { - int i, cmd = 0, cmdret = 0; + int i, cmdret = 0, foreground = -1; +#ifndef WIN32 + int cmd = 0; + pid_t oldpid = -1; +#else + const char * cmd = NULL; +#endif char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; @@ -1254,14 +1899,33 @@ int main(int argc, char **argv) /* yes, xstrdup - the conf handlers call free on this later */ statepath = xstrdup(dflt_statepath()); - datapath = xstrdup(DATADIR); +#ifndef WIN32 + datapath = xstrdup(NUT_DATADIR); +#else + datapath = getfullpath(PATH_SHARE); + + /* remove trailing .exe */ + char * drv_name; + drv_name = (char *)xbasename(argv[0]); + char * name = strrchr(drv_name,'.'); + if( name != NULL ) { + if(strcasecmp(name, ".exe") == 0 ) { + progname = strdup(drv_name); + char * t = strrchr(progname,'.'); + *t = 0; + } + } + else { + progname = drv_name; + } +#endif /* set up some things for later */ snprintf(pidfn, sizeof(pidfn), "%s/%s.pid", altpidpath(), progname); 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': @@ -1285,22 +1949,45 @@ int main(int argc, char **argv) break; case 'V': - /* do nothing - we already printed the banner */ + /* Note - we already printed the banner for program name */ + nut_report_config_flags(); + exit(EXIT_SUCCESS); case 'c': - if (!strncmp(optarg, "reload", strlen(optarg))) + if (!strncmp(optarg, "reload", strlen(optarg))) { cmd = SIGCMD_RELOAD; - if (!strncmp(optarg, "stop", strlen(optarg))) + } else + if (!strncmp(optarg, "stop", strlen(optarg))) { cmd = SIGCMD_STOP; + } /* bad command given */ if (cmd == 0) help(progname); break; +#ifndef WIN32 + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(progname); + break; +#endif + case 'D': nut_debug_level++; + nut_debug_level_args++; + 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': @@ -1317,18 +2004,138 @@ int main(int argc, char **argv) } } - if (cmd) { + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + + { /* scoping */ + char *s = getenv("NUT_DEBUG_LEVEL"); + int l; + if (s && str_to_int(s, &l, 10)) { + if (l > 0 && nut_debug_level_args < 1) { + upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d " + "since none was requested by command-line options", l); + nut_debug_level = l; + nut_debug_level_args = l; + } /* else follow -D settings */ + } /* else nothing to bother about */ + } + + /* Note: "cmd" may be non-trivial to command that instance by + * explicit PID number or lookup in PID file (error if absent). + * Otherwise, we are being asked to start and "cmd" is 0/NULL - + * for probing whether a competing older instance of this program + * is running (error if it is). + */ +#ifndef WIN32 + /* If cmd == 0 we are starting and check if a previous instance + * is running by sending signal '0' (i.e. 'kill 0' equivalent) + */ + + if (oldpid < 0) { cmdret = sendsignalfn(pidfn, cmd); - exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } +#else /* if WIN32 */ + if (cmd) { + /* Command the running daemon, it should be there */ + cmdret = sendsignal(UPSD_PIPE_NAME, cmd); + } else { + /* Starting new daemon, check for competition */ + mutex = CreateMutex(NULL, TRUE, UPSD_PIPE_NAME); + if (mutex == NULL) { + if (GetLastError() != ERROR_ACCESS_DENIED) { + fatalx(EXIT_FAILURE, + "Can not create mutex %s : %d.\n", + UPSD_PIPE_NAME, (int)GetLastError()); + } + } + + cmdret = -1; /* unknown, maybe ok */ + if (GetLastError() == ERROR_ALREADY_EXISTS + || GetLastError() == ERROR_ACCESS_DENIED + ) { + cmdret = 0; /* known conflict */ + } } +#endif /* WIN32 */ + + switch (cmdret) { + case 0: + if (cmd) { + upsdebugx(1, "Signaled old daemon OK"); + } else { + 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); + } + break; - /* 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) { - 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: + /* if starting new daemon, no competition running - + * maybe OK (or failed to detect it => problem) + * if signaling old daemon - certainly have a problem + */ + upslogx(LOG_WARNING, "Could not %s PID file '%s' " + "to see if previous upsd instance is " + "already running!", + (cmdret == -3 ? "find" : "parse"), + pidfn); + break; + + case -1: + case 1: /* WIN32 */ + default: + /* if cmd was nontrivial - speak up below, else be quiet */ + upsdebugx(1, "Just failed to send signal, no daemon was running"); + break; + } + + if (cmd) { + /* We were signalling a daemon, successfully or not - exit now... */ + if (cmdret != 0) { + /* sendsignal*() above might have logged more details + * for troubleshooting, e.g. about lack of PID file + */ + upslogx(LOG_NOTICE, "Failed to signal the currently running daemon (if any)"); +#ifndef WIN32 +# ifdef HAVE_SYSTEMD + switch (cmd) { + case SIGCMD_RELOAD: + upslogx(LOG_NOTICE, "Try 'systemctl reload %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + case SIGCMD_STOP: + upslogx(LOG_NOTICE, "Try 'systemctl stop %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + default: + upslogx(LOG_NOTICE, "Try 'systemctl %s'%s", + SERVICE_UNIT_NAME, + (oldpid < 0 ? " or add '-P $PID' argument" : "")); + break; + } + /* ... or edit nut-server.service locally to start `upsd -FF` + * and so save the PID file for ability to manage the daemon + * beside the service framework, possibly confusing things... + */ +# else + if (oldpid < 0) { + upslogx(LOG_NOTICE, "Try to add '-P $PID' argument"); + } +# endif +#endif /* not WIN32 */ + } + + exit((cmdret == 0) ? EXIT_SUCCESS : EXIT_FAILURE); } argc -= optind; @@ -1354,13 +2161,25 @@ int main(int argc, char **argv) chroot_start(chroot_path); } +#ifndef WIN32 /* default to system limit (may be overridden in upsd.conf) */ /* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */ maxconn = (nfds_t)sysconf(_SC_OPEN_MAX); +#else + maxconn = 64; /*FIXME : arbitrary value, need adjustement */ +#endif /* 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 @@ -1383,20 +2202,47 @@ int main(int argc, char **argv) } } /* scope */ + { /* scope */ + /* As documented above, the ALLOW_NOT_ALL_LISTENERS can be provided via + * envvars and then has higher priority than an upsd.conf setting + */ + const char *envvar = getenv("ALLOW_NOT_ALL_LISTENERS"); + if ( envvar != NULL) { + if ( (!strncasecmp("TRUE", envvar, 4)) || (!strncasecmp("YES", envvar, 3)) || (!strncasecmp("ON", envvar, 2)) || (!strncasecmp("1", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * NUT protocol if at least one configured listener + * works (some may be missing and clients using those + * addresses would not be served!) + */ + allow_not_all_listeners = 1; + } else if ( (!strncasecmp("FALSE", envvar, 5)) || (!strncasecmp("NO", envvar, 2)) || (!strncasecmp("OFF", envvar, 3)) || (!strncasecmp("0", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * NUT protocol only if all configured listeners work + * (default for least surprise - admins must address + * any configuration inconsistencies!) + */ + allow_not_all_listeners = 0; + } + } + } /* scope */ + /* start server */ server_load(); become_user(new_uid); - +#ifndef WIN32 if (chdir(statepath)) { fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", statepath); + } else { + upsdebugx(1, "chdired into statepath %s for driver sockets", statepath); } +#endif /* check statepath perms */ check_perms(statepath); /* handle ups.conf */ - read_upsconf(); + read_upsconf(1); /* 1 = may abort upon fundamental errors */ upsconf_add(0); /* 0 = initial */ poll_reload(); @@ -1406,6 +2252,8 @@ int main(int argc, char **argv) } else { fatalx(EXIT_FAILURE, "Fatal error: at least one UPS must be defined in ups.conf"); } + } else { + upslogx(LOG_INFO, "Found %d UPS defined in ups.conf", num_ups); } /* try to bring in the var/cmd descriptions */ @@ -1414,23 +2262,32 @@ int main(int argc, char **argv) /* handle upsd.users */ user_load(); - if (!nut_debug_level) { + if (!foreground) { background(); writepid(pidfn); } else { - memset(pidfn, 0, sizeof(pidfn)); + if (foreground == 2) { + upslogx(LOG_WARNING, "Running as foreground process, but saving a PID file anyway"); + writepid(pidfn); + } else { + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); + memset(pidfn, 0, sizeof(pidfn)); + } } /* initialize SSL (keyfile must be readable by nut user) */ ssl_init(); + upsnotify(NOTIFY_STATE_READY_WITH_PID, NULL); + while (!exit_flag) { + /* Note: mainloop() calls upsnotify(NOTIFY_STATE_WATCHDOG, NULL); */ mainloop(); } - ssl_cleanup(); - upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); + upsnotify(NOTIFY_STATE_STOPPING, "Signal %d: exiting", exit_flag); + + ssl_cleanup(); return EXIT_SUCCESS; } - diff --git a/server/upsd.h b/server/upsd.h index 13801885bd..dfe27140ae 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -31,14 +31,20 @@ #include "common.h" +#ifndef WIN32 #include #include #include +#endif #include "timehead.h" #include -#include /* nfds_t */ +#ifdef HAVE_POLL_H +# include /* nfds_t */ +#else +typedef unsigned long int nfds_t; +#endif #include "parseconf.h" #include "nut_ctype.h" @@ -91,16 +97,20 @@ int tracking_disable(void); int tracking_is_enabled(void); /* declarations from upsd.c */ -extern int maxage, tracking_delay, allow_no_device; +extern int maxage, tracking_delay, allow_no_device, allow_not_all_listeners; extern nfds_t maxconn; extern char *statepath, *datapath; extern upstype_t *firstups; extern nut_ctype_t *firstclient; /* map commands onto signals */ - +#ifndef WIN32 #define SIGCMD_STOP SIGTERM #define SIGCMD_RELOAD SIGHUP +#else +#define SIGCMD_STOP COMMAND_STOP +#define SIGCMD_RELOAD COMMAND_RELOAD +#endif /* awkward way to make a string out of a numeric constant */ diff --git a/server/upstype.h b/server/upstype.h index bc031878aa..4efac5446c 100644 --- a/server/upstype.h +++ b/server/upstype.h @@ -23,6 +23,7 @@ #define NUT_UPSTYPE_H_SEEN 1 #include "parseconf.h" +#include "common.h" #ifdef __cplusplus /* *INDENT-OFF* */ @@ -35,8 +36,11 @@ typedef struct upstype_s { char *name; char *fn; char *desc; - - int sock_fd; + TYPE_FD sock_fd; +#ifdef WIN32 + char buf[SMALLBUF]; + OVERLAPPED read_overlapped; +#endif int stale; int dumpdone; int data_ok; diff --git a/server/user.c b/server/user.c index b0c985be55..8ebb418ec9 100644 --- a/server/user.c +++ b/server/user.c @@ -20,9 +20,11 @@ #include "config.h" /* must be the first header */ #include +#ifndef WIN32 #include #include #include +#endif #include "common.h" #include "parseconf.h" diff --git a/tests/.gitignore b/tests/.gitignore index 40cf44ca81..876e375e29 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,13 +1,24 @@ /cppunittest /cppunittest.log /cppunittest.trs -/nutlogtest +/cppnit +/cppnit.log +/cppnit.trs +/gpiotest +/gpiotest.log +/gpiotest.trs /test-suite.log /selftest-rw/* +/nutlogtest-nofail.sh /nutlogtest /nutlogtest.log /nutlogtest.trs +/nuttimetest +/nuttimetest.log +/nuttimetest.trs /getvaluetest /getvaluetest.log /getvaluetest.trs /hidparser.c +/generic_gpio_libgpiod.c +/generic_gpio_common.c diff --git a/tests/Makefile.am b/tests/Makefile.am index d0283191c3..d7a1cd9ccc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,20 +1,61 @@ # Network UPS Tools: tests +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + +SUBDIRS = . NIT + all: $(TESTS) EXTRA_DIST = nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf -TESTS = nutlogtest +TESTS = CLEANFILES = *.trs *.log AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/drivers AM_CXXFLAGS = -I$(top_srcdir)/include +# Compiler flags for cppunit tests +CPPUNIT_NUT_CXXFLAGS = @CPPUNIT_NUT_CXXFLAGS@ + check_PROGRAMS = $(TESTS) +check_SCRIPTS = + +# NUT Integration Testing suite +check-NIT check-NIT-devel: + +cd "$(builddir)/NIT" && $(MAKE) $(AM_MAKEFLAGS) $@ nutlogtest_SOURCES = nutlogtest.c nutlogtest_LDADD = $(top_builddir)/common/libcommon.la +if REQUIRE_NUT_STRARG +check_SCRIPTS += nutlogtest-nofail.sh +CLEANFILES += nutlogtest-nofail.sh nutlogtest$(EXEEXT) nutlogtest + +nutlogtest-nofail.sh: nutlogtest$(EXEEXT) + @echo '#!/bin/sh' > $@ + @echo 'echo "WARNING: Your C library requires workarounds to print NULL values!" >&2' >> $@ + @echo 'echo "If nutlogtest below, or generally some NUT program, crashes with" >&2' >> $@ + @echo 'echo "a segmentation fault (especially during verbose debug) - that may be why" >&2' >> $@ + @echo 'SCRIPT_DIR="`dirname "$$0"`"' >> $@ + @echo '"$${SCRIPT_DIR}/nutlogtest" "$$@" || echo "nutlogtest FAILED but it was expected"' >> $@ + @chmod +x $@ + +# NOTE: Keep the line above empty! +else !REQUIRE_NUT_STRARG +TESTS += nutlogtest$(EXEEXT) +endif !REQUIRE_NUT_STRARG + +TESTS += nuttimetest +nuttimetest_SOURCES = nuttimetest.c +nuttimetest_LDADD = $(top_builddir)/common/libcommon.la + # Separate the .deps of other dirs from this one LINKED_SOURCE_FILES = hidparser.c @@ -31,21 +72,48 @@ nodist_getvaluetest_SOURCES = hidparser.c # Pull the right include path for chosen libusb version: getvaluetest_CFLAGS = $(AM_CFLAGS) $(LIBUSB_CFLAGS) getvaluetest_LDADD = $(top_builddir)/common/libcommon.la -endif +else !WITH_USB +EXTRA_DIST += getvaluetest.c hidparser.c +endif !WITH_USB + +if WITH_GPIO +TESTS += gpiotest + +# NOTE: Not using "$<" due to a legacy Sun/illumos dmake bug with resolver +# of dynamic vars, see e.g. https://man.omnios.org/man1/make#BUGS +generic_gpio_libgpiod.c: $(top_srcdir)/drivers/generic_gpio_libgpiod.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/generic_gpio_libgpiod.c" "$@" + +generic_gpio_common.c: $(top_srcdir)/drivers/generic_gpio_common.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/generic_gpio_common.c" "$@" + +gpiotest_SOURCES = generic_gpio_utest.c generic_gpio_liblocal.c +nodist_gpiotest_SOURCES = generic_gpio_libgpiod.c generic_gpio_common.c +gpiotest_LDADD = $(top_builddir)/drivers/libdummy_mockdrv.la +gpiotest_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/tests -DDRIVERS_MAIN_WITHOUT_MAIN=1 +else !WITH_GPIO +EXTRA_DIST += generic_gpio_utest.c generic_gpio_liblocal.c +endif !WITH_GPIO + +CLEANFILES += generic_gpio_libgpiod.c generic_gpio_common.c +EXTRA_DIST += generic_gpio_utest.h generic_gpio_test.txt # Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/drivers/libdummy_mockdrv.la \ $(top_builddir)/common/libnutconf.la \ $(top_builddir)/common/libcommon.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) ### Optional tests which can not be built everywhere # List of src files for CppUnit tests CPPUNITTESTSRC = example.cpp nutclienttest.cpp # These are an optional part of cppunittest, if building WITH_NUTCONF -CPPUNITTESTSRC_NUTCONF = nutconf.cpp nutstream_ut.cpp nutconf_ut.cpp nutipc_ut.cpp +CPPUNITTESTSRC_NUTCONF = nutconf_parser_ut.cpp nutstream_ut.cpp nutconf_ut.cpp nutipc_ut.cpp # The test driver which orchestrates running those tests above CPPUNITTESTERSRC = cpputest.cpp +CPPCLIENTTESTSRC = cpputest-client.cpp + TESTS_CXX11 = cppunittest if HAVE_CXX11 @@ -55,13 +123,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 +endif WITH_VALGRIND cppunittest_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) -cppunittest_LDFLAGS = $(CPPUNIT_LIBS) +###cppunittest_CXXFLAGS += -I$(top_srcdir)/include -DTOP_SRCDIR="\"$(top_srcdir)\"" +cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la cppunittest_LDADD += $(top_builddir)/clients/libnutclientstub.la cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) @@ -71,25 +143,36 @@ cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) if WITH_NUTCONF cppunittest_SOURCES += $(CPPUNITTESTSRC_NUTCONF) cppunittest_LDADD += $(top_builddir)/common/libnutconf.la -endif +endif WITH_NUTCONF + +cppnit_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +cppnit_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +cppnit_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +cppnit_SOURCES = $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) # Make sure out-of-dir C++ dependencies exist (especially when dev-building # only some parts of NUT): $(top_builddir)/clients/libnutclient.la \ $(top_builddir)/clients/libnutclientstub.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@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) $(CPPUNITTESTERSRC) $(CPPUNITTESTSRC_NUTCONF) +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(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 @@ -103,4 +186,4 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! #clean-local: -# rm -rf $(builddir)/.deps +# $(AM_V_at)rm -rf $(builddir)/.deps 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..f251fa7b1f --- /dev/null +++ b/tests/NIT/Makefile.am @@ -0,0 +1,61 @@ +# Network UPS Tools: NUT Integration Tests (NIT) + +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + +EXTRA_DIST = nit.sh README.adoc + +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) $(AM_MAKEFLAGS) -s cppnit$(EXEEXT) || echo "OPTIONAL C++ test client test will be skipped" ) + +@cd "$(top_builddir)/clients" && $(MAKE) $(AM_MAKEFLAGS) -s upsc$(EXEEXT) upsrw$(EXEEXT) upsmon$(EXEEXT) + +@cd "$(top_builddir)/server" && $(MAKE) $(AM_MAKEFLAGS) -s upsd$(EXEEXT) + +@cd "$(top_builddir)/drivers" && $(MAKE) $(AM_MAKEFLAGS) -s dummy-ups$(EXEEXT) + +@$(MAKE) $(AM_MAKEFLAGS) check-NIT + +SPELLCHECK_SRC = README.adoc + +# NOTE: Due to portability, we do not use a GNU percent-wildcard extension. +# We also have to export some variables that may be tainted by relative +# paths when parsing the other makefile (e.g. MKDIR_P that may be defined +# via expanded $(top_builddir)/install-sh): +#%-spellchecked: % Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) +# +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +# NOTE: Portable suffix rules do not allow prerequisites, so we shim them here +# by a wildcard target in case the make implementation can put the two together. +*-spellchecked: Makefile.am $(top_srcdir)/docs/Makefile.am $(abs_srcdir)/$(NUT_SPELL_DICT) + +.sample.sample-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +.in.in-spellchecked: + +$(MAKE) -s -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC_ONE="$<" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +spellcheck spellcheck-interactive spellcheck-sortdict: + +$(MAKE) -f $(top_builddir)/docs/Makefile $(AM_MAKEFLAGS) MKDIR_P="$(MKDIR_P)" builddir="$(builddir)" srcdir="$(srcdir)" top_builddir="$(top_builddir)" top_srcdir="$(top_srcdir)" SPELLCHECK_SRC="$(SPELLCHECK_SRC)" SPELLCHECK_SRCDIR="$(srcdir)" SPELLCHECK_BUILDDIR="$(builddir)" $@ + +CLEANFILES = *-spellchecked + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + $(AM_V_at)rm -rf tmp diff --git a/tests/NIT/README.adoc b/tests/NIT/README.adoc new file mode 100644 index 0000000000..b442f27434 --- /dev/null +++ b/tests/NIT/README.adoc @@ -0,0 +1,18 @@ +NUT Integration Testing suite (aka NIT) +======================================= + +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] +and link:https://salsa.debian.org/debian/nut/-/tree/debian/debian/tests[Debian packaging recipe] +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..611c1d2eed --- /dev/null +++ b/tests/NIT/nit.sh @@ -0,0 +1,1436 @@ +#!/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-2023 Jim Klimov +# +# License: GPLv2+ + +TZ=UTC +LANG=C +LC_ALL=C +export TZ LANG LC_ALL + +NUT_QUIET_INIT_SSL="true" +export NUT_QUIET_INIT_SSL + +NUT_QUIET_INIT_UPSNOTIFY="true" +export NUT_QUIET_INIT_UPSNOTIFY + +NUT_DEBUG_PID="true" +export NUT_DEBUG_PID + +log_separator() { + echo "" >&2 + echo "================================" >&2 +} + +shouldDebug() { + [ -n "$DEBUG" ] || [ -n "$DEBUG_SLEEP" ] +} + +log_debug() { + if shouldDebug ; then + echo "`TZ=UTC LANG=C date` [DEBUG] $@" >&2 + fi + return 0 +} + +log_info() { + echo "`TZ=UTC LANG=C date` [INFO] $@" >&2 +} + +log_warn() { + echo "`TZ=UTC LANG=C date` [WARNING] $@" >&2 +} + +log_error() { + echo "`TZ=UTC LANG=C date` [ERROR] $@" >&2 +} + +report_NUT_PORT() { + # Try to say which processes deal with current NUT_PORT + [ -n "${NUT_PORT}" ] || return + + log_info "Trying to report users of NUT_PORT=${NUT_PORT}" + # Note: on Solarish systems, `netstat -anp` does not report PID info + (netstat -an ; netstat -anp || sockstat -l) 2>/dev/null | grep -w "${NUT_PORT}" \ + || (lsof -i :"${NUT_PORT}") 2>/dev/null \ + || true + + [ -z "${PID_UPSD}" ] || log_info "UPSD was last known to start as PID ${PID_UPSD}" +} + +isBusy_NUT_PORT() { + # Try to say if current NUT_PORT is busy (0 = true) + # or available (non-0 = false) + [ -n "${NUT_PORT}" ] || return + + log_debug "isBusy_NUT_PORT() Trying to report if NUT_PORT=${NUT_PORT} is used" + if [ -e /proc/net/tcp ] || [ -e /proc/net/tcp6 ]; then + # Assume Linux - hex-encoded + # IPv4: + # sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + # 0: 0100007F:EE48 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 48881 1 00000000ec238d02 100 0 0 10 0 + # ^^^ 1.0.0.127 - note reversed byte order! + # IPv6: + # sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + # 0: 00000000000000000000000000000000:1F46 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 33 0 37451 1 00000000fa3c0c15 100 0 0 10 0 + NUT_PORT_HEX="`printf '%04X' "${NUT_PORT}"`" + NUT_PORT_HITS="`cat /proc/net/tcp /proc/net/tcp6 2>/dev/null | awk '{print $2}' | grep -E ":${NUT_PORT_HEX}\$"`" \ + && [ -n "$NUT_PORT_HITS" ] \ + && log_debug "isBusy_NUT_PORT() found that NUT_PORT=${NUT_PORT} is busy per /proc/net/tcp*" \ + && return 0 + + # We had a way to check, and the way said port is available + log_debug "isBusy_NUT_PORT() found that NUT_PORT=${NUT_PORT} is not busy per /proc/net/tcp*" + return 1 + fi + + (netstat -an || sockstat -l || ss -tn || ss -n) 2>/dev/null | grep -E "[:.]${NUT_PORT}(\t| |\$)" > /dev/null \ + && log_debug "isBusy_NUT_PORT() found that NUT_PORT=${NUT_PORT} is busy per netstat, sockstat or ss" \ + && return + + (lsof -i :"${NUT_PORT}") 2>/dev/null \ + && log_debug "isBusy_NUT_PORT() found that NUT_PORT=${NUT_PORT} is busy per lsof" \ + && return + + # Not busy... or no tools to confirm? + if (command -v netstat || command -v sockstat || command -v ss || command -v lsof) 2>/dev/null >/dev/null ; then + # at least one tool is present, so not busy + log_debug "isBusy_NUT_PORT() found that NUT_PORT=${NUT_PORT} is not busy per netstat, sockstat, ss or lsof" + return 1 + fi + + # If the current shell interpreter is bash, it can do a bit of networking: + if [ -n "${BASH_VERSION-}" ]; then + # NOTE: Probing host names we use in upsd.conf + # See generatecfg_upsd_trivial() for a more carefully made list + ( # Hide this looped noise: + # ./nit.sh: connect: Connection refused + # ./nit.sh: line 112: /dev/tcp/localhost/35050: Connection refused + if shouldDebug ; then : ; else + exec 2>/dev/null + fi + for H in "localhost" "127.0.0.1" "::1"; do + if echo "HELP" > "/dev/tcp/${H}/${NUT_PORT}" ; then + # Successfully connected - port isBusy + return 0 + fi + done + return 1 + ) && return 0 + log_warn "isBusy_NUT_PORT() tried with BASH-specific query, and port does not seem busy (or something else errored out)" + fi + + # Assume not busy to not preclude testing in 100% of the cases + log_warn "isBusy_NUT_PORT() can not say definitively, tools for checking NUT_PORT=$NUT_PORT are not available" + return 1 +} + +die() { + echo "[FATAL] $@" >&2 + exit 1 +} + +# By default, keep stdout hidden but report the errors: +[ -n "$RUNCMD_QUIET_OUT" ] || RUNCMD_QUIET_OUT=true +[ -n "$RUNCMD_QUIET_ERR" ] || RUNCMD_QUIET_ERR=false +runcmd() { + # Re-uses a couple of files in test scratch area NUT_STATEPATH + # to store the stderr and stdout of the launched program. + # Prints the captured output back and returns the exit code. + + [ -n "${NUT_STATEPATH}" -a -d "${NUT_STATEPATH}" -a -w "${NUT_STATEPATH}" ] \ + || die "runcmd() called when NUT_STATEPATH was not yet set up" + + # Values from variables below may be used until next runcmd(): + CMDRES=0 + CMDOUT="" + CMDERR="" + + "$@" > "${NUT_STATEPATH}/runcmd.out" 2>"${NUT_STATEPATH}/runcmd.err" || CMDRES=$? + CMDOUT="`cat "${NUT_STATEPATH}/runcmd.out"`" + CMDERR="`cat "${NUT_STATEPATH}/runcmd.err"`" + + [ "$RUNCMD_QUIET_OUT" = true ] || { [ -z "$CMDOUT" ] || echo "$CMDOUT" ; } + [ "$RUNCMD_QUIET_ERR" = true ] || { [ -z "$CMDERR" ] || echo "$CMDERR" >&2 ; } + + return $CMDRES +} + +# 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 most of the (client) binaries that +# need it, the PATH entries below would contain libtool wrapper scripts; +# for other builds we use system default or caller's env. One exception +# so far is nut-scanner that needs to know where libupsclient.so is... +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'" + +LD_LIBRARY_PATH_ORIG="${LD_LIBRARY_PATH-}" +LD_LIBRARY_PATH_CLIENT="" +if [ x"${TOP_BUILDDIR}" != x ]; then + LD_LIBRARY_PATH_CLIENT="${TOP_BUILDDIR}/clients:${TOP_BUILDDIR}/clients/.libs" +fi + +if [ x"${LD_LIBRARY_PATH_CLIENT}" != x ]; then + if [ -n "${LD_LIBRARY_PATH_ORIG-}" ]; then + LD_LIBRARY_PATH_CLIENT="${LD_LIBRARY_PATH_CLIENT}:${LD_LIBRARY_PATH_ORIG}" + fi +else + LD_LIBRARY_PATH_CLIENT="${LD_LIBRARY_PATH_ORIG}" +fi + +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 + : + else + 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" + +if [ "`id -u`" = 0 ]; then + log_info "Test script was started by 'root' - expanding permissions for '${TESTDIR}/run' so unprivileged daemons may create pipes and PID files there" + chmod 777 "${TESTDIR}/run" +fi + +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 || return 0 + wait $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 || true + fi + + PID_UPSD="" + PID_DUMMYUPS="" + PID_DUMMYUPS1="" + PID_DUMMYUPS2="" +} + +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? +if [ -n "${NUT_PORT-}" ] && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] ; then + if isBusy_NUT_PORT ; then + log_warn "NUT_PORT=$NUT_PORT requested by caller seems occupied; tests may fail below" + fi +else + COUNTDOWN=60 + while [ "$COUNTDOWN" -gt 0 ] ; do + 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 + + if isBusy_NUT_PORT ; then : ; else + break + fi + + log_warn "Selected NUT_PORT=$NUT_PORT seems occupied; will try another in a few seconds" + COUNTDOWN="`expr "$COUNTDOWN" - 1`" + + [ "$COUNTDOWN" = 0 ] || sleep 2 + done + + if [ "$COUNTDOWN" = 0 ] ; then + COUNTDOWN=60 + DELTA1=1025 + while [ "$COUNTDOWN" -gt 0 ] ; do + DELTA2="`expr $RANDOM % 64000`" \ + && [ "$DELTA2" -ge 0 ] || die "Can not pick random port" + + NUT_PORT="`expr $DELTA1 + $DELTA2`" + if isBusy_NUT_PORT ; then : ; else + break + fi + + # Loop quickly, no sleep here + COUNTDOWN="`expr "$COUNTDOWN" - 1`" + done + + if [ "$COUNTDOWN" = 0 ] ; then + die "Can not pick random port" + fi + fi +fi +export NUT_PORT +# Help track collisions in log, if someone else starts a test in same directory +log_info "Using NUT_PORT=${NUT_PORT} for this test run" + +### 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" + + if [ "`id -u`" = 0 ]; then + log_info "Test script was started by 'root' - expanding permissions for '$NUT_CONFPATH/upsd.conf' so unprivileged daemons (after de-elevation) may read it" + chmod 644 "$NUT_CONFPATH/upsd.conf" + else + chmod 640 "$NUT_CONFPATH/upsd.conf" + fi + + # 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" + + if [ "`id -u`" = 0 ]; then + log_info "Test script was started by 'root' - expanding permissions for '$NUT_CONFPATH/upsd.users' so unprivileged daemons (after de-elevation) may read it" + chmod 644 "$NUT_CONFPATH/upsd.users" + else + chmod 640 "$NUT_CONFPATH/upsd.users" + fi +} + +### 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" + + if [ "`id -u`" = 0 ]; then + log_info "Test script was started by 'root' - expanding permissions for '$NUT_CONFPATH/upsmon.conf' so unprivileged daemons (after de-elevation) may read it" + chmod 644 "$NUT_CONFPATH/upsmon.conf" + else + chmod 640 "$NUT_CONFPATH/upsmon.conf" + fi +} + +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" + + if [ "`id -u`" = 0 ]; then + log_info "Test script was started by 'root' - expanding permissions for '$NUT_CONFPATH/ups.conf' so unprivileged daemons (after de-elevation) may read it" + chmod 644 "$NUT_CONFPATH/ups.conf" + else + chmod 640 "$NUT_CONFPATH/ups.conf" + fi +} + +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] + # Avoid "sed -i", it behaves differently on some platforms + # and is completely absent on others [#1736 and earlier] + for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do + sed -e 's,^ups.status: *$,ups.status: OL BOOST,' "$F" > "$F.bak" + mv -f "$F.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 +FAILED_FUNCS="" +PASSED=0 + +testcase_upsd_no_configs_at_all() { + log_separator + log_info "[testcase_upsd_no_configs_at_all] Test UPSD without configs at all" + upsd -F + if [ "$?" = 0 ]; then + log_error "[testcase_upsd_no_configs_at_all] upsd should fail without configs" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_upsd_no_configs_at_all" + else + log_info "[testcase_upsd_no_configs_at_all] PASSED: upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_upsd_no_configs_driver_file() { + log_separator + log_info "[testcase_upsd_no_configs_driver_file] Test UPSD without driver config file" + generatecfg_upsd_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "[testcase_upsd_no_configs_driver_file] upsd should fail without driver config file" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_upsd_no_configs_driver_file" + else + log_info "[testcase_upsd_no_configs_driver_file] PASSED: upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_upsd_no_configs_in_driver_file() { + log_separator + log_info "[testcase_upsd_no_configs_in_driver_file] Test UPSD without drivers defined in config file" + generatecfg_upsd_trivial + generatecfg_ups_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "[testcase_upsd_no_configs_in_driver_file] upsd should fail without drivers defined in config file" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_upsd_no_configs_in_driver_file" + else + log_info "[testcase_upsd_no_configs_in_driver_file] PASSED: upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +upsd_start_loop() { + TESTCASE="${1-upsd_start_loop}" + + if isPidAlive "$PID_UPSD" ; then + return 0 + fi + + upsd -F & + PID_UPSD="$!" + log_debug "[${TESTCASE}] Tried to start UPSD as PID $PID_UPSD" + sleep 2 + # Due to a busy port, server could have died by now + + COUNTDOWN=60 + while [ "$COUNTDOWN" -gt 0 ]; do + sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + + # Is our server alive AND occupying the port? + PID_OK=true + isPidAlive "$PID_UPSD" || PID_OK=false # not running + PORT_OK=true + isBusy_NUT_PORT 2>/dev/null >/dev/null || PORT_OK=false # not busy + if "${PID_OK}" ; then + if "${PORT_OK}" ; then break ; fi + continue + fi + + # FIXME: If we are here, even once, then PID_UPSD which we + # knew has already disappeared... wait() for its exit-code? + # Give some time for ports to time out, if busy and that is + # why the server died. + PORT_WORD="" + ${PORT_OK} || PORT_WORD="not " + log_warn "[${TESTCASE}] Port ${NUT_PORT} is ${PORT_WORD}listening and UPSD PID $PID_UPSD is not alive, will sleep and retry" + + sleep 10 + upsd -F & + PID_UPSD="$!" + log_warn "[${TESTCASE}] Tried to start UPSD again, now as PID $PID_UPSD" + sleep 5 + done + + if [ "$COUNTDOWN" -le 50 ] ; then + # Should not get to this, except on very laggy systems maybe + log_warn "[${TESTCASE}] Had to wait a few retries for the UPSD process to appear" + fi + + # Return code is 0/OK if the server is alive AND occupying the port + if isPidAlive "$PID_UPSD" && isBusy_NUT_PORT 2>/dev/null >/dev/null ; then + log_debug "[${TESTCASE}] Port ${NUT_PORT} is listening and UPSD PID $PID_UPSD is alive" + return + fi + + log_error "[${TESTCASE}] Port ${NUT_PORT} is not listening and/or UPSD PID $PID_UPSD is not alive" + return 1 +} + +testcase_upsd_allow_no_device() { + log_separator + log_info "[testcase_upsd_allow_no_device] Test UPSD allowed to run without driver configs" + generatecfg_upsd_nodev + generatecfg_upsdusers_trivial + generatecfg_ups_trivial + if shouldDebug ; then + ls -la "$NUT_CONFPATH/" || true + fi + + upsd_start_loop "testcase_upsd_allow_no_device" + + res_testcase_upsd_allow_no_device=0 + if [ "$COUNTDOWN" -gt 0 ] \ + && isPidAlive "$PID_UPSD" \ + ; then + log_info "[testcase_upsd_allow_no_device] OK, upsd is running" + PASSED="`expr $PASSED + 1`" + + log_separator + log_info "[testcase_upsd_allow_no_device] Query listing from UPSD by UPSC (no devices configured yet) to test that UPSD responds to UPSC" + if runcmd upsc -l localhost:$NUT_PORT ; then + : + else + # Note: avoid exact matching for stderr, because it can have Init SSL messages etc. + if echo "$CMDERR" | grep "Error: Server disconnected" >/dev/null ; then + log_warn "[testcase_upsd_allow_no_device] Retry once to rule out laggy systems" + sleep 3 + runcmd upsc -l localhost:$NUT_PORT + fi + if echo "$CMDERR" | grep "Error: Server disconnected" >/dev/null ; then + log_warn "[testcase_upsd_allow_no_device] Retry once more to rule out very laggy systems" + sleep 15 + runcmd upsc -l localhost:$NUT_PORT + fi + [ "$CMDRES" = 0 ] || die "[testcase_upsd_allow_no_device] upsd does not respond on port ${NUT_PORT} ($?): $CMDOUT" + fi + if [ -n "$CMDOUT" ] ; then + log_error "[testcase_upsd_allow_no_device] got reply for upsc listing when none was expected: $CMDOUT" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_upsd_allow_no_device" + res_testcase_upsd_allow_no_device=1 + else + log_info "[testcase_upsd_allow_no_device] OK, empty response as expected" + PASSED="`expr $PASSED + 1`" + fi + else + log_error "[testcase_upsd_allow_no_device] upsd was expected to be running although no devices are defined; is ups.conf populated?" + ls -la "$NUT_CONFPATH/" || true + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_upsd_allow_no_device" + res_testcase_upsd_allow_no_device=1 + report_NUT_PORT + fi + + log_info "[testcase_upsd_allow_no_device] stopping upsd: $PID_UPSD" + UPSD_RES=0 + kill -15 $PID_UPSD + wait $PID_UPSD || UPSD_RES=$? + if [ "$res_testcase_upsd_allow_no_device" = 0 ] ; then + log_info "[testcase_upsd_allow_no_device] upsd exit-code was: $UPSD_RES" + else + log_error "[testcase_upsd_allow_no_device] upsd exit-code was: $UPSD_RES" + fi + if [ "$UPSD_RES" != 0 ]; then + return $UPSD_RES + fi + return $res_testcase_upsd_allow_no_device +} + +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_start_loop "sandbox" +} + +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="$!" + log_debug "Tried to start dummy-ups driver for 'dummy' as PID $PID_DUMMYUPS" + + if [ x"${TOP_SRCDIR}" != x ]; then + dummy-ups -a UPS1 -F & + PID_DUMMYUPS1="$!" + log_debug "Tried to start dummy-ups driver for 'UPS1' as PID $PID_DUMMYUPS1" + + dummy-ups -a UPS2 -F & + PID_DUMMYUPS2="$!" + log_debug "Tried to start dummy-ups driver for 'UPS2' as PID $PID_DUMMYUPS2" + fi + + sleep 5 + + if shouldDebug ; then + (ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy|'"`basename "$0"`"')' | grep -vE '(ssh|startups|grep)' || true + fi + + 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 + log_info "Starting dummy-ups driver(s) for sandbox - all expected processes are running" + return 0 + else + log_error "Starting dummy-ups driver(s) for sandbox - finished, but something seems to not be running" + return 1 + fi +} + +testcase_sandbox_start_upsd_alone() { + log_separator + log_info "[testcase_sandbox_start_upsd_alone] 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" + # For windows runners (strip CR if any): + EXPECTED_UPSLIST="`echo "$EXPECTED_UPSLIST" | tr -d '\r'`" + fi + + log_info "[testcase_sandbox_start_upsd_alone] Query listing from UPSD by UPSC (driver not running yet)" + res_testcase_sandbox_start_upsd_alone=0 + runcmd upsc -l localhost:$NUT_PORT || die "[testcase_sandbox_start_upsd_alone] upsd does not respond on port ${NUT_PORT} ($?): $CMDOUT" + # For windows runners (printf can do wonders, so strip CR if any): + if [ x"${TOP_SRCDIR}" != x ]; then + CMDOUT="`echo "$CMDOUT" | tr -d '\r'`" + fi + if [ x"$CMDOUT" != x"$EXPECTED_UPSLIST" ] ; then + log_error "[testcase_sandbox_start_upsd_alone] got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: '$CMDOUT'" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_start_upsd_alone" + res_testcase_sandbox_start_upsd_alone=1 + else + PASSED="`expr $PASSED + 1`" + fi + + log_info "[testcase_sandbox_start_upsd_alone] Query driver state from UPSD by UPSC (driver not running yet)" + runcmd upsc dummy@localhost:$NUT_PORT && { + log_error "upsc was supposed to answer with error exit code: $CMDOUT" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_start_upsd_alone" + res_testcase_sandbox_start_upsd_alone=1 + } + # Note: avoid exact matching for stderr, because it can have Init SSL messages etc. + if echo "$CMDERR" | grep 'Error: Driver not connected' >/dev/null ; then + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_start_upsd_alone] got some other reply for upsc query when 'Error: Driver not connected' was expected on stderr: '$CMDOUT'" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_start_upsd_alone" + res_testcase_sandbox_start_upsd_alone=1 + fi + + if [ "$res_testcase_sandbox_start_upsd_alone" = 0 ]; then + log_info "[testcase_sandbox_start_upsd_alone] PASSED: got just the failures expected for data server alone (driver not running yet)" + else + log_error "[testcase_sandbox_start_upsd_alone] got some unexpected failures, see above" + fi + + return $res_testcase_sandbox_start_upsd_alone +} + +testcase_sandbox_start_upsd_after_drivers() { + # Historically this is a fallback from testcase_sandbox_start_drivers_after_upsd + log_info "[testcase_sandbox_start_upsd_after_drivers] Test starting UPSD after drivers" + kill -15 $PID_UPSD 2>/dev/null + wait $PID_UPSD + + # Not calling upsd_start_loop() here, before drivers + # If the server starts, fine; if not - we retry below + upsd -F & + PID_UPSD="$!" + log_debug "[testcase_sandbox_start_upsd_after_drivers] Tried to start UPSD as PID $PID_UPSD" + + sandbox_start_drivers + sandbox_start_upsd + + sleep 5 + + COUNTDOWN=90 + while [ "$COUNTDOWN" -gt 0 ]; do + # For query errors or known wait, keep looping + runcmd upsc dummy@localhost:$NUT_PORT \ + && case "$CMDOUT" in + *"ups.status: WAIT"*) ;; + *) log_info "Got output:" ; echo "$CMDOUT" ; break ;; + esac + sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + done + + if [ "$COUNTDOWN" -le 88 ] ; then + log_warn "[testcase_sandbox_start_upsd_after_drivers] Had to wait a few retries for the dummy driver to connect" + fi + + if [ "$COUNTDOWN" -le 1 ] ; then + report_NUT_PORT + die "[testcase_sandbox_start_upsd_after_drivers] upsd does not respond on port ${NUT_PORT} ($?)" + fi + + log_info "[testcase_sandbox_start_upsd_after_drivers] PASSED: upsd responds on port ${NUT_PORT}" +} + +testcase_sandbox_start_drivers_after_upsd() { + #sandbox_start_upsd + testcase_sandbox_start_upsd_alone + sandbox_start_drivers + + log_info "[testcase_sandbox_start_drivers_after_upsd] Query driver state from UPSD by UPSC after driver startup" + # Timing issues: upsd starts, we wait 10 sec, drivers start and init, + # at 20 sec upsd does not see them yet, at 30 sec the sockets connect + # but info does not come yet => may be "driver stale", finally at + # 40+(drv)/50+(upsd) sec a DUMPALL is processed (regular 30-sec loop?) - + # so tightly near a minute until we have sturdy replies. + COUNTDOWN=90 + while [ "$COUNTDOWN" -gt 0 ]; do + # For query errors or known wait, keep looping. May get: + # driver.state: updateinfo + # ups.status: WAIT + runcmd upsc dummy@localhost:$NUT_PORT \ + && case "$CMDOUT" in + *"ups.status: WAIT"*) ;; + *) log_info "[testcase_sandbox_start_drivers_after_upsd] Got output:" ; echo "$CMDOUT" ; break ;; + esac + sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + done + + if [ "$COUNTDOWN" -le 88 ] ; then + log_warn "[testcase_sandbox_start_drivers_after_upsd] Had to wait a few retries for the dummy driver to connect" + fi + + if [ "$COUNTDOWN" -le 1 ] ; then + # Should not get to this, except on very laggy systems maybe + log_error "[testcase_sandbox_start_drivers_after_upsd] Query failed, retrying with UPSD started after drivers" + testcase_sandbox_start_upsd_after_drivers + fi + + if [ x"${TOP_SRCDIR}" != x ]; then + log_info "[testcase_sandbox_start_drivers_after_upsd] Wait for dummy UPSes with larger data sets to initialize" + for U in UPS1 UPS2 ; do + COUNTDOWN=90 + # TODO: Convert to runcmd()? + OUT="" + while [ x"$OUT" = x"ups.status: WAIT" ] ; do + OUT="`upsc $U@localhost:$NUT_PORT ups.status`" || break + [ x"$OUT" = x"ups.status: WAIT" ] || { log_info "[testcase_sandbox_start_drivers_after_upsd] Got output:"; echo "$OUT"; break; } + sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + # Systemic error, e.g. could not create socket file? + [ "$COUNTDOWN" -lt 1 ] && die "[testcase_sandbox_start_drivers_after_upsd] Dummy driver did not start or respond in time" + done + if [ "$COUNTDOWN" -le 88 ] ; then + log_warn "[testcase_sandbox_start_drivers_after_upsd] Had to wait a few retries for the $U driver to connect" + fi + done + fi + + log_info "[testcase_sandbox_start_drivers_after_upsd] PASSED: Expected drivers are now responding via UPSD" +} + +testcase_sandbox_upsc_query_model() { + log_info "[testcase_sandbox_upsc_query_model] Query model from dummy device" + runcmd upsc dummy@localhost:$NUT_PORT device.model || die "[testcase_sandbox_upsc_query_model] upsd does not respond on port ${NUT_PORT} ($?): $CMDOUT" + if [ x"$CMDOUT" != x"Dummy UPS" ] ; then + log_error "[testcase_sandbox_upsc_query_model] got this reply for upsc query when 'Dummy UPS' was expected: $CMDOUT" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_model" + else + PASSED="`expr $PASSED + 1`" + log_info "[testcase_sandbox_upsc_query_model] PASSED: got expected model from dummy device: $CMDOUT" + fi +} + +testcase_sandbox_upsc_query_bogus() { + log_info "[testcase_sandbox_upsc_query_bogus] Query driver state from UPSD by UPSC for bogus info" + runcmd upsc dummy@localhost:$NUT_PORT ups.bogus.value && { + log_error "[testcase_sandbox_upsc_query_bogus] upsc was supposed to answer with error exit code: $CMDOUT" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_bogus" + } + # Note: avoid exact matching for stderr, because it can have Init SSL messages etc. + if echo "$CMDERR" | grep 'Error: Variable not supported by UPS' >/dev/null ; then + PASSED="`expr $PASSED + 1`" + log_info "[testcase_sandbox_upsc_query_bogus] PASSED: got expected reply to bogus query" + else + log_error "[testcase_sandbox_upsc_query_bogus] got some other reply for upsc query when 'Error: Variable not supported by UPS' was expected on stderr: stderr:'$CMDERR' / stdout:'$CMDOUT'" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_bogus" + fi +} + +testcase_sandbox_upsc_query_timer() { + log_separator + log_info "[testcase_sandbox_upsc_query_timer] 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 + # TODO: Any need to convert to runcmd()? + OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT1" ; sleep 3 + OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT2" + OUT3="" + OUT4="" + if [ x"$OUT1" = x"$OUT2" ]; then + sleep 3 + OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT3" + if [ x"$OUT2" = x"$OUT3" ]; then + sleep 3 + OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT4" + fi + fi + if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then + log_info "[testcase_sandbox_upsc_query_timer] PASSED: ups.status flips over time" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_upsc_query_timer] ups.status did not flip over time" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_timer" + 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 + PY_SHEBANG="`head -1 "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py"`" + if [ x"${PY_SHEBANG}" = x"#!no" ] ; then + return 1 + fi + return 0 +} + +testcase_sandbox_python_without_credentials() { + isTestablePython || return 0 + log_separator + log_info "[testcase_sandbox_python_without_credentials] 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 "[testcase_sandbox_python_without_credentials] PASSED: PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_python_without_credentials] PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_python_without_credentials" + 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 "[testcase_sandbox_python_with_credentials] 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 "[testcase_sandbox_python_with_credentials] PASSED: PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_python_with_credentials] PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_python_with_credentials" + fi +} + +testcase_sandbox_python_with_upsmon_credentials() { + isTestablePython || return 0 + + log_separator + log_info "[testcase_sandbox_python_with_upsmon_credentials] Call Python module test suite: PyNUT (NUT Python bindings) with upsmon role login credentials" + if ( + NUT_USER='dummy-admin' + NUT_PASS="${TESTPASS_UPSMON_PRIMARY}" + export NUT_USER NUT_PASS + "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" + ) ; then + log_info "[testcase_sandbox_python_with_upsmon_credentials] PASSED: PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_python_with_upsmon_credentials] PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_python_with_upsmon_credentials" + fi +} + +testcases_sandbox_python() { + isTestablePython || return 0 + testcase_sandbox_python_without_credentials + testcase_sandbox_python_with_credentials + testcase_sandbox_python_with_upsmon_credentials +} + +#################################### + +isTestableCppNIT() { + # We optionally make and here can run C++ client tests: + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/tests/cppnit" ] \ + ; then + log_warn "SKIP: ${TOP_BUILDDIR}/tests/cppnit: Not found" + return 1 + fi + return 0 +} + +testcase_sandbox_cppnit_without_creds() { + isTestableCppNIT || return 0 + + log_separator + log_info "[testcase_sandbox_cppnit_without_creds] Call libnutclient test suite: cppnit without login credentials" + if ( unset NUT_USER || true + unset NUT_PASS || true + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "[testcase_sandbox_cppnit_without_creds] PASSED: cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_cppnit_without_creds] cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_cppnit_without_creds" + fi +} + +testcase_sandbox_cppnit_simple_admin() { + isTestableCppNIT || return 0 + + log_separator + log_info "[testcase_sandbox_cppnit_simple_admin] 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 "[testcase_sandbox_cppnit_simple_admin] PASSED: cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_cppnit_simple_admin] cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_cppnit_simple_admin" + fi +} + +testcase_sandbox_cppnit_upsmon_primary() { + isTestableCppNIT || return 0 + + log_separator + log_info "[testcase_sandbox_cppnit_upsmon_primary] 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 "[testcase_sandbox_cppnit_upsmon_primary] PASSED: cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_cppnit_upsmon_primary] cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_cppnit_upsmon_primary" + fi +} + +testcase_sandbox_cppnit_upsmon_master() { + isTestableCppNIT || return 0 + + log_separator + log_info "[testcase_sandbox_cppnit_upsmon_master] 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 "[testcase_sandbox_cppnit_upsmon_master] PASSED: cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "[testcase_sandbox_cppnit_upsmon_master] cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_cppnit_upsmon_master" + 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 +} + +#################################### + +isTestableNutScanner() { + # We optionally make and here can run nut-scanner (as NUT client) + # tests, which tangentially tests the C client library: + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/tools/nut-scanner/nut-scanner" ] \ + ; then + log_warn "SKIP: ${TOP_BUILDDIR}/tools/nut-scanner/nut-scanner: Not found" + return 1 + fi + return 0 +} + +testcase_sandbox_nutscanner_list() { + isTestableNutScanner || return 0 + + log_separator + log_info "[testcase_sandbox_nutscanner_list] Call libupsclient test suite: nut-scanner on localhost:${NUT_PORT}" + log_info "[testcase_sandbox_nutscanner_list] Preparing LD_LIBRARY_PATH='${LD_LIBRARY_PATH_CLIENT}'" + + # Note: for some reason `LD_LIBRARY_PATH=... runcmd ...` loses it :\ + LD_LIBRARY_PATH="${LD_LIBRARY_PATH_CLIENT}" + export LD_LIBRARY_PATH + + # NOTE: Currently mask mode is IPv4 only + runcmd "${TOP_BUILDDIR}/tools/nut-scanner/nut-scanner" -m 127.0.0.1/32 -O -p "${NUT_PORT}" \ + && test -n "$CMDOUT" \ + || runcmd "${TOP_BUILDDIR}/tools/nut-scanner/nut-scanner" -s localhost -O -p "${NUT_PORT}" + + LD_LIBRARY_PATH="${LD_LIBRARY_PATH_ORIG}" + export LD_LIBRARY_PATH + + log_info "[testcase_sandbox_nutscanner_list] findings from nut-scanner:" + echo "$CMDOUT" + log_info "[testcase_sandbox_nutscanner_list] inspecting these findings from nut-scanner..." + + # Note: the reported "driver" string is not too helpful as a "nutclient". + # In practice this could be a "dummy-ups" repeater or "clone" driver, + # or some of the config elements needed for upsmon (lacking creds/role) + # Also note that before PR #2247 nut-scanner returned "nutdev" + # section names, but now it returns "nutdev-" to differentiate + # the scanned buses (serial, snmp, usb, etc.) + if ( + test -n "$CMDOUT" \ + && echo "$CMDOUT" | grep -E '^\[nutdev-nut1\]$' \ + && echo "$CMDOUT" | grep 'port = "dummy@' \ + || return + + if [ "${NUT_PORT}" = 3493 ] || [ x"$NUT_PORT" = x ]; then + log_info "[testcase_sandbox_nutscanner_list] Note: not testing for suffixed port number" >&2 + else + echo "$CMDOUT" | grep -E 'dummy@.*'":${NUT_PORT}" \ + || { + log_error "[testcase_sandbox_nutscanner_list] dummy@... not found" >&2 + return 1 + } + fi + + if [ x"${TOP_SRCDIR}" = x ]; then + log_info "[testcase_sandbox_nutscanner_list] Note: only testing one dummy device" >&2 + else + echo "$CMDOUT" | grep -E '^\[nutdev-nut2\]$' \ + && echo "$CMDOUT" | grep 'port = "UPS1@' \ + && echo "$CMDOUT" | grep -E '^\[nutdev-nut3\]$' \ + && echo "$CMDOUT" | grep 'port = "UPS2@' \ + || { + log_error "[testcase_sandbox_nutscanner_list] something about UPS1/UPS2 not found" >&2 + return 1 + } + fi + + if [ x"${TOP_SRCDIR}" = x ]; then + PORTS_WANT=1 + else + PORTS_WANT=3 + fi + PORTS_SEEN="`echo "$CMDOUT" | grep -Ec 'port *='`" + + if [ "$PORTS_WANT" != "$PORTS_SEEN" ]; then + log_error "[testcase_sandbox_nutscanner_list] Too many 'port=' lines: want $PORTS_WANT != seen $PORTS_SEEN" >&2 + return 1 + fi + ) >/dev/null ; then + log_info "[testcase_sandbox_nutscanner_list] PASSED: nut-scanner found all expected devices" + PASSED="`expr $PASSED + 1`" + else + if ( echo "$CMDERR" | grep -E "Cannot load NUT library.*libupsclient.*found.*NUT search disabled" ) ; then + log_warn "[testcase_sandbox_nutscanner_list] SKIP: ${TOP_BUILDDIR}/tools/nut-scanner/nut-scanner: $CMDERR" + else + log_error "[testcase_sandbox_nutscanner_list] nut-scanner complained or did not return all expected data, check above" + FAILED="`expr $FAILED + 1`" + FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_nutscanner_list" + fi + fi +} + +testcases_sandbox_nutscanner() { + isTestableNutScanner || return 0 + testcase_sandbox_nutscanner_list +} + +#################################### + +# 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 + testcases_sandbox_nutscanner + + log_separator + sandbox_forget_configs +} + +testgroup_sandbox_python() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_python + + log_separator + sandbox_forget_configs +} + +testgroup_sandbox_cppnit() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_cppnit + + log_separator + sandbox_forget_configs +} + +testgroup_sandbox_cppnit_simple_admin() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcase_sandbox_cppnit_simple_admin + + log_separator + sandbox_forget_configs +} + +testgroup_sandbox_nutscanner() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_nutscanner + + log_separator + sandbox_forget_configs +} + +################################################################ + +case "${NIT_CASE}" in + isBusy_NUT_PORT) DEBUG=yes isBusy_NUT_PORT ;; + cppnit) testgroup_sandbox_cppnit ;; + python) testgroup_sandbox_python ;; + nutscanner|nut-scanner) testgroup_sandbox_nutscanner ;; + testcase_*|testgroup_*|testcases_*|testgroups_*) + log_warn "========================================================" + log_warn "You asked to run just a specific testcase* or testgroup*" + log_warn "Be sure to have previously run with DEBUG_SLEEP and" + log_warn " have exported the NUT_PORT upsd is listening on!" + log_warn "========================================================" + "${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" +if [ -n "$FAILED_FUNCS" ]; then + for F in $FAILED_FUNCS ; do echo "$F" ; done | sort | uniq -c +fi + +# Allow to leave the sandbox daemons running for a while, +# to experiment with them interactively: +if [ -n "${DEBUG_SLEEP-}" ] ; then + if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then : ; else + DEBUG_SLEEP=60 + fi + + log_separator + log_info "Sleeping now as asked (for ${DEBUG_SLEEP} seconds starting `date -u`), so you can play with the driver and server running; hint: export NUT_PORT=$NUT_PORT" + log_separator + + sleep "${DEBUG_SLEEP}" + log_info "Sleep finished" + log_separator +fi + +stop_daemons + +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..1c21cd589d --- /dev/null +++ b/tests/cpputest-client.cpp @@ -0,0 +1,479 @@ +/* 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_list_ups_clients ); + 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_list_ups_clients(); + 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_list_ups_clients() { + nut::TcpClient c("localhost", NUT_PORT); + std::map> deviceClients; + 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; + /* no failure here */ + } + + try { + c.deviceLogin(NUT_PRIMARY_DEVICE); + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not log into primary device (envvars not set?) so test below should return empty client lists: " << ex.what() << std::endl; + /* no failure here */ + } + + try { + deviceClients = c.listDeviceClients(); + std::cerr << "[D] Got device client list (" << deviceClients.size() << "): ["; + for (std::map>::iterator itM = deviceClients.begin(); + itM != deviceClients.end(); itM++ + ) { + if (itM != deviceClients.begin()) { + std::cerr << "," << std::endl; + } + std::cerr << "{ \"" << itM->first << "\" : "; + for (std::set::iterator it = itM->second.begin(); + it != itM->second.end(); it++ + ) { + if (it != itM->second.begin()) { + std::cerr << ", "; + } + std::cerr << '"' << *it << '"'; + } + std::cerr << " }"; + } + std::cerr << " ]" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not device client list: " << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to list UPS with TcpClient: threw NutException", + noException); +} + +void NutActiveClientTest::test_auth_user() { + if (NUT_USER.empty()) { + std::cerr << "[D] SKIPPING test_auth_user()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + /* Note: no high hopes here, credentials are checked by server + * when running critical commands, not at auth request itself */ + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as a simple user: " << ex.what() << std::endl; + noException = false; + } + + if (!NUT_SETVAR_DEVICE.empty()) { + try { + TrackingResult tres; + TrackingID tid; + int i; + std::string nutVar = "ups.status"; /* Has a risk of flip-flop with NIT dummy setup */ + std::string s1 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::string sTest = s1 + "-test"; + + std::cerr << "[D] Got initial device '" << NUT_SETVAR_DEVICE + << "' variable '" << nutVar << "' value: " << s1 << std::endl; + CPPUNIT_ASSERT_MESSAGE( + "Did not expect empty value here", + !s1.empty()); + + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, sTest); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + /* Check what we got after set */ + /* Note that above we told the server to tell the driver + * to set a dstate entry; below we ask the server to ask + * the driver and relay the answer to us. The dummy-ups + * driver may also be in a sleeping state between cycles. + * Data propagation may be not instantaneous, so we loop + * for a while to see the expected value (or give up). + */ + std::string s2; + for (i = 0; i < 100 ; i++) { + s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s2 == sTest) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s2 + << " after " << (100 * i) << "msec" + << std::endl; + + /* Fix it back */ + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s1); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + std::string s3; + for (i = 0; i < 100 ; i++) { + s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s3 == s1) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s3 + << " after " << (100 * i) << "msec" + << std::endl; + + if (s3 != s1) { + std::cerr << "[D] Final device variable value '" << s3 + << "' differs from original '" << s1 + << "'" << std::endl; + noException = false; + } + + if (s2 == s1) { + std::cerr << "[D] Tweaked device variable value '" << s2 + << "' does not differ from original '" << s1 + << "'" << std::endl; + noException = false; + } + + if (noException) { + std::cerr << "[D] Tweaked device variable value OK" << std::endl; + } + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Failed to set device variable: " + << ex.what() << std::endl; + noException = false; + } + } else { + std::cerr << "[D] SKIPPING test_auth_user() active test " + << "(got no NUT_SETVAR_DEVICE to poke)" << std::endl; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient or tweak device variable", + noException); +} + +void NutActiveClientTest::test_auth_primary() { + if (NUT_USER.empty() || NUT_PRIMARY_DEVICE.empty()) { + std::cerr << "[D] SKIPPING test_auth_primary()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as an upsmon primary user: " + << ex.what() << std::endl; + noException = false; + } + + try { + Device d = c.getDevice(NUT_PRIMARY_DEVICE); + bool gotPrimary = false; + bool gotMaster = false; + + try { + c.deviceMaster(NUT_PRIMARY_DEVICE); + gotMaster = true; + std::cerr << "[D] Elevated as MASTER without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as MASTER for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + try { + c.devicePrimary(NUT_PRIMARY_DEVICE); + gotPrimary = true; + std::cerr << "[D] Elevated as PRIMARY without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as PRIMARY for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + if (!gotMaster && !gotPrimary) + noException = false; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE + << " not found on Data Server: " + << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient: threw NutException", + noException); +} + +} // namespace nut {} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic pop +#endif diff --git a/tests/cpputest.cpp b/tests/cpputest.cpp index d0b0adbec7..099e00ebd8 100644 --- a/tests/cpputest.cpp +++ b/tests/cpputest.cpp @@ -19,13 +19,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "common.h" + #include +#include #include #include #include #include #include -#include "common.h" + +extern "C" { +#include "timehead.h" + +/* Let tests also see this flag */ +extern bool verbose; +} + +bool verbose = false; // Inspired by https://stackoverflow.com/a/66702001 class MyCustomProgressTestListener : public CppUnit::TextTestProgressListener { @@ -39,18 +50,19 @@ class MyCustomProgressTestListener : public CppUnit::TextTestProgressListener { // [-Werror,-Wweak-vtables] void MyCustomProgressTestListener::startTest(CppUnit::Test *test) { //fprintf(stderr, "starting test %s\n", test->getName().c_str()); - std::cerr << "starting test " << test->getName() << std::endl; + std::cerr << "starting test " << (test == nullptr ? "" : test->getName()) << std::endl << std::flush; } int main(int argc, char* argv[]) { - bool verbose = false; if (argc > 1) { if (strcmp("-v", argv[1]) == 0 || strcmp("--verbose", argv[1]) == 0 ) { verbose = true; } } + ::srand(static_cast(::time(nullptr))); + /* Get the top level suite from the registry */ std::cerr << "D: Getting test suite..." << std::endl; CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); @@ -68,7 +80,8 @@ int main(int argc, char* argv[]) if (verbose) { /* Add a listener to report test names */ std::cerr << "D: Setting test runner listener for test names..." << std::endl; - MyCustomProgressTestListener progress; + /* Only allocate when needed; static to avoid freeing */ + static MyCustomProgressTestListener progress; runner.eventManager().addListener(&progress); } diff --git a/tests/generic_gpio_liblocal.c b/tests/generic_gpio_liblocal.c new file mode 100644 index 0000000000..2716a6db76 --- /dev/null +++ b/tests/generic_gpio_liblocal.c @@ -0,0 +1,135 @@ +/* generic_gpio_libglocal.c - gpio device emulation library for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "common.h" +#include +#include +#include "generic_gpio_utest.h" + +static char chipName[NUT_GPIO_CHIPNAMEBUF]; +static unsigned int num_lines=0; + +struct gpiod_chip *gpiod_chip_open_by_name(const char *name) { + strcpy(chipName, name); + if(strcmp(name, "gpiochip1")) + return (struct gpiod_chip *)1; + else { + errno = EACCES; + return NULL; + } +} + +unsigned int gpiod_chip_num_lines(struct gpiod_chip *chip) { + NUT_UNUSED_VARIABLE(chip); + if(!strcmp(chipName, "gpiochip2")) + return 2; + return 32; +} + +int gpiod_chip_get_lines(struct gpiod_chip *chip, + unsigned int *offsets, unsigned int num_offsets, + struct gpiod_line_bulk *bulk) { + NUT_UNUSED_VARIABLE(chip); + NUT_UNUSED_VARIABLE(offsets); + NUT_UNUSED_VARIABLE(bulk); + num_lines=num_offsets; + return 0; +} + +int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, + const struct gpiod_line_request_config *config, + const int *default_vals) +{ + NUT_UNUSED_VARIABLE(bulk); + NUT_UNUSED_VARIABLE(config); + NUT_UNUSED_VARIABLE(default_vals); + return 0; +} + +static int gStatus = 0; +static int errReqFor_line_get_value_bulk=0; + +void setNextLinesReadToFail(void) { + errReqFor_line_get_value_bulk=1; +} + +int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, + int *values) +{ + unsigned int i; + int pinPos = 1; + NUT_UNUSED_VARIABLE(bulk); + + if(errReqFor_line_get_value_bulk) { + errReqFor_line_get_value_bulk=0; + errno = EPERM; + return -1; + } + for(i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "main.h" +#include "dstate.h" +#include "attribute.h" +#include "nut_stdint.h" +#include "generic_gpio_utest.h" + +#include +#include +#include + +extern struct gpioups_t *gpioupsfd; + +static int done=0; +static int test_with_exit; +static jmp_buf env_buffer; +static FILE * testData; +static int fEof; +static int cases_passed; +static int cases_failed; + +static char * pass_fail[2] = {"pass", "fail"}; + +void getWithoutUnderscores(char *var) { + int i; + + fEof=fscanf(testData, "%s", var); + for (i=0; var[i]; i++) { + if (var[i]=='_') var[i]=' '; + } +} + +#define MFR "CyberPower" +#define MODEL "CyberShield CSN27U12V" +#define DESCRIPTION "modem and DNS server UPS" + +int get_test_status(struct gpioups_t *result, int on_fail_path) { + int expecting_failure; + int upsLinesCount; /* no of lines used in rules */ + int upsMaxLine; /* maximum line number referenced in rules */ + int rulesCount; /* rules subitem count: no of NUT states defined in rules*/ + char stateName[12]; /* NUT state name for rules in cRules */ + int subCount; /* element count in translated rules subitem */ + int ruleInt; + int i, j; + + fEof = fscanf(testData, "%d", &expecting_failure); + if (on_fail_path) { + if(expecting_failure) cases_failed++; else cases_passed++; + return expecting_failure; + } + + if (!expecting_failure) { + cases_failed++; + printf("expecting case to fail\n"); + return 1; + } + + fEof = fscanf(testData, "%d", &upsLinesCount); + if (result->upsLinesCount!=upsLinesCount) { + cases_failed++; + printf("expecting upsLinesCount %d, got %d\n", upsLinesCount, result->upsLinesCount); + return 1; + } + + fEof = fscanf(testData, "%d", &upsMaxLine); + if (result->upsMaxLine!=upsMaxLine) { + cases_failed++; + printf("expecting rulesCount %d, got %d\n", upsMaxLine, result->upsMaxLine); + return 1; + } + + fEof = fscanf(testData, "%d", &rulesCount); + if (result->rulesCount!=rulesCount) { + cases_failed++; + printf("expecting rulesCount %d, got %d\n", rulesCount, result->rulesCount); + return 1; + } + + for (i=0; irulesCount; i++) { + fEof=fscanf(testData, "%s", stateName); + if(!strcmp(result->rules[i]->stateName,stateName)) { + cases_failed++; + printf("expecting stateName %s, got %s for rule %d\n", stateName, result->rules[i]->stateName, i); + return 1; + } + fEof=fscanf(testData, "%d", &subCount); + if(result->rules[i]->subCount!=subCount) { + cases_failed++; + printf("expecting subCount %d, got %d for rule %d\n", result->rules[i]->subCount, subCount, i); + return 1; + } + for (j=0; jrules[i]->cRules[j]!=ruleInt) { + cases_failed++; + printf("expecting cRule %d, got %d for rule %d subRule %d\n", ruleInt, result->rules[i]->cRules[j], i, j); + return 1; + } + } + } + + cases_passed++; + return 0; +} + +void exit(int code) +{ + if (!done) { + longjmp(env_buffer, 1); + } + else { + _exit(code); + } +} + +int main(int argc, char **argv) { + int jmp_result; + char rules[128]; + char testType[128]; + char testDescFileNameBuf[LARGEBUF]; + char *testDescFileName = "generic_gpio_test.txt"; + unsigned int i; + + test_with_exit=0; + + if(argc==2) { + testDescFileName=argv[1]; + } + + testData = fopen (testDescFileName, "r"); + if(!testData) { + if (!strchr(testDescFileName, '/')) { + /* "srcdir" may be set by automake test harness, see + * https://www.gnu.org/software/automake/manual/1.12.2/html_node/Scripts_002dbased-Testsuites.html + */ + char *testDescFileDir = getenv("srcdir"); + if (testDescFileDir) { + printf("failed to open test description file %s " + "in current working directory, " + "retrying with srcdir %s\n", + testDescFileName, testDescFileDir); + if (snprintf(testDescFileNameBuf, sizeof(testDescFileNameBuf), "%s/%s", + testDescFileDir, testDescFileName) > 0 + ) { + testDescFileName = testDescFileNameBuf; + testData = fopen (testDescFileName, "r"); + } + } + } + + if(!testData) { + done = 1; + printf("failed to open test description file %s\n", testDescFileName); + exit(EXIT_FAILURE); + } + } + + cases_passed=0; + cases_failed=0; + fEof = 1; + for (i=0; fEof!=EOF; i++) { + char fmt[16]; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* To avoid safety warnings, must provide a limit + * here (bufsize - 1), and use fixed format strings + * because scanf() does not support asterisk for + * width specifier; have to create it on the fly. + */ + snprintf(fmt, sizeof(fmt), "%%%" PRIuSIZE "s", sizeof(rules)-1); + do { + fEof=fscanf(testData, fmt, rules); + } while(strcmp("*", rules)); + snprintf(fmt, sizeof(fmt), "%%%" PRIuSIZE "s", sizeof(testType)-1); + fEof=fscanf(testData, fmt, testType); + snprintf(fmt, sizeof(fmt), "%%%" PRIuSIZE "s", sizeof(rules)-1); + fEof=fscanf(testData, fmt, rules); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + if(fEof!=EOF) { + if(!strcmp(testType, "rules")) { + struct gpioups_t *upsfdtest=xcalloc(sizeof(*upsfdtest),1); + jmp_result = setjmp(env_buffer); + if(jmp_result) { /* test case exiting */ + generic_gpio_close(upsfdtest); + printf("%s %s test rule %u [%s]\n", pass_fail[get_test_status(upsfdtest, 1)], testType, i, rules); + } else { /* run test case */ + get_ups_rules(upsfdtest, (unsigned char *)rules); + generic_gpio_close(upsfdtest); + printf("%s %s test rule %u [%s]\n", pass_fail[get_test_status(upsfdtest, 0)], testType, i, rules); + } + } + if(!strcmp(testType, "states")) { + int expectedStateValue; + int calculatedStateValue; + struct gpioups_t *upsfdtest=xcalloc(sizeof(*upsfdtest),1); + int j; + + get_ups_rules(upsfdtest, (unsigned char *)rules); + upsfdtest->upsLinesStates=xcalloc(sizeof(int),upsfdtest->upsLinesCount); + for (j=0; j < upsfdtest->upsLinesCount; j++) { + fEof=fscanf(testData, "%d", &upsfdtest->upsLinesStates[j]); + } + for (j=0; j < upsfdtest->rulesCount; j++) { + fEof=fscanf(testData, "%d", &expectedStateValue); + calculatedStateValue=calc_rule_states(upsfdtest->upsLinesStates, upsfdtest->rules[j]->cRules, upsfdtest->rules[j]->subCount, 0); + if (expectedStateValue==calculatedStateValue) { + printf("%s %s test rule %u [%s]\n", pass_fail[0], testType, i, rules); + cases_passed++; + } else { + int k; + printf("%s %s test rule %u [%s] %s", pass_fail[1], testType, i, rules, upsfdtest->rules[j]->stateName); + for(k=0; kupsLinesCount; k++) { + printf(" %d", upsfdtest->upsLinesStates[k]); + } + printf("\n"); + cases_failed++; + } + } + generic_gpio_close(upsfdtest); + } + if(!strcmp(testType, "update")) { + char upsStatus[256]; + char chargeStatus[256]; + char chargeLow[256]; + char charge[256]; + struct gpioups_t *upsfdtest=xcalloc(sizeof(*upsfdtest),1); + int j; + + /* "volatile" trickery to avoid the likes of: + * error: variable 'failed' might be clobbered by 'longjmp' or 'vfork' [-Werror=clobbered] + * due to presence of setjmp(). + */ + int volatile failed = 0; + const char * volatile currUpsStatus = NULL; + const char * volatile currChargerStatus = NULL; + const char * volatile currCharge = NULL; + + get_ups_rules(upsfdtest, (unsigned char *)rules); + upsfdtest->upsLinesStates=xcalloc(sizeof(int),upsfdtest->upsLinesCount); + for (j = 0; j < upsfdtest->upsLinesCount; j++) { + fEof=fscanf(testData, "%d", &upsfdtest->upsLinesStates[j]); + } + getWithoutUnderscores(upsStatus); + getWithoutUnderscores(chargeStatus); + getWithoutUnderscores(chargeLow); + getWithoutUnderscores(charge); + if (strcmp(chargeLow, ".")) + dstate_setinfo("battery.charge.low", "%s", chargeLow); + jmp_result = setjmp(env_buffer); + if (jmp_result) { + failed=1; + generic_gpio_close(upsfdtest); + } else { + update_ups_states(upsfdtest); + currUpsStatus=dstate_getinfo("ups.status"); + currChargerStatus=dstate_getinfo("battery.charger.status"); + currCharge=dstate_getinfo("battery.charge"); + if(strcmp(currUpsStatus, upsStatus)) failed=1; + if( strcmp(chargeStatus,".") && (!currChargerStatus || strcmp(currChargerStatus, chargeStatus))) failed=1; + if(!strcmp(chargeStatus,".") && currChargerStatus!=NULL) failed=1; + if( strcmp(chargeLow,".") && strcmp(charge,".") && (!currCharge || strcmp(currCharge, charge))) failed=1; + if(!strcmp(chargeLow,".") && !strcmp(charge,".") && currCharge!=NULL) failed=1; + generic_gpio_close(upsfdtest); + } + printf("%s %s test rule %u [%s] ([%s] %s %s (%s)) ([%s] %s %s)\n", + pass_fail[failed], testType, i, rules, + upsStatus, chargeStatus, charge, chargeLow, + NUT_STRARG(currUpsStatus), + NUT_STRARG(currChargerStatus), + NUT_STRARG(currCharge)); + if (!failed) { + cases_passed++; + } else { + cases_failed++; + } + vartab_free(); vartab_h = NULL; + } + if(!strcmp(testType, "library")) { + char chipNameLocal[NUT_GPIO_CHIPNAMEBUF]; + int expecting_failure, failed; + char subType[NUT_GPIO_SUBTYPEBUF]; + fEof=fscanf(testData, "%d", &expecting_failure); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* To avoid safety warnings, must provide a limit + * here (bufsize - 1), and use fixed format strings + * because scanf() does not support asterisk for + * width specifier; have to create it on the fly. + */ + snprintf(fmt, sizeof(fmt), "%%%us", NUT_GPIO_CHIPNAMEBUF-1); + fEof=fscanf(testData, fmt, chipNameLocal); + snprintf(fmt, sizeof(fmt), "%%%us", NUT_GPIO_SUBTYPEBUF-1); + fEof=fscanf(testData, fmt, subType); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + jmp_result = setjmp(env_buffer); + failed = expecting_failure; + if(jmp_result) { /* test case exiting */ + if(expecting_failure) failed=0; + upsdrv_cleanup(); + } else { + if(expecting_failure) failed=1; + device_path = chipNameLocal; + if(strcmp(subType, "initinfo") || !expecting_failure) { + addvar(VAR_VALUE, "rules", ""); + storeval("rules", rules); + } + addvar(VAR_VALUE, "mfr", MFR); + storeval("mfr", MFR); + addvar(VAR_VALUE, "model", MODEL); + storeval("model", MODEL); + dstate_setinfo("device.description", DESCRIPTION); + upsdrv_initups(); + if(!strcmp(subType, "initinfo")) { + upsdrv_makevartable(); + if(expecting_failure) setNextLinesReadToFail(); + upsdrv_initinfo(); + if(!dstate_getinfo("device.mfr") || strcmp(dstate_getinfo("device.mfr"), MFR) || + !dstate_getinfo("device.model") || strcmp(dstate_getinfo("device.model"), MODEL) || + !dstate_getinfo("device.description") || strcmp(dstate_getinfo("device.description"), DESCRIPTION)) failed=1; + } + if(!strcmp(subType, "updateinfo")) { + int k; + for(k=0; kupsLinesCount; k++) { + gpioupsfd->upsLinesStates[k]=-1; + } + if(expecting_failure) setNextLinesReadToFail(); + upsdrv_updateinfo(); + for(k=0; kupsLinesCount && failed!=1; k++) { + if(gpioupsfd->upsLinesStates[k]<0) failed=1; + } + } + upsdrv_cleanup(); + } + printf("%s %s %s test rule %u [%s] %s %d\n", + pass_fail[failed], testType, subType, i, rules, chipNameLocal, expecting_failure); + if (!failed) { + cases_passed++; + } else { + cases_failed++; + } + vartab_free(); + vartab_h = NULL; + } + } + } + + printf("test_rules completed. Total cases %d, passed %d, failed %d\n", + cases_passed+cases_failed, cases_passed, cases_failed); + fclose(testData); + done = 1; + + /* Return 0 (exit-code OK, boolean false) if no tests failed and some ran */ + if ( (cases_failed == 0) && (cases_passed > 0) ) + return 0; + + return 1; +} diff --git a/tests/generic_gpio_utest.h b/tests/generic_gpio_utest.h new file mode 100644 index 0000000000..9ad884319d --- /dev/null +++ b/tests/generic_gpio_utest.h @@ -0,0 +1,35 @@ +/* generic_gpio_utest.h - unit tests for gpiod based NUT driver definitions + * for GPIO attached UPS devices + * + * Copyright (C) + * 2023 Modris Berzonis + * 2023 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef GENERIC_GPIO_UTEST_H +#define GENERIC_GPIO_UTEST_H + +#include "generic_gpio_common.h" +#include "generic_gpio_libgpiod.h" + +void setNextLinesReadToFail(void); +void getWithoutUnderscores(char *var); +int get_test_status(struct gpioups_t *result, int on_fail_path); + +#endif /* GENERIC_GPIO_UTEST_H */ diff --git a/tests/getvaluetest.c b/tests/getvaluetest.c index 0723969f00..0d2c445ad6 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 @@ -28,12 +29,12 @@ #include "config.h" -#include -#include +#include "nut_stdint.h" #include #include #include #include "hidtypes.h" +#include "usb-common.h" #include "common.h" void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue); @@ -64,8 +65,6 @@ static void PrintBufAndData(uint8_t *buf, size_t bufSize, HIDData_t *pData) { } static int RunBuiltInTests(char *argv[]) { - NUT_UNUSED_VARIABLE(argv); - int exitStatus = 0; size_t i; char *next; @@ -103,7 +102,15 @@ static int RunBuiltInTests(char *argv[]) { {.buf = "16 0c 00 00 00", .Offset = 10, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0} }; - for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++) { + /* See comments below about rdlen calculation emulation for tests */ + usb_ctrl_char bufC[2]; + signed char bufS[2]; + unsigned char bufU[2]; + int rdlen; + + NUT_UNUSED_VARIABLE(argv); + + for (i = 0; i < SIZEOF_ARRAY(testData); i++) { next = testData[i].buf; for (bufSize = 0; *next != 0; bufSize++) { reportBuf[bufSize] = (uint8_t) strtol(next, (char **)&next, 16); @@ -116,7 +123,7 @@ static int RunBuiltInTests(char *argv[]) { GetValue(reportBuf, &data, &value); - printf("Test #%zd ", i + 1); + printf("Test #%" PRIiSIZE " ", i + 1); PrintBufAndData(reportBuf, bufSize, &data); if (value == testData[i].expectedValue) { printf(" value %ld PASS\n", value); @@ -125,6 +132,102 @@ 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. + */ + + /* 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/tests/nutconf.cpp b/tests/nutconf_parser_ut.cpp similarity index 92% rename from tests/nutconf.cpp rename to tests/nutconf_parser_ut.cpp index 3af0cccfa3..f429b28cc9 100644 --- a/tests/nutconf.cpp +++ b/tests/nutconf_parser_ut.cpp @@ -1,28 +1,32 @@ -/* example - CppUnit unit test example +/* + tests/nutconf.cpp - based on CppUnit unit test example - Copyright (C) + Copyright (C) 2012 Emilien Kia - 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 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "config.h" + #include -// Define to desactivate protection of parsing tool members: +// Define to de-activate protection of parsing tool members: #define UNITEST_MODE 1 -#include "nutconf.h" +#include "nutconf.hpp" using namespace nut; #include @@ -44,8 +48,8 @@ class NutConfTest : public CppUnit::TestFixture CPPUNIT_TEST_SUITE_END(); public: - void setUp(); - void tearDown(); + void setUp() override; + void tearDown() override; void testOptions(); void testParseCHARS(); @@ -82,7 +86,7 @@ void NutConfTest::testOptions() { NutParser parse("Bonjour monde!", NutParser::OPTION_IGNORE_COLON); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Has bad parsing options", (unsigned int)NutParser::OPTION_IGNORE_COLON, parse.getOptions()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Has bad parsing options", static_cast(NutParser::OPTION_IGNORE_COLON), parse.getOptions()); CPPUNIT_ASSERT_MESSAGE("Has not OPTION_IGNORE_COLON parsing option", parse.hasOptions(NutParser::OPTION_IGNORE_COLON)); } @@ -282,7 +286,7 @@ void NutConfTest::testUpsmonConfigParser() CPPUNIT_ASSERT_MESSAGE("Find a NOTIFYFLAG ONLINE", !conf.notifyFlags[nut::UpsmonConfiguration::NOTIFY_ONLINE].set()); CPPUNIT_ASSERT_MESSAGE("Cannot find a NOTIFYFLAG LOWBATT", conf.notifyFlags[nut::UpsmonConfiguration::NOTIFY_LOWBATT].set()); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Cannot find a NOTIFYFLAG LOWBATT SYSLOG+WALL", 3u, (unsigned int)conf.notifyFlags[nut::UpsmonConfiguration::NOTIFY_LOWBATT]); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Cannot find a NOTIFYFLAG LOWBATT SYSLOG+WALL", 3u, static_cast(conf.notifyFlags[nut::UpsmonConfiguration::NOTIFY_LOWBATT])); CPPUNIT_ASSERT_MESSAGE("Find a NOTIFYMSG LOWBATT", !conf.notifyMessages[nut::UpsmonConfiguration::NOTIFY_LOWBATT].set()); @@ -301,7 +305,6 @@ void NutConfTest::testNutConfConfigParser() CPPUNIT_ASSERT_MESSAGE("Cannot find a MODE", conf.mode.set()); CPPUNIT_ASSERT_EQUAL_MESSAGE("Cannot find a MODE=standalone", nut::NutConfiguration::MODE_STANDALONE, *conf.mode); - } void NutConfTest::testUpsdConfigParser() @@ -340,7 +343,3 @@ void NutConfTest::testUpsdConfigParser() } } - - - - diff --git a/tests/nutconf_ut.cpp b/tests/nutconf_ut.cpp index 3fc4b2fcdf..ba8c8947d1 100644 --- a/tests/nutconf_ut.cpp +++ b/tests/nutconf_ut.cpp @@ -1,32 +1,32 @@ /* -NUT configuration unit test + NUT configuration unit test -Copyright (C) -2012 Vaclav Krpec + Copyright (C) + 2012 Vaclav Krpec -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "nutstream.hpp" -#include "nutconf.h" +#include "nutconf.hpp" #include "nutwriter.hpp" #include - /** * \brief NUT configuration unit test */ @@ -34,7 +34,11 @@ class NutConfigUnitTest: public CppUnit::TestFixture { private: CPPUNIT_TEST_SUITE(NutConfigUnitTest); - CPPUNIT_TEST(test); + CPPUNIT_TEST( testNutConfiguration ); + CPPUNIT_TEST( testUpsmonConfiguration ); + CPPUNIT_TEST( testUpsdConfiguration ); + CPPUNIT_TEST( testUpsConfiguration ); + CPPUNIT_TEST( testUpsdUsersConfiguration ); CPPUNIT_TEST_SUITE_END(); /** @@ -46,13 +50,15 @@ class NutConfigUnitTest: public CppUnit::TestFixture { void load(nut::Serialisable * config, const std::string & file_name); /** - * \brief Check configuration serialisation contents + * \brief Check configuration serialization contents * * \param config Configuration object - * \param content Expected serialisation + * \param content Expected serialization */ void check(const nut::Serialisable * config, const std::string & content); + public: + /** nut.conf test */ void testNutConfiguration(); @@ -68,22 +74,12 @@ class NutConfigUnitTest: public CppUnit::TestFixture { /** upsd.users test */ void testUpsdUsersConfiguration(); - public: - - inline void setUp() {} - inline void tearDown() {} - - inline void test() { - testNutConfiguration(); - testUpsmonConfiguration(); - testUpsdConfiguration(); - testUpsConfiguration(); - testUpsdUsersConfiguration(); - } + inline void setUp() override {} + inline void tearDown() override {} + virtual ~NutConfigUnitTest() override; }; // end of class NutConfigUnitTest - // Register the test suite CPPUNIT_TEST_SUITE_REGISTRATION(NutConfigUnitTest); @@ -108,9 +104,9 @@ void NutConfigUnitTest::check(const nut::Serialisable * config, const std::strin if (content != str) { std::cerr << "--- expected ---" << std::endl << content << "--- end ---" << std::endl; - std::cerr << "--- serialised ---" << std::endl << str << "--- end ---" << std::endl; + std::cerr << "--- serialized ---" << std::endl << str << "--- end ---" << std::endl; - CPPUNIT_ASSERT_MESSAGE("Configuration serialisation check failed", 0); + CPPUNIT_ASSERT_MESSAGE("Configuration serialization check failed", 0); } } @@ -118,7 +114,7 @@ void NutConfigUnitTest::check(const nut::Serialisable * config, const std::strin void NutConfigUnitTest::testNutConfiguration() { nut::NutConfiguration config; - load(static_cast(&config), TOP_SRCDIR "/conf/nut.conf.sample"); + load(static_cast(&config), ABS_TOP_SRCDIR "/conf/nut.conf.sample"); config.mode = nut::NutConfiguration::MODE_STANDALONE; @@ -131,7 +127,8 @@ void NutConfigUnitTest::testNutConfiguration() { void NutConfigUnitTest::testUpsmonConfiguration() { nut::UpsmonConfiguration config; - load(static_cast(&config), TOP_SRCDIR "/conf/upsmon.conf.sample"); + // Note: this file gets generated from a .in template + load(static_cast(&config), ABS_TOP_BUILDDIR "/conf/upsmon.conf.sample"); config.shutdownCmd = "/sbin/shutdown -h +2 'System shutdown in 2 minutes!'"; config.powerDownFlag = "/run/nut/killpower"; @@ -156,7 +153,7 @@ void NutConfigUnitTest::testUpsmonConfiguration() { void NutConfigUnitTest::testUpsdConfiguration() { nut::UpsdConfiguration config; - load(static_cast(&config), TOP_SRCDIR "/conf/upsd.conf.sample"); + load(static_cast(&config), ABS_TOP_SRCDIR "/conf/upsd.conf.sample"); config.maxAge = 15; config.statePath = "/var/run/nut"; @@ -188,7 +185,7 @@ void NutConfigUnitTest::testUpsdConfiguration() { void NutConfigUnitTest::testUpsConfiguration() { nut::UpsConfiguration config; - load(static_cast(&config), TOP_SRCDIR "/conf/ups.conf.sample"); + load(static_cast(&config), ABS_TOP_SRCDIR "/conf/ups.conf.sample"); static const std::string my_ups("powerpal"); @@ -196,7 +193,9 @@ void NutConfigUnitTest::testUpsConfiguration() { config.setPort(my_ups, "/dev/ttyS0"); config.setDescription(my_ups, "Web server"); + // Note: "maxretry = 3" comes from current ups.conf.sample non-comment lines check(static_cast(&config), + "maxretry = 3\n\n" "[powerpal]\n" "\tdesc = \"Web server\"\n" "\tdriver = blazer_ser\n" @@ -209,7 +208,7 @@ void NutConfigUnitTest::testUpsConfiguration() { void NutConfigUnitTest::testUpsdUsersConfiguration() { nut::UpsdUsersConfiguration config; - load(static_cast(&config), TOP_SRCDIR "/conf/upsd.users.sample"); + load(static_cast(&config), ABS_TOP_SRCDIR "/conf/upsd.users.sample"); config.setPassword("upsmon", "ytrewq"); config.setUpsmonMode(nut::UpsdUsersConfiguration::UPSMON_MASTER); @@ -230,3 +229,9 @@ void NutConfigUnitTest::testUpsdUsersConfiguration() { "\n" ); } + +// Implement out of class declaration to avoid +// error: 'SomeClass' has no out-of-line virtual method +// definitions; its vtable will be emitted in every translation unit +// [-Werror,-Wweak-vtables] +NutConfigUnitTest::~NutConfigUnitTest() {} diff --git a/tests/nutipc_ut.cpp b/tests/nutipc_ut.cpp index eb3a83fec0..9c49f0f0a7 100644 --- a/tests/nutipc_ut.cpp +++ b/tests/nutipc_ut.cpp @@ -1,29 +1,29 @@ /* -NUT IPC unit test + NUT IPC unit test -Copyright (C) 2012 + Copyright (C) 2012 -\author Vaclav Krpec + \author Vaclav Krpec -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "nutipc.hpp" #include "nutstream.hpp" -#include "config.h" #include @@ -33,6 +33,8 @@ extern "C" { #include #include #include + +extern bool verbose; } @@ -43,12 +45,12 @@ class NutIPCUnitTest: public CppUnit::TestFixture { private: CPPUNIT_TEST_SUITE(NutIPCUnitTest); - CPPUNIT_TEST(test); + CPPUNIT_TEST( testExec ); + CPPUNIT_TEST( testSignalSend ); + CPPUNIT_TEST( testSignalRecvQuick ); + CPPUNIT_TEST( testSignalRecvStaggered ); CPPUNIT_TEST_SUITE_END(); - /** External command execution test */ - void testExec(); - /** * \brief Test signal handler * @@ -56,31 +58,34 @@ class NutIPCUnitTest: public CppUnit::TestFixture { */ static void testSignalHandler(int signal); + public: + + /** External command execution test */ + void testExec(); + /** Signal sending test */ void testSignalSend(); /** Signal receiving test */ - void testSignalRecv(); + void testSignalRecvQuick(); + void testSignalRecvStaggered(); - public: - - inline void setUp() {} - inline void tearDown() {} - - inline void test() { - testExec(); - testSignalSend(); - testSignalRecv(); - } + inline void setUp() override {} + inline void tearDown() override {} + virtual ~NutIPCUnitTest() override; }; // end of class NutIPCUnitTest - // Register the test suite CPPUNIT_TEST_SUITE_REGISTRATION(NutIPCUnitTest); void NutIPCUnitTest::testExec() { +#ifdef WIN32 + /* FIXME: Some other program, maybe NUT's "message" handler, or "cmd -k" etc.? + * And get Process working in the first place */ + std::cout << "NutIPCUnitTest::testExec(): skipped on this platform" << std::endl; +#else static const std::string bin = "/bin/sh"; nut::Process::Executor::Arguments args; @@ -92,7 +97,8 @@ void NutIPCUnitTest::testExec() { CPPUNIT_ASSERT(123 == child.wait()); - CPPUNIT_ASSERT(0 == nut::Process::execute("test 'Hello world' == 'Hello world'")); + CPPUNIT_ASSERT(0 == nut::Process::execute("test 'Hello world' = 'Hello world'")); +#endif /* WIN32 */ } @@ -104,32 +110,52 @@ void NutIPCUnitTest::testSignalHandler(int signal) { } void NutIPCUnitTest::testSignalSend() { +#ifdef WIN32 + /* FIXME: Needs implementation for signals via pipes */ + std::cout << "NutIPCUnitTest::testSignalSend(): skipped on this platform" << std::endl; +#else struct sigaction action; pid_t my_pid = nut::Process::getPID(); // Set SIGUSR1 signal handler ::memset(&action, 0, sizeof(action)); +# ifdef sigemptyset + // no :: here because macro + sigemptyset(&action.sa_mask); +# else ::sigemptyset(&action.sa_mask); +# endif action.sa_handler = &testSignalHandler; - CPPUNIT_ASSERT(0 == ::sigaction((int)nut::Signal::USER1, &action, NULL)); + CPPUNIT_ASSERT(0 == ::sigaction(static_cast(nut::Signal::USER1), &action, nullptr)); // Send signal directly CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); - CPPUNIT_ASSERT((int)nut::Signal::USER1 == signal_caught); + CPPUNIT_ASSERT(static_cast(nut::Signal::USER1) == signal_caught); signal_caught = 0; - // Save PID to a PIDfile - static const std::string pid_file_name("/tmp/foobar.pid"); - std::stringstream my_pid_ss; + // Save PID to a PIDfile; use an unique filename as much as we can + // (avoid conflicts in parallel running tests on NUT CI farm, etc.) + my_pid_ss << nut::NutFile::tmp_dir() << nut::NutFile::path_sep() + << "nutipc_ut_" << my_pid << ".pid"; + static const std::string pid_file_name(my_pid_ss.str()); + + my_pid_ss.str(""); + my_pid_ss.clear(); my_pid_ss << my_pid; + if (verbose) + std::cerr << "NutIPCUnitTest::testSignalSend(): using PID file '" + << pid_file_name << "' for PID " << my_pid + << " to store string '" << my_pid_ss.str() << "'" + << std::endl << std::flush; + nut::NutFile pid_file(pid_file_name, nut::NutFile::WRITE_ONLY); pid_file.putString(my_pid_ss.str()); @@ -139,11 +165,12 @@ void NutIPCUnitTest::testSignalSend() { // Send signal to process via the PIDfile CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, pid_file_name)); - CPPUNIT_ASSERT((int)nut::Signal::USER1 == signal_caught); + CPPUNIT_ASSERT(static_cast(nut::Signal::USER1) == signal_caught); pid_file.removex(); signal_caught = 0; +#endif /* WIN32 */ } @@ -154,15 +181,21 @@ static nut::Signal::List caught_signals; class TestSignalHandler: public nut::Signal::Handler { public: - void operator () (nut::Signal::enum_t signal) { + void operator () (nut::Signal::enum_t signal) override { caught_signals.push_back(signal); } + virtual ~TestSignalHandler() override; }; // end of class TestSignalHandler -void NutIPCUnitTest::testSignalRecv() { +void NutIPCUnitTest::testSignalRecvQuick() { +#ifdef WIN32 + /* FIXME: Needs implementation for signals via pipes */ + std::cout << "NutIPCUnitTest::testSignalRecvQuick(): skipped on this platform" << std::endl; +#else // Create signal handler thread nut::Signal::List signals; + caught_signals.clear(); signals.push_back(nut::Signal::USER1); signals.push_back(nut::Signal::USER2); @@ -171,6 +204,13 @@ void NutIPCUnitTest::testSignalRecv() { pid_t my_pid = nut::Process::getPID(); + /* NOTE: The signal order delivery is not specified by POSIX if several + * ones arrive nearly simultaneously (and/or get confused by multi-CPU + * routing). In this test we only verify that after sending several copies + * of several signals, the expected counts of events were received. + */ + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER2, my_pid)); CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER2, my_pid)); CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); @@ -178,15 +218,90 @@ void NutIPCUnitTest::testSignalRecv() { // Let the sig. handler thread finish... ::sleep(1); - CPPUNIT_ASSERT(caught_signals.size() == 3); + CPPUNIT_ASSERT(caught_signals.size() == 5); + + int countUSER1 = 0; + int countUSER2 = 0; + while (!caught_signals.empty()) { + nut::Signal::enum_t signal = caught_signals.front(); + caught_signals.pop_front(); + + if (signal == nut::Signal::USER1) { + countUSER1++; + } else if (signal == nut::Signal::USER2) { + countUSER2++; + } else { + std::stringstream msg; + msg << "Unexpected signal was received: " << signal; + CPPUNIT_ASSERT_MESSAGE(msg.str(), 0); + } + } + + CPPUNIT_ASSERT(countUSER1 == 3); + CPPUNIT_ASSERT(countUSER2 == 2); +#endif /* WIN32 */ +} + +void NutIPCUnitTest::testSignalRecvStaggered() { +#ifdef WIN32 + /* FIXME: Needs implementation for signals via pipes */ + std::cout << "NutIPCUnitTest::testSignalRecvStaggered(): skipped on this platform" << std::endl; +#else + // Create signal handler thread + nut::Signal::List signals; + caught_signals.clear(); + + signals.push_back(nut::Signal::USER1); + signals.push_back(nut::Signal::USER2); + + nut::Signal::HandlerThread sig_handler(signals); + + pid_t my_pid = nut::Process::getPID(); + + /* NOTE: The signal order delivery is not specified by POSIX if several + * ones arrive nearly simultaneously (and/or get confused by multi-CPU + * routing). Linux tends to deliver lower-numbered signals first, so we + * expect USER1 (10) before USER2 (12) to be consistent. Otherwise CI + * builds tend to mess this up a bit. + */ + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); + ::sleep(1); + + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER2, my_pid)); + ::sleep(1); + + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER2, my_pid)); + ::sleep(1); + + /* Help ensure ordered (one-by-one) delivery before re-posting a + * presumably lower-numbered signal after some higher-numbered ones. + */ + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::USER1, my_pid)); + + // Let the sig. handler thread finish... + ::sleep(1); + + CPPUNIT_ASSERT(caught_signals.size() == 4); + + CPPUNIT_ASSERT(caught_signals.front() == nut::Signal::USER1); + + caught_signals.pop_front(); CPPUNIT_ASSERT(caught_signals.front() == nut::Signal::USER2); caught_signals.pop_front(); - CPPUNIT_ASSERT(caught_signals.front() == nut::Signal::USER1); + CPPUNIT_ASSERT(caught_signals.front() == nut::Signal::USER2); caught_signals.pop_front(); CPPUNIT_ASSERT(caught_signals.front() == nut::Signal::USER1); +#endif /* WIN32 */ } + +// Implement out of class declaration to avoid +// error: 'SomeClass' has no out-of-line virtual method +// definitions; its vtable will be emitted in every translation unit +// [-Werror,-Wweak-vtables] +TestSignalHandler::~TestSignalHandler() {} +NutIPCUnitTest::~NutIPCUnitTest() {} diff --git a/tests/nutlogtest.c b/tests/nutlogtest.c index 09342c76b4..b8b51dcf77 100644 --- a/tests/nutlogtest.c +++ b/tests/nutlogtest.c @@ -3,7 +3,7 @@ * do not crash). * * Copyright (C) - * 2020 Jim Klimov + * 2020-2023 Jim Klimov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "config.h" #include "common.h" int main(void) { @@ -29,7 +31,9 @@ int main(void) { # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wformat-overflow" #endif - upsdebugx(0, "D: checking with libc handling of NULL: '%s' vs '%s'", s1, s2); + + upsdebugx(0, "D: checking with libc handling of NULL (can segfault for some libc implementations):"); + upsdebugx(0, "D: '%s' vs '%s'", s1, s2); /* This explicitly does not work with -Wformat, due to verbatim NULL without a var: * nutlogtest.c:20:5: error: reading through null pointer (argument 4) [-Werror=format=] diff --git a/tests/nutstream_ut.cpp b/tests/nutstream_ut.cpp index 11d1d374a1..918daa0c95 100644 --- a/tests/nutstream_ut.cpp +++ b/tests/nutstream_ut.cpp @@ -1,41 +1,70 @@ /* -NUT stream unit test + NUT stream unit test -Copyright (C) -2012 Vaclav Krpec + Copyright (C) + 2012 Vaclav Krpec -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + 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. + 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 + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "nutstream.hpp" - -#include +#include "nutipc.hpp" /* Used in a test to "freeze" a writer child process */ #include #include #include extern "C" { -#include +#ifndef WIN32 +# include +# include +#else +# if !(defined random) && !(defined HAVE_RANDOM) + /* WIN32 names it differently: */ +# define random() rand() +# endif +#endif /* WIN32 */ #include #include -#include #include + +extern bool verbose; } +/* 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 || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_DEPRECATED_DECLARATIONS) +#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 +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_DEPRECATED_DECLARATIONS +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif +#endif +#ifdef __clang__ +# pragma clang diagnostic push "-Wdeprecated-declarations" +#endif + +#include + +namespace nut { /** Test data */ static const std::string test_data( @@ -60,7 +89,7 @@ static const std::string test_data( * \retval false in case of failure */ static bool readTestData(nut::NutStream * stream) { - assert(NULL != stream); + assert(nullptr != stream); // Read characters from the stream for (size_t pos = 0, iter = 0; ; ++iter) { @@ -68,17 +97,28 @@ static bool readTestData(nut::NutStream * stream) { nut::NutStream::status_t status = stream->getChar(ch); - if (nut::NutStream::NUTS_ERROR == status) + if (nut::NutStream::NUTS_ERROR == status) { + if (verbose) + std::cerr << "readTestData(): status==nut::NutStream::NUTS_ERROR" << std::endl; return false; + } if (nut::NutStream::NUTS_EOF == status) break; - if (nut::NutStream::NUTS_OK != status) + if (nut::NutStream::NUTS_OK != status) { + if (verbose) + std::cerr << "readTestData(): status!=nut::NutStream::NUTS_OK: " << status << std::endl; return false; + } - if (ch != test_data.at(pos)) + if (ch != test_data.at(pos)) { + if (verbose) + std::cerr << "readTestData(): unexpected char '" + << ch << "' at pos " << pos << ": want '" + << test_data.at(pos) << "'" << std::endl; return false; + } // Every other character shall be checked twice if (0 == iter % 8) @@ -103,9 +143,9 @@ static bool readTestData(nut::NutStream * stream) { * \retval false in case of failure */ static bool writeTestData(nut::NutStream * stream) { - assert(NULL != stream); + assert(nullptr != stream); - size_t pivot = 0.5 * test_data.size(); + size_t pivot = static_cast(0.5 * static_cast(test_data.size())); // Write characters to the stream for (size_t i = 0; i < pivot; ++i) { @@ -113,8 +153,11 @@ static bool writeTestData(nut::NutStream * stream) { nut::NutStream::status_t status = stream->putChar(ch); - if (nut::NutStream::NUTS_OK != status) + if (nut::NutStream::NUTS_OK != status) { + if (verbose) + std::cerr << "writeTestData(): status!=nut::NutStream::NUTS_OK: " << status << std::endl; return false; + } } // Write string to the stream @@ -156,6 +199,7 @@ class NutStreamUnitTest: public CppUnit::TestFixture { CPPUNIT_ASSERT(writeTestData(stream)); } + virtual ~NutStreamUnitTest() override; }; // end of class NutStreamUnitTest @@ -171,10 +215,10 @@ class NutMemoryUnitTest: public NutStreamUnitTest { public: - inline void setUp() {} - inline void tearDown() {} + inline void setUp() override {} + inline void tearDown() override {} - void test(); + virtual void test(); }; // end of class NutMemoryUnitTest @@ -201,10 +245,10 @@ class NutFileUnitTest: public NutStreamUnitTest { public: - inline void setUp() {} - inline void tearDown() {} + inline void setUp() override {} + inline void tearDown() override {} - void test(); + virtual void test(); }; // end of class NutFileUnitTest @@ -213,6 +257,7 @@ void NutFileUnitTest::test() { nut::NutFile fstream(nut::NutFile::ANONYMOUS); writex(&fstream); + fstream.flushx(); readx(&fstream); } @@ -263,16 +308,71 @@ class NutSocketUnitTest: public NutStreamUnitTest { public: - inline void setUp() {}; - inline void tearDown() {} + inline void setUp() override {} + inline void tearDown() override {} - void test(); + virtual void test(); }; // end of class NutSocketUnitTest -const nut::NutSocket::Address NutSocketUnitTest::m_listen_address(127, 0, 0, 1, 10000); +/* Static initializer below may run before methods of the test, + * so it tends to repeat the same port for parallel CI runs */ +static long reallyRandom() { + ::srand(static_cast(::time(nullptr))); + return ::random(); +} + +/* Randomize to try avoiding collisions in parallel testing */ +static uint16_t getFreePort() { + int tries = 100; +#ifdef WIN32 + WSADATA wsaData; + static int wsaStarted = 0; + if (!wsaStarted) { + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "WIN32: Failed to WSAStartup() the socket layer" << std::endl << std::flush; + } + // well, at least attempted + wsaStarted = 1; + } +#endif + while (tries > 0) { + uint16_t port = 10000 + static_cast(reallyRandom() % 40000); + nut::NutSocket::Address addr(127, 0, 0, 1, port); + nut::NutSocket sock; + int ec; + std::string em; + + if (sock.bind(addr, ec, em)) { + /* FWIW, "verbose" is only set in main() and this method currently + * is part of static initialization before that. So no trace. + */ + if (verbose) + std::cerr << "getFreePort() could bind() port " << port + << "; is FD valid?=" << sock.valid() << std::endl; + /* Let the destructor close it */ + sock.closex(); + return port; + } + + if (verbose) + std::cerr << "getFreePort() failed to bind() port " << port + << ": code " << ec << " aka " << em << ": will try another" + << std::endl; + sock.closex(); + tries--; + } + + // Well, gotta try something... + if (verbose) + std::cerr << "getFreePort() failed to bind(), falling back to 10000" << std::endl; + return 10000; +} +const nut::NutSocket::Address NutSocketUnitTest::m_listen_address( + 127, 0, 0, 1, + getFreePort()); bool NutSocketUnitTest::Writer::run() { nut::NutSocket conn_sock; @@ -291,6 +391,10 @@ bool NutSocketUnitTest::Writer::run() { void NutSocketUnitTest::test() { +#ifdef WIN32 + /* FIXME: get Process working in the first place */ + std::cout << "NutSocketUnitTest::test(): skipped on this platform" << std::endl; +#else // Fork writer pid_t writer_pid = ::fork(); @@ -301,13 +405,33 @@ void NutSocketUnitTest::test() { // Run writer CPPUNIT_ASSERT(Writer(m_listen_address).run()); - return; + exit(0); } + // Freeze the writer until we bind the port + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::STOP, writer_pid)); + // Listen nut::NutSocket listen_sock; - CPPUNIT_ASSERT(listen_sock.bind(m_listen_address)); + std::stringstream msg_bind; + msg_bind << "Expected to listen on " << m_listen_address.str(); + bool bound = listen_sock.bind(m_listen_address); + int retries = 5; + + while (!bound && retries > 0) { + retries--; + if (verbose) + std::cerr << msg_bind.str() << ": will retry test in 15 sec (" + << retries << " retries remaining)" << std::endl; + sleep(15); + bound = listen_sock.bind(m_listen_address); + } + + // Un-freeze the writer as we have bound the port (or will fail next line) + CPPUNIT_ASSERT(0 == nut::Signal::send(nut::Signal::CONT, writer_pid)); + + CPPUNIT_ASSERT_MESSAGE(msg_bind.str(), bound); CPPUNIT_ASSERT(listen_sock.listen(10)); // Accept connection @@ -321,7 +445,11 @@ void NutSocketUnitTest::test() { pid_t wpid = ::waitpid(writer_pid, &writer_exit, 0); CPPUNIT_ASSERT(wpid == writer_pid); - CPPUNIT_ASSERT(0 == writer_exit); + + std::stringstream msg_writer_exit; + msg_writer_exit << "Got writer_exit=" << writer_exit << ", expected 0"; + CPPUNIT_ASSERT_MESSAGE(msg_writer_exit.str(), 0 == writer_exit); +#endif /* WIN32 */ } @@ -329,3 +457,18 @@ void NutSocketUnitTest::test() { CPPUNIT_TEST_SUITE_REGISTRATION(NutMemoryUnitTest); CPPUNIT_TEST_SUITE_REGISTRATION(NutFileUnitTest); CPPUNIT_TEST_SUITE_REGISTRATION(NutSocketUnitTest); + +// Implement out of class declaration to avoid +// error: 'SomeClass' has no out-of-line virtual method +// definitions; its vtable will be emitted in every translation unit +// [-Werror,-Wweak-vtables] +NutStreamUnitTest::~NutStreamUnitTest() {} + +} // namespace nut {} + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#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 || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_DEPRECATED_DECLARATIONS) +# pragma GCC diagnostic pop +#endif diff --git a/tests/nuttimetest.c b/tests/nuttimetest.c new file mode 100644 index 0000000000..2ef2444d11 --- /dev/null +++ b/tests/nuttimetest.c @@ -0,0 +1,154 @@ +/* nuttimetest.c - test custom NUT routines for time comparison and manipulation + * + * Copyright (C) + * 2023 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "common.h" +#include "nut_stdint.h" +#include "nut_float.h" + +#include +#include + +static int check_difftime(void) +{ + time_t tv1, tv2; + double d1, d2; + int res = 0; + + printf("=== %s(time_t):\t", __func__); + + /* Often time_t is a long int, maybe signed */ + tv1 = 5; + printf(" tv1=%" PRIiMAX, (intmax_t)tv1); + + tv2 = 8; + printf(" tv2=%" PRIiMAX, (intmax_t)tv2); + + /* like in difftime(): (double)seconds elapsed time between older tv2 and newer tv1 */ + d1 = difftime(tv1, tv2); + if (!d_equal(d1, -3)) + res++; + printf(" => diff1(tv1, tv2)=%f (%s)", d1, d_equal(d1, -3) ? "OK" : "FAIL"); + + d2 = difftime(tv2, tv1); + if (!d_equal(d2, 3)) + res++; + printf(" => diff2(tv2, tv1)=%f (%s)", d2, d_equal(d2, 3) ? "OK" : "FAIL"); + + if (!d_equal(d1, -d2)) { + printf(" => diff1 != -diff2 (FAIL)\n"); + res++; + } else { + printf(" => diff1 == -diff2 (OK)\n"); + } + + return res; +} + +static int check_difftimeval(void) +{ + struct timeval tv1, tv2; + double d1, d2; + int res = 0; + + printf("=== %s(struct timeval):\t", __func__); + + tv1.tv_sec = 5; + tv1.tv_usec = 900000; + printf(" tv1=%" PRIiMAX ".%06" PRIiMAX, (intmax_t)tv1.tv_sec, (intmax_t)tv1.tv_usec); + + tv2.tv_sec = 8; + tv2.tv_usec = 230000; + printf(" tv2=%" PRIiMAX ".%06" PRIiMAX, (intmax_t)tv2.tv_sec, (intmax_t)tv2.tv_usec); + + /* like in difftime(): (double)seconds elapsed time between older tv2 and newer tv1 */ + d1 = difftimeval(tv1, tv2); + if (!d_equal(d1, -2.33)) + res++; + printf(" => diff1(tv1, tv2)=%f (%s)", d1, d_equal(d1, -2.33) ? "OK" : "FAIL"); + + d2 = difftimeval(tv2, tv1); + if (!d_equal(d2, 2.33)) + res++; + printf(" => diff2(tv2, tv1)=%f (%s)", d2, d_equal(d2, 2.33) ? "OK" : "FAIL"); + + if (!d_equal(d1, -d2)) { + printf(" => diff1 != -diff2 (FAIL)\n"); + res++; + } else { + printf(" => diff1 == -diff2 (OK)\n"); + } + + return res; +} + +static int check_difftimespec(void) +{ + int res = 0; +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) && HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC + struct timespec tv1, tv2; + double d1, d2; + + printf("=== %s(struct timespec):\t", __func__); + + tv1.tv_sec = 5; + tv1.tv_nsec = 900000000; + printf(" tv1=%" PRIiMAX ".%09" PRIiMAX, (intmax_t)tv1.tv_sec, (intmax_t)tv1.tv_nsec); + + tv2.tv_sec = 8; + tv2.tv_nsec = 230000; + printf(" tv2=%" PRIiMAX ".%09" PRIiMAX, (intmax_t)tv2.tv_sec, (intmax_t)tv2.tv_nsec); + + /* like in difftime(): (double)seconds elapsed time between older tv2 and newer tv1 */ + d1 = difftimespec(tv1, tv2); + if (!d_equal(d1, -2.10023)) + res++; + printf(" => diff1(tv1, tv2)=%f (%s)", d1, d_equal(d1, -2.10023) ? "OK" : "FAIL"); + + d2 = difftimespec(tv2, tv1); + if (!d_equal(d2, 2.10023)) + res++; + printf(" => diff2(tv2, tv1)=%f (%s)", d2, d_equal(d2, 2.10023) ? "OK" : "FAIL"); + + if (!d_equal(d1, -d2)) { + printf(" => diff1 != -diff2 (FAIL)\n"); + res++; + } else { + printf(" => diff1 == -diff2 (OK)\n"); + } +#else + printf("=== %s(struct timespec):\tSKIP: NOT IMPLEMENTED for this build (not HAVE_CLOCK_GETTIME or not HAVE_CLOCK_MONOTONIC)\n", __func__); +#endif + + return res; +} + +int main(void) +{ + int ret = 0; + + ret += check_difftime(); + ret += check_difftimeval(); + ret += check_difftimespec(); + + return (ret != 0); +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 8867f259fc..350cc7f008 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,3 +1,14 @@ +# Network UPS Tools: generators and other tools + +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + # SUBDIRS are explicitly a listing of all the directories that make # must recurse into BEFORE processing the current directory. # @@ -13,26 +24,42 @@ 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: -#GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.h +# These files are generated for nut-scanner builds (and cleaned as any others), +# and can change as respective SNMP/USB subdriver sources are iterated by a +# locally active developer. +### DMF recipe modification: we generate C not H here for SNMP: +###GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.h GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.c GENERATED_USB_FILES = nut-scanner/nutscan-usb.h +# These files for OS integration are generated by `autogen.sh` and distributed +# by a release tarball (and not normally cleaned away). While technically their +# contents can change as respective USB subdriver sources are iterated by a +# locally active developer (especially changing supported VID:PID combinations), +# this is not a requirement that would block local re-builds nor a frequent +# use case. So they are not normally rebuilt intentionally (could be anyway, +# as part of nut-scanner dependency re-generations), and for install/release +# after such fundamental changes the project should better run `autogen.sh`. + # Hotplug output file -GENERATED_USB_OS_FILES = ../scripts/hotplug/libhid.usermap +GENERATED_USB_OS_FILES = $(top_builddir)/scripts/hotplug/libhid.usermap # udev output file -GENERATED_USB_OS_FILES += ../scripts/udev/nut-usbups.rules.in +GENERATED_USB_OS_FILES += $(top_builddir)/scripts/udev/nut-usbups.rules.in # BSD devd output file -GENERATED_USB_OS_FILES += ../scripts/devd/nut-usb.conf.in +GENERATED_USB_OS_FILES += $(top_builddir)/scripts/devd/nut-usb.conf.in + +# FreeBSD quirks output file +GENERATED_USB_OS_FILES += $(top_builddir)/scripts/devd/nut-usb.quirks # UPower output file -GENERATED_USB_OS_FILES += ../scripts/upower/95-upower-hid.rules +# Note: unlike others above, this one is currently tracked in Git +GENERATED_USB_OS_FILES += $(top_builddir)/scripts/upower/95-upower-hid.hwdb CLEANFILES = $(GENERATED_SNMP_FILES) $(GENERATED_USB_FILES) # We do not clean away these files, some are even tracked in Git: @@ -49,8 +76,8 @@ nut-scanner-deps-usb: $(GENERATED_USB_FILES) # The distributed nut-snmpinfo.py.in template is assumed to only differ from # a generated nut-snmpinfo.py by the @PYTHON@ shebang. -$(GENERATED_SNMP_FILES): $(top_srcdir)/drivers/*-mib.c - @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ +$(GENERATED_SNMP_FILES): $(top_srcdir)/drivers/*-mib.c $(top_srcdir)/tools/nut-snmpinfo.py.in + @if [ -n "$(PYTHON)" ] && [ x"no" != x"$(PYTHON)" ] && $(PYTHON) -c 1; then \ echo "Regenerating the SNMP helper files in SRC dir with '$(PYTHON)'."; \ TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ @@ -62,7 +89,35 @@ $(GENERATED_SNMP_FILES): $(top_srcdir)/drivers/*-mib.c echo "----------------------------------------------------------------------"; \ fi -$(GENERATED_USB_FILES): $(top_srcdir)/drivers/*-hid.c $(top_srcdir)/drivers/*usb*.c $(top_srcdir)/drivers/nutdrv_qx.c +# Currently GENERATED_USB_FILES refers to a single file, which ensures +# that the perl script (if needed) runs once even in parallel builds. +# The GENERATED_USB_OS_FILES should not *have* to be built by make at +# all, but just in case (developer iterations) a dependency is here. +# It is expected that the files exist (from tarball or latest run of +# script for GENERATED_USB_FILES purposes), so we only confirm that +# and bump their timestamps to avoid more noise. Note that for some +# builds from scratch (with explicit envvars passed to autogen.sh) +# these files may be empty to satisfy dependencies and forfeit the +# features for a particular build. +# To facilitate "make distcheck" and out-of-tree builds, we try to +# use files pre-generated in SRCDIR if those in BUILDDIR dir are empty +# or did not appear. +$(GENERATED_USB_OS_FILES): $(GENERATED_USB_FILES) + @test -s "$@" || echo "WARNING: GENERATED_USB_OS_FILES: '$@' is empty or missing; do not use this build to generate 'dist' tarballs" >&2 + @$(MKDIR_P) "$(@D)" + @if ! test -s "$@" ; then \ + if test x"$(top_srcdir)" != x"$(top_builddir)" ; then \ + RELNAME="`echo "$@" | sed 's,^$(top_builddir)/*,,'`" ; \ + SRCNAME="$(top_srcdir)/$${RELNAME}" ; \ + if test -s "$${SRCNAME}" ; then \ + echo "WARNING: GENERATED_USB_OS_FILES: using '$${SRCNAME}' for '$@'" >&2 ; \ + cp "$${SRCNAME}" "$@" ; \ + fi ; \ + fi ; \ + fi + @touch "$@" + +$(GENERATED_USB_FILES): $(top_srcdir)/drivers/*-hid.c $(top_srcdir)/drivers/*usb*.c $(top_srcdir)/drivers/nutdrv_qx.c $(top_srcdir)/tools/nut-usbinfo.pl @if perl -e 1; then \ echo "Regenerating the USB helper files in SRC dir."; \ TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ @@ -85,7 +140,7 @@ $(GENERATED_USB_FILES): $(top_srcdir)/drivers/*-hid.c $(top_srcdir)/drivers/*usb # The distributed nut-snmpinfo.py.in template is assumed to only differ from # a generated nut-snmpinfo.py by the @PYTHON@ shebang. dist-hook: - @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ + @if [ -n "$(PYTHON)" ] && [ x"no" != x"$(PYTHON)" ] && $(PYTHON) -c 1; then \ echo "Regenerating the SNMP helper files in DIST dir with '$(PYTHON)'."; \ TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ diff --git a/tools/gitlog2changelog.py.in b/tools/gitlog2changelog.py.in index 5d882cbe37..a5e4d78660 100755 --- a/tools/gitlog2changelog.py.in +++ b/tools/gitlog2changelog.py.in @@ -1,22 +1,75 @@ #!@PYTHON@ # Copyright 2008 Marcus D. Hanwell # Minor changes for NUT by Charles Lepple +# Subsequent maintenance for NUT by Jim Klimov (since 2021) # Distributed under the terms of the GNU General Public License v2 or later -import string, re, os +import re +import os from textwrap import TextWrapper import sys +import subprocess -rev_range = '' +rev_range = "HEAD" if len(sys.argv) > 1: base = sys.argv[1] - rev_range = '%s..HEAD' % base + rev_range = "%s..HEAD" % base # Execute git log with the desired command line options. -fin = os.popen('git log --summary --stat --no-merges --date=short %s' % rev_range, 'r') -# Create a ChangeLog file in the current directory. -fout = open('ChangeLog', 'w') +# Support Python2 and Python3 (esp. 3.6 and earlier) semantics +# with regard to utf-8 content support (avois ascii decoding in Py3) +fin_mode = 0 +# Remove trailing end of line? spitlines() in py3 variant takes care of them +fin_chop = 0 +try: + p = subprocess.Popen( + [ + "git", + "log", + "--pretty=medium", + "--summary", + "--stat", + "--no-merges", + "--date=short", + ("%s" % rev_range), + ], + encoding="utf-8", + close_fds=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + fin, ferr = p.communicate() + if p.wait() != 0: + print("ERROR getting git changelog") + sys.exit(1) + fin = fin.splitlines() + fin_mode = 3 +except TypeError: + fin = os.popen( + "git log --pretty=medium --summary --stat --no-merges --date=short %s" + % rev_range, + "r", + ) + fin_mode = 2 + fin_chop = 1 + +# Create a ChangeLog file in the current directory by default. +CHANGELOG_FILE = "ChangeLog" +try: + # e.g. point from Makefile to a builddir (caller ensures it exists) + if os.environ.get("CHANGELOG_FILE", None) is not None: + CHANGELOG_FILE = os.environ.get("CHANGELOG_FILE") +except Exception as ignored: + pass + +if CHANGELOG_FILE == "-": + fout = sys.stdout +else: + if fin_mode == 3: + fout = open(CHANGELOG_FILE, "w", encoding="UTF-8") + else: + fout = open(CHANGELOG_FILE, "w") # Set up the loop variables in order to locate the blocks we want authorFound = False @@ -28,12 +81,13 @@ messageNL = False files = "" prevAuthorLine = "" -wrapper = TextWrapper(initial_indent="\t", subsequent_indent="\t ") +# See also: https://github.com/python/cpython/blob/main/Lib/textwrap.py +wrapper = TextWrapper(initial_indent="\t", subsequent_indent="\t ", break_on_hyphens=False, break_long_words=False) # The main part of the loop for line in fin: # The commit line marks the start of a new commit object. - if line.startswith('commit'): + if line.startswith("commit"): # Start all over again... authorFound = False dateFound = False @@ -45,42 +99,42 @@ for line in fin: continue # Match the author line and extract the part we want # (Don't use startswith to allow Author override inside commit message.) - elif 'Author:' in line: - authorList = re.split(': ', line, 1) + elif "Author:" in line: + authorList = re.split(": ", line, 1) try: author = authorList[1] - author = author[0:len(author)-1] + author = author[0 : len(author) - fin_chop] authorFound = True except: - print ("Could not parse authorList = '%s'" % (line)) + print("Could not parse authorList = '%s'" % (line)) # Match the date line - elif line.startswith('Date:'): - dateList = re.split(': ', line, 1) + elif line.startswith("Date:"): + dateList = re.split(": ", line, 1) try: date = dateList[1] - date = date[0:len(date)-1] + date = date[0 : len(date) - fin_chop] dateFound = True except: - print ("Could not parse dateList = '%s'" % (line)) + print("Could not parse dateList = '%s'" % (line)) # The Fossil-IDs are ignored: - elif line.startswith(' Fossil-ID:') or line.startswith(' [[SVN:'): + elif line.startswith(" Fossil-ID:") or line.startswith(" [[SVN:"): continue # The svn-id lines are ignored - elif ' git-svn-id:' in line: + elif " git-svn-id:" in line: continue # The sign off line is ignored too - elif 'Signed-off-by' in line: + elif "Signed-off-by" in line: continue # Extract the actual commit message for this commit - elif authorFound & dateFound & messageFound == False: + elif authorFound and dateFound and messageFound is False: # Find the commit message if we can - if len(line) == 1: + if len(line) == fin_chop: if messageNL: messageFound = True else: messageNL = True - elif len(line) == 4: + elif len(line) == 3 + fin_chop: messageFound = True else: if len(message) == 0: @@ -88,19 +142,19 @@ for line in fin: else: message = message + " " + line.strip() # If this line is hit all of the files have been stored for this commit - elif re.search('files? changed', line): + elif re.search("files? changed", line): filesFound = True continue # Collect the files for this commit. FIXME: Still need to add +/- to files - elif authorFound & dateFound & messageFound: - fileList = re.split(' \| ', line, 2) + elif authorFound and dateFound and messageFound: + fileList = re.split(r' \| ', line, 2) if len(fileList) > 1: if len(files) > 0: files = files + ", " + fileList[0].strip() else: files = fileList[0].strip() # All of the parts of the commit have been found - write out the entry - if authorFound & dateFound & messageFound & filesFound: + if authorFound and dateFound and messageFound and filesFound: # First the author line, only outputted if it is the first for that # author on this day authorLine = date + " " + author @@ -113,12 +167,28 @@ for line in fin: # Assemble the actual commit message line(s) and limit the line length # to 80 characters. - commitLine = "* " + files + ": " + message + # Avoid printing same (or equivalen) filename lists twice, if commit + # message starts with them. + if message.startswith(files + ":"): + commitLine = "* " + message + else: + namesF = None + namesM = None + try: + namesM = sorted(re.split(r"[ ,]", message.split(":")[0])) + namesF = sorted(re.split(r"[ ,]", files)) + except: + pass + + if namesM is not None and namesM == namesF: + commitLine = "* " + message + else: + commitLine = "* " + files + ": " + message # Write out the commit line fout.write(wrapper.fill(commitLine) + "\n") - #Now reset all the variables ready for a new commit block. + # Now reset all the variables ready for a new commit block. authorFound = False dateFound = False messageFound = False @@ -129,5 +199,9 @@ for line in fin: prevAuthorLine = authorLine # Close the input and output lines now that we are finished. -fin.close() +if fin_mode == 3: + p.stdout.close() + p.stderr.close() +else: + fin.close() fout.close() diff --git a/tools/nut-ddl-dump.sh b/tools/nut-ddl-dump.sh index 326a06daa2..4a1a9f3958 100755 --- a/tools/nut-ddl-dump.sh +++ b/tools/nut-ddl-dump.sh @@ -2,13 +2,15 @@ ################################################################################ # A script to ease the generation of NUT device dumps for NUT Devices Dumps Library ################################################################################ -# Author: (C) Arnaud Quette +# Authors: +# Copyright (C) 2015 - 2017 by Arnaud Quette +# Copyright (C) 2020 - 2023 by Jim Klimov # License: GPL v2+ ################################################################################ # FIXME: # - check if a previous report exists, and increase report number #Ā  - we currently use the .dev format ; but also consider the NDS format -# http://www.networkupstools.org/ddl/ +# https://www.networkupstools.org/ddl/ ################################################################################ strUsage="Usage: $0 " @@ -18,48 +20,66 @@ if [ -z "$1" ]; then echo "$strUsage" exit else - DDL_DEVICE_NAME=$1 + DDL_DEVICE_NAME="$1" fi # Test communication with the device -upscResult="`upsc ${DDL_DEVICE_NAME} 2> /dev/null`" +upscResult="`upsc "${DDL_DEVICE_NAME}" 2> /dev/null`" if [ $? -gt 0 ]; then - echo "Can't communicate with ${DDL_DEVICE_NAME}" + echo "Can't communicate with ${DDL_DEVICE_NAME}" >&2 exit fi # Collect more information dumpDate="`date -R`" -upsrwResult="`upsrw ${DDL_DEVICE_NAME} 2> /dev/null`" -upscmdResult="`upscmd -l ${DDL_DEVICE_NAME} 2> /dev/null`" +upsrwResult="`upsrw "${DDL_DEVICE_NAME}" 2> /dev/null`" +upscmdResult="`upscmd -l "${DDL_DEVICE_NAME}" 2> /dev/null`" # Build the filename # ________. # Process the Manufacturer name -RAW_DDL_MFR="`upsc ${DDL_DEVICE_NAME} device.mfr 2>/dev/null`" +RAW_DDL_MFR="`upsc "${DDL_DEVICE_NAME}" device.mfr 2>/dev/null`" if [ "${RAW_DDL_MFR}" = "EATON" ]; then RAW_DDL_MFR="Eaton" fi # Replace spaces with underscores -DDL_MFR="`echo ${RAW_DDL_MFR} | sed s/\ /_/g`" +DDL_MFR="`echo "${RAW_DDL_MFR}" | sed s/\ /_/g`" # Process the Model name # Replace spaces with underscores -RAW_DDL_MODEL="`upsc ${DDL_DEVICE_NAME} device.model 2>/dev/null`" -DDL_MODEL="`echo ${RAW_DDL_MODEL} | sed s/\ /_/g`" +RAW_DDL_MODEL="`upsc "${DDL_DEVICE_NAME}" device.model 2>/dev/null`" +DDL_MODEL="`echo "${RAW_DDL_MODEL}" | sed s/\ /_/g`" # Process the driver name and NUT version -DDL_DRIVER_NAME="`upsc ${DDL_DEVICE_NAME} driver.name 2>/dev/null`" -DDL_NUT_VERSION="`upsc ${DDL_DEVICE_NAME} driver.version 2>/dev/null`" -# TODO: check if a similar file exists, to update Report nb +DDL_DRIVER_NAME="`upsc "${DDL_DEVICE_NAME}" driver.name 2>/dev/null`" +DDL_NUT_VERSION="`upsc "${DDL_DEVICE_NAME}" driver.version 2>/dev/null`" +# TODO: check if a similar file exists in nut-ddl repo, to update Report nb DDL_REPORT_NUMBER="01" DDL_FILENAME="${DDL_MFR}__${DDL_MODEL}__${DDL_DRIVER_NAME}__${DDL_NUT_VERSION}__${DDL_REPORT_NUMBER}.dev" # Dump device data into the file -echo "# Device dump generated by $0 on $dumpDate" > ${DDL_FILENAME} -echo "# upsrw output:" >> ${DDL_FILENAME} -echo "${upsrwResult}" | sed -e 's/^/# /' >> ${DDL_FILENAME} -echo "# upscmd output:" >> ${DDL_FILENAME} -echo "${upscmdResult}" | sed -e 's/^/# /' >> ${DDL_FILENAME} -echo "" >> ${DDL_FILENAME} -echo "# upsc output:" >> ${DDL_FILENAME} -echo "${upscResult}" >> ${DDL_FILENAME} -echo "${DDL_FILENAME} generated using ${DDL_DEVICE_NAME} " +( + echo "# Please add if relevant: DEVICE:URL:REPORT: " + echo "# Please add if relevant: DEVICE:URL:VENDOR: " + echo "" + echo "# Please add comments for humans here and perhaps raise concerns about the data points," + echo "# see https://networkupstools.org/ddl/#devseq-files for comment-tag syntax" + echo "# strip sensitive data (passwords, SNMP community, serial number, IP address, host name...)" + echo "# and post as a pull request to https://github.com/networkupstools/nut-ddl/" + echo "# DEVICE:COMMENT:" + echo "# Device dump generated by $0 on ${dumpDate}" + echo "# DEVICE:EOC" + echo "" + echo "# :; upsc ${DDL_DEVICE_NAME}" + echo "${upscResult}" + echo "" + echo "# DEVICE:COMMENT-BLOCK:FIXME:UPSRW: ${DDL_DEVICE_NAME}" + echo "${upsrwResult}" | sed -e 's/^/# /' -e 's/^# $/#/' + echo "# DEVICE:EOC" + echo "" + echo "# DEVICE:COMMENT-BLOCK:FIXME:UPSCMD: ${DDL_DEVICE_NAME}" + echo "${upscmdResult}" | sed -e 's/^/# /' -e 's/^# $/#/' + echo "# DEVICE:EOC" + echo "" +) > ${DDL_FILENAME} + +echo "${DDL_FILENAME} generated using ${DDL_DEVICE_NAME} on ${dumpDate}" +echo "Please revise and post as a pull request to https://github.com/networkupstools/nut-ddl/" 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/.gitignore b/tools/nut-scanner/.gitignore index 05ea931f54..12fd1a92ec 100644 --- a/tools/nut-scanner/.gitignore +++ b/tools/nut-scanner/.gitignore @@ -2,5 +2,7 @@ /nutscan-snmp.c /nutscan-usb.h /nut-scanner-reindex-dmfsnmp +/dmf-reindex /serial.c /bcmxcp_ser.c +/README diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 56b41f9d66..f5b6d01230 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -1,6 +1,19 @@ +# Network UPS Tools: nut-scanner + +# Export certain values for ccache which NUT ci_build.sh can customize, +# to facilitate developer iteration re-runs of "make" later. +# At least GNU and BSD make implementations are okay with this syntax. +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_NAMESPACE=@CCACHE_NAMESPACE@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_BASEDIR=@CCACHE_BASEDIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_DIR=@CCACHE_DIR@ +@NUT_AM_MAKE_CAN_EXPORT@export CCACHE_PATH=@CCACHE_PATH@ +@NUT_AM_MAKE_CAN_EXPORT@export PATH=@PATH_DURING_CONFIGURE@ + # Generally, list headers and/or sources which are re-generated # for nut-scanner in the parent dir NUT_SCANNER_DEPS_H = nutscan-usb.h +### DMF recipe modification: we generate C not H here for SNMP: +###NUT_SCANNER_DEPS_H += nutscan-snmp.h NUT_SCANNER_DEPS_C = nutscan-snmp.c # General set of nut-scanner dependencies generated in the parent dir @@ -8,19 +21,23 @@ NUT_SCANNER_DEPS = $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) BUILT_SOURCES = $(NUT_SCANNER_DEPS) CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = README.adoc # Make sure we have the freshest files (no-op if built earlier and then # no driver sources and other dependencies were edited by a developer) $(NUT_SCANNER_DEPS): dummy - @cd .. && $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps + +@cd .. && $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps # Make sure out-of-dir dependencies exist (especially when dev-building parts): $(top_builddir)/include/nut_version.h \ -$(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libparseconf.la \ $(top_builddir)/common/libnutdmfsnmp.la \ -$(top_builddir)/drivers/libserial-nutscan.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) +$(top_builddir)/common/libnutwincompat.la \ +$(top_builddir)/drivers/libserial-nutscan.la \ +$(top_builddir)/common/libcommonstr.la \ +$(top_builddir)/common/libcommonclient.la \ +$(top_builddir)/common/libcommon.la: dummy + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) # do not hard depend on '../include/nut_version.h', since it blocks # 'dist', and is only required for actual build, in which case @@ -55,10 +72,10 @@ LDFLAGS_LUA = $(LUA_LIB) if WITH_DMF_LUA CFLAGS_DMF_LUA = -DWITH_DMF_LUA=1 -DWITH_DMF_FUNCTIONS=1 $(CFLAGS_LUA) LDFLAGS_DMF_LUA = $(LDFLAGS_LUA) -else +else !WITH_DMF_LUA CFLAGS_DMF_LUA = -DWITH_DMF_LUA=0 LDFLAGS_DMF_LUA = -endif +endif !WITH_DMF_LUA if WITH_NEON if WITH_SNMP @@ -70,31 +87,32 @@ if WITH_DMF nut_scanner_reindex_dmfsnmp_SOURCES = nut-scanner-reindex-dmfsnmp.c nut_scanner_reindex_dmfsnmp_LDADD = \ $(top_builddir)/common/libnutdmfsnmp.la \ - $(top_builddir)/common/libcommon.la \ + $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libparseconf.la \ - $(LIBNETSNMP_LIBS) + $(LIBNETSNMP_LIBS) nut_scanner_reindex_dmfsnmp_CFLAGS = -I$(top_srcdir)/include \ - -I$(top_srcdir)/drivers -I$(top_srcdir)/tools/nut-scanner \ - $(LIBNETSNMP_CFLAGS) $(LIBNEON_CFLAGS) \ - -DDMFREINDEXER_MAKECHECK=0 + -I$(top_srcdir)/drivers -I$(top_srcdir)/tools/nut-scanner \ + $(LIBNETSNMP_CFLAGS) $(LIBNEON_CFLAGS) \ + -DDMFREINDEXER_MAKECHECK=0 if WITH_DMF_LUA nut_scanner_reindex_dmfsnmp_LDADD += $(LDFLAGS_DMF_LUA) nut_scanner_reindex_dmfsnmp_CFLAGS += $(CFLAGS_DMF_LUA) -endif +endif WITH_DMF_LUA if ! WITH_LIBLTDL nut_scanner_reindex_dmfsnmp_LDADD += $(LIBNEON_LIBS) -endif -endif -endif -endif +endif ! WITH_LIBLTDL +endif WITH_DMF +endif WITH_SNMP +endif WITH_NEON -# Only build nut-scanner, and its library, if libltdl was found (required ATM!) -if WITH_LIBLTDL +# Note: we only build nut-scanner, and its library, if libltdl was found (required ATM!) +if WITH_NUT_SCANNER bin_PROGRAMS += nut-scanner lib_LTLIBRARIES += libnutscan.la -endif -libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c nutscan-snmp.c \ +endif WITH_NUT_SCANNER +libnutscan_la_SOURCES = scan_nut.c scan_nut_simulation.c scan_ipmi.c \ + nutscan-snmp.c \ nutscan-device.c nutscan-ip.c nutscan-display.c \ nutscan-init.c scan_usb.c scan_snmp.c scan_xml_http.c \ scan_avahi.c scan_eaton_serial.c nutscan-serial.c @@ -103,8 +121,22 @@ libnutscan_la_LIBADD = $(NETLIBS) libnutscan_la_LIBADD += $(top_builddir)/drivers/libserial-nutscan.la if WITH_LIBLTDL libnutscan_la_LIBADD += $(LIBLTDL_LIBS) -endif +endif WITH_LIBLTDL #libnutscan_la_LIBADD += $(top_builddir)/common/libcommonclient.la +if HAVE_SEMAPHORE_LIBS +# Are additional libraries needed for semaphore support? +libnutscan_la_LIBADD += $(SEMLIBS) +endif HAVE_SEMAPHORE_LIBS + +libnutscan_la_LIBADD += $(SERLIBS) + +libnutscan_la_LDFLAGS = +if HAVE_WINDOWS + # Many versions of MingW seem to fail to build non-static DLL without this + libnutscan_la_LIBADD += $(top_builddir)/common/libnutwincompat.la + libnutscan_la_LDFLAGS += -no-undefined +endif HAVE_WINDOWS + # # Below we set API versions of public libraries # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html @@ -112,7 +144,7 @@ endif # object .so names would differ) # # libnutscan version information -libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 +libnutscan_la_LDFLAGS += -version-info 2:5:0 # libnutscan exported symbols regex # WARNING: Since the library includes parts of libcommon (as much as needed @@ -124,28 +156,30 @@ libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 # One solution to tackle if needed for those cases would be to make some # dynamic/shared libnutcommon (etc.) if WITH_DMF -libnutscan_la_LDFLAGS += -export-symbols-regex '^((dmf)?nutscan_|uninit_snmp_device_table|nut_debug_level|s_upsdebugx|max_threads|curr_threads)' -else -libnutscan_la_LDFLAGS += -export-symbols-regex '^(nutscan_|nut_debug_level|s_upsdebugx|max_threads|curr_threads)' -endif - +libnutscan_la_LDFLAGS += -export-symbols-regex '^((dmf)?nutscan_|uninit_snmp_device_table|nut_debug_level|s_upsdebugx|max_threads|curr_threads|nut_report_config_flags|upsdebugx_report_search_paths|nut_prepare_search_paths)' +else !WITH_DMF +libnutscan_la_LDFLAGS += -export-symbols-regex '^(nutscan_|nut_debug_level|s_upsdebugx|max_threads|curr_threads|nut_report_config_flags|upsdebugx_report_search_paths|nut_prepare_search_paths)' +endif !WITH_DMF libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include \ $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers + +if WITH_NEON +if WITH_SNMP if WITH_DMF libnutscan_la_CFLAGS += -DWITH_DMFMIB=1 if WITH_DMF_LUA libnutscan_la_LDFLAGS += $(LDFLAGS_DMF_LUA) libnutscan_la_CFLAGS += -DWITH_DMF_LUA=1 $(CFLAGS_DMF_LUA) -else +else !WITH_DMF_LUA libnutscan_la_CFLAGS += -DWITH_DMF_LUA=0 -endif -else +endif !WITH_DMF_LUA +libnutscan_la_LIBADD += $(top_builddir)/common/libnutdmfsnmp.la +else !WITH_DMF libnutscan_la_CFLAGS += -DWITH_DMFMIB=0 -DWITH_DMF_LUA=0 -endif +endif !WITH_DMF +endif WITH_SNMP +endif WITH_NEON -if WITH_DMF -libnutscan_la_LIBADD += $(top_builddir)/common/libnutdmfsnmp.la -endif libnutscan_la_LIBADD += $(top_builddir)/common/libcommonstr.la #libnutscan_la_LIBADD += $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la @@ -163,56 +197,71 @@ nut_scanner_CFLAGS += -DWITH_DMFMIB=1 if WITH_DMF_LUA nut_scanner_CFLAGS += -DWITH_DMF_LUA=1 $(CFLAGS_DMF_LUA) nut_scanner_LDFLAGS = $(LDFLAGS_DMF_LUA) -else +else !WITH_DMF_LUA nut_scanner_CFLAGS += -DWITH_DMF_LUA=0 -endif -else +endif !WITH_DMF_LUA +else !WITH_DMF nut_scanner_CFLAGS += -DWITH_DMFMIB=0 -DWITH_DMF_LUA=0 -endif +endif !WITH_DMF if ! WITH_LIBLTDL # The libs needed to handle XML parsing and usage of DMF SNMP are ltdl()'ed # but without this builds fail (TODO: Revise responsible code? LTDL in DMF?) # $(LIBNETSNMP_LIBS) libnutscan_la_LIBADD += $(LIBNEON_LIBS) -endif -endif -endif +endif !WITH_LIBLTDL +endif WITH_NEON +endif WITH_SNMP if WITH_SSL libnutscan_la_CFLAGS += $(LIBSSL_CFLAGS) libnutscan_la_LIBADD += $(LIBSSL_LIBS) -endif +endif WITH_SSL if WITH_USB libnutscan_la_CFLAGS += $(LIBUSB_CFLAGS) -endif +endif WITH_USB +# Note: do not indent automake "if" lines if WITH_SNMP libnutscan_la_CFLAGS += $(LIBNETSNMP_CFLAGS) -endif +if WITH_SNMP_STATIC + # MinGW builds of libnetsnmp are static-only, so we link it in: + libnutscan_la_CFLAGS += -DWITH_SNMP_STATIC=1 + # Some workarouds here, to avoid libtool bailing out like this: + # *** Warning: This system cannot link to static lib archive /usr/x86_64-w64-mingw32/lib//libnetsnmp.la. + # *** I have the capability to make that library automatically link in when + # *** you link to this library. But I can only do this if you have a + # *** shared version of the library, which you do not appear to have. + # Note that LIBNETSNMP_LIBS prepared by nut_check_libnetsnmp.m4 for such + # builds particularly WITH_SNMP_STATIC is special (with -Wl passthrough, + # and for that automake requires LDFLAGS not LIBADD): + libnutscan_la_LDFLAGS += $(LIBNETSNMP_LIBS) +endif WITH_SNMP_STATIC +endif WITH_SNMP if WITH_NEON libnutscan_la_CFLAGS += $(LIBNEON_CFLAGS) -endif +endif WITH_NEON if WITH_AVAHI libnutscan_la_CFLAGS += $(LIBAVAHI_CFLAGS) -endif +endif WITH_AVAHI if WITH_IPMI libnutscan_la_CFLAGS += $(LIBIPMI_CFLAGS) -endif +endif WITH_IPMI # C is not a header, but there is no dist_noinst_SOURCES dist_noinst_HEADERS += $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) +# Optionally deliverable as part of NUT public API: if WITH_DEV include_HEADERS += nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h nutscan-snmp.h -else +else !WITH_DEV dist_noinst_HEADERS += nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h nutscan-snmp.h -endif +endif !WITH_DEV dummy: -CLEANFILES += *-spellchecked +CLEANFILES += *-spellchecked README nut-scanner-reindex-dmfsnmp dmf-reindex MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! #clean-local: -# rm -rf $(builddir)/.deps +# $(AM_V_at)rm -rf $(builddir)/.deps diff --git a/tools/nut-scanner/README b/tools/nut-scanner/README.adoc similarity index 93% rename from tools/nut-scanner/README rename to tools/nut-scanner/README.adoc index d834754399..2f574997de 100644 --- a/tools/nut-scanner/README +++ b/tools/nut-scanner/README.adoc @@ -28,6 +28,8 @@ functions. Here is a simple example that scans for USB devices, and use its own iteration function to display results: +.Scanning and reporting example +------ #include #include #include @@ -41,10 +43,11 @@ iteration function to display results: { nutscan_options_t * opt; nutscan_device_t *device; + nutscan_usb_t usb_scanopts; nutscan-init(); - if ((device = nutscan_scan_usb()) == NULL) { + if ((device = nutscan_scan_usb(&usb_scanopts)) == NULL) { printf("No device found\n"); exit(EXIT_FAILURE); } @@ -78,7 +81,7 @@ iteration function to display results: exit(EXIT_SUCCESS); } - +------ This library file and the associated header files are not installed by default. You must `./configure --with-dev` to enable building and @@ -95,8 +98,8 @@ referenced in the same file. Configuration helpers ~~~~~~~~~~~~~~~~~~~~~ -NUT provides helper scripts to ease the configuration step of your program, by -detecting the right compilation and link flags. +NUT provides helper scripts to ease the configuration step of your program, +by detecting the right compilation and link flags. For more information, refer to a <>. diff --git a/tools/nut-scanner/nut-scan.h b/tools/nut-scanner/nut-scan.h index dee6739430..3bba701369 100644 --- a/tools/nut-scanner/nut-scan.h +++ b/tools/nut-scanner/nut-scan.h @@ -1,9 +1,10 @@ /* * Copyright (C) * 2011 - EATON - * 2012 - Arnaud Quette + * 2012 - 2024 Arnaud Quette * 2016 - EATON - IP addressed XML scan - * 2016-2021 - EATON - Various threads-related improvements + * 2016 - 2021 - EATON - Various threads-related improvements + * 2023 - 2024 - 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 @@ -31,14 +32,36 @@ #ifndef NUT_SCAN_H #define NUT_SCAN_H -#include "config.h" #include -#include "nut_stdint.h" -#include -#include -#include -#include +/* Ensure uint16_t et al: */ +#if defined HAVE_INTTYPES_H +# include +#endif + +#if defined HAVE_STDINT_H +# include +#endif + +#if defined HAVE_LIMITS_H +# include +#endif + +/* Ensure useconds_t et al: */ +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include "nutscan-init.h" +#include "nutscan-device.h" +#include "nutscan-ip.h" #ifdef WITH_IPMI #include @@ -105,10 +128,10 @@ typedef struct nutscan_ipmi { #define IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY 0x04 #define IPMI_AUTHENTICATION_TYPE_OEM_PROP 0x05 #define IPMI_AUTHENTICATION_TYPE_RMCPPLUS 0x06 -#endif +#endif /* IPMI_AUTHENTICATION_TYPE_NONE */ #ifndef IPMI_PRIVILEGE_LEVEL_ADMIN #define IPMI_PRIVILEGE_LEVEL_ADMIN 0x04 -#endif +#endif /* IPMI_PRIVILEGE_LEVEL_ADMIN */ #define IPMI_1_5 1 #define IPMI_2_0 0 @@ -121,10 +144,24 @@ typedef struct nutscan_xml { char *peername; /* Hostname or NULL for broadcast mode */ } nutscan_xml_t; +/* USB scan options structure */ +typedef struct nutscan_usb { + /* Hardware link related values below are not reliable for run-time + * matching (they can change over time) but can be useful if e.g. + * "serial" is not available or unique */ + int report_bus; + int report_busport; + int report_device; + + /* The value is not currently used for device matching, but might be + * used later, and it is available from discovery */ + int report_bcdDevice; +} nutscan_usb_t; + /* Scanning */ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, useconds_t usec_timeout, nutscan_snmp_t * sec); -nutscan_device_t * nutscan_scan_usb(void); +nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts); /* If "ip" == NULL, do a broadcast scan */ /* If sec->usec_timeout <= 0 then the common usec_timeout arg overrides it */ @@ -132,6 +169,8 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char *start_ip, const char nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, useconds_t usec_timeout); +nutscan_device_t * nutscan_scan_nut_simulation(void); + nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout); nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec); @@ -150,6 +189,11 @@ sem_t * nutscan_semaphore(void); void nutscan_display_ups_conf(nutscan_device_t * device); void nutscan_display_parsable(nutscan_device_t * device); +/* Display sanity-check concerns for various fields etc. (if any) */ +void nutscan_display_ups_conf_with_sanity_check(nutscan_device_t * device); +void nutscan_display_sanity_check(nutscan_device_t * device); +void nutscan_display_sanity_check_serial(nutscan_device_t * device); + #ifdef __cplusplus /* *INDENT-OFF* */ } diff --git a/tools/nut-scanner/nut-scanner-reindex-dmfsnmp.c b/tools/nut-scanner/nut-scanner-reindex-dmfsnmp.c index 1109b8c336..7bf788fe5f 100644 --- a/tools/nut-scanner/nut-scanner-reindex-dmfsnmp.c +++ b/tools/nut-scanner/nut-scanner-reindex-dmfsnmp.c @@ -5,6 +5,7 @@ * Copyright (C) 2016 Carlos Dominguez * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016 Jim Klimov + * Copyright (C) 2024 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 @@ -21,11 +22,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + #include #include #include -#include "config.h" #include "common.h" #if (!DMFREINDEXER_MAKECHECK) # include "nut_version.h" @@ -41,11 +43,11 @@ #define XSD_DMFNUTSCAN_XMLNS "http://www.networkupstools.org/dmf/snmp/nutscan" #endif -const char optstring[] = "?hDVkKZ:"; +static const char optstring[] = "?hDVkKZ:"; #define ERR_BAD_OPTION (-1) #ifdef HAVE_GETOPT_LONG -const struct option longopts[] = { +static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "nut_debug_level", no_argument, NULL, 'D' }, { "version", no_argument, NULL, 'V' }, @@ -62,6 +64,10 @@ int main(int argc, char *argv[]) int opt_ret; int result = 0; int ret_code = EXIT_SUCCESS; + mibdmf_parser_t *dmp = NULL, *newdmp = NULL; + snmp_device_id_t *devtab = NULL, *newdevtab = NULL; + size_t i, j, k, newdmf_len, newdmf_size; + char *newdmf = NULL; int proceed_on_errors = 1; /* By default, do as much as we can */ char *dir_name = NULL; /* TODO: Make configurable the dir and/or list of files */ int dir_name_dynamic = 0; @@ -82,6 +88,15 @@ int main(int argc, char *argv[]) /* TODO: Usage (help), Command-line args */ /* option to append just a few (new) files to existing (large) index */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK +#pragma GCC diagnostic ignored "-Wunreachable-code-break" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif while ((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) { switch(opt_ret) { @@ -133,12 +148,15 @@ int main(int argc, char *argv[]) return ret_code; } } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif - mibdmf_parser_t * dmp = mibdmf_parser_new(); + dmp = mibdmf_parser_new(); if (!dmp) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not allocate the DMF parsing structures\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } upsdebugx(1, "=== DMF-Reindex: Loading DMF structures from directory '%s':\n\n", dir_name); @@ -149,18 +167,18 @@ int main(int argc, char *argv[]) /* TODO: Error-checking? Faults in some parses should be fatal or not? */ fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Could not find or parse some files (return code %i)\n", result); /* TODO: Can we pass this code to fatalx? */ - return result; + /*return result;*/ } /* Loop through discovered device_table and print it back as DMF markup */ upsdebugx(2, "=== DMF-Reindex: Print DMF subset for snmp_device_table[]...\n\n"); - snmp_device_id_t *devtab = mibdmf_get_device_table(dmp); + devtab = mibdmf_get_device_table(dmp); if (!devtab) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not access the parsed device_table\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } /* Below we sprintf the index into a memory string, parse the result as @@ -169,13 +187,13 @@ int main(int argc, char *argv[]) /* TODO: uniquify output, so that an old index that was read in does not * pollute the parsed results (at least not for completely same items as * already exist in the table)? What to do about partial hits ~ updates? */ - size_t i; - size_t newdmf_len = 0, newdmf_size = 1024; - char *newdmf = (char*)calloc(newdmf_size, sizeof(char)); + newdmf_len = 0; + newdmf_size = 1024; + newdmf = (char*)calloc(newdmf_size, sizeof(char)); if (!newdmf) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not allocate the buffer for parsed DMF\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } newdmf_len += snprintf(newdmf + newdmf_len, (newdmf_size - newdmf_len), "\n" @@ -193,7 +211,7 @@ int main(int argc, char *argv[]) if (!newdmf) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not extend the buffer for parsed DMF\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } upsdebugx(2, "\nExtended the buffer to %zu bytes\n", newdmf_size); } @@ -222,11 +240,11 @@ int main(int argc, char *argv[]) upsdebugx(2, "[LAST: num=%zu (lenafter=%zu)] ", i, newdmf_len); upsdebugx(1, "\n=== DMF-Reindex: Indexed %zu entries...\n\n", i); - mibdmf_parser_t * newdmp = mibdmf_parser_new(); + newdmp = mibdmf_parser_new(); if (!newdmp) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not allocate the DMF verification parsing structures\n\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } upsdebugx(1, "=== DMF-Reindex: Loading DMF structures from prepared string (verification)\n\n"); @@ -235,20 +253,21 @@ int main(int argc, char *argv[]) if (result != 0) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: The generated document FAILED syntax verification (return code %d)\n\n", ret_code); /* TODO: Can we pass this code to fatalx? */ - return ret_code; + /*return ret_code;*/ } /* Loop through reparsed device_table and compare to original one */ upsdebugx(1, "=== DMF-Reindex: Verify reparsed content for snmp_device_table[]...\n\n"); - snmp_device_id_t *newdevtab = mibdmf_get_device_table(newdmp); + newdevtab = mibdmf_get_device_table(newdmp); if (!newdevtab) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: FATAL: Can not access the reparsed device_table\n"); /* TODO: Can we pass this code to fatalx? */ - return ENOMEM; + /*return ENOMEM;*/ } - size_t j = -1, k = -1; + j = -1; + k = -1; result = 0; /* Make sure that all values we've considered are present in re-parse */ for (k = 0; !is_sentinel__snmp_device_id_t(&(devtab[k])) ; k++) @@ -293,7 +312,7 @@ int main(int argc, char *argv[]) { fatalx(EXIT_FAILURE, "=== DMF-Reindex: The generated document FAILED content verification (%d issues)\n\n", result); /* TODO: Can we pass this code to fatalx? */ - return result; + /*return result;*/ } upsdebugx(1, "=== DMF-Reindex: Checks succeeded - printing generated DMF to stdout...\n\n"); diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 0da36716f0..060e3e7080 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2011 - 2012 Arnaud Quette + * Copyright (C) 2011 - 2024 Arnaud Quette * Copyright (C) 2016 Michal Vyskocil * Copyright (C) 2016 - 2021 Jim Klimov + * Copyright (C) 2022 - 2024 Jim Klimov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ \author Arnaud Quette \author Michal Vyskocil \author Jim Klimov + \author Jim Klimov */ #include "common.h" /* Must be first include to pull "config.h" */ @@ -57,18 +59,23 @@ #include "nut-scan.h" +#ifndef WITH_DMFMIB +# define WITH_DMFMIB 0 +#endif + #if WITH_DMFMIB # ifdef WANT_LIBNUTSCAN_SNMP_DMF # undef WANT_LIBNUTSCAN_SNMP_DMF # endif -// This chains to also include nutscan-snmp.h and the desired -// variables need structures defined lower in the dmf.h file. -// But there is protection in nutscan-snmp.h to only declare -// those vars if dmf.h was already completely imported. +/* This chains to also include nutscan-snmp.h and the desired + * variables need structures defined lower in the dmf.h file. + * But there is protection in nutscan-snmp.h to only declare + * those vars if dmf.h was already completely imported. + */ # include "dmf.h" -// Now we may "want" the variables from libnutscan with types from dmf.h +/* Now we may "want" the variables from libnutscan with types from dmf.h */ # define WANT_LIBNUTSCAN_SNMP_DMF 1 # include "nutscan-snmp.h" #endif /* WITH_DMFMIB */ @@ -89,7 +96,7 @@ #define ERR_BAD_OPTION (-1) /* TODO : #if WITH_DMFMIB for options to set up path(s) to the DMFs to load */ -static const char optstring[] = "?ht:T:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:NPqIVaDzZ:"; +static const char optstring[] = "?ht:T:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:QnNPqIVaDzZ:"; #ifdef HAVE_GETOPT_LONG static const struct option longopts[] = { @@ -117,7 +124,9 @@ static const struct option longopts[] = { { "xml_scan", no_argument, NULL, 'M' }, { "oldnut_scan", no_argument, NULL, 'O' }, { "avahi_scan", no_argument, NULL, 'A' }, + { "nut_simulation_scan", no_argument, NULL, 'n' }, { "ipmi_scan", no_argument, NULL, 'I' }, + { "disp_nut_conf_with_sanity_check", no_argument, NULL, 'Q' }, { "disp_nut_conf", no_argument, NULL, 'N' }, { "disp_parsable", no_argument, NULL, 'P' }, { "quiet", no_argument, NULL, 'q' }, @@ -140,15 +149,62 @@ static char * start_ip = NULL; static char * end_ip = NULL; static char * port = NULL; static char * serial_ports = NULL; +static int cli_link_detail_level = -1; #ifdef HAVE_PTHREAD static pthread_t thread[TYPE_END]; static void * run_usb(void *arg) { - NUT_UNUSED_VARIABLE(arg); + nutscan_usb_t scanopts, *scanopts_ptr = &scanopts; + + if (!arg) { + /* null => use library defaults; should not happen here anyway */ + scanopts_ptr = NULL; + } else { + /* 0: do not report bus/device/busport details + * 1: report bus and busport, if available + * 2: report bus/device/busport details + * 3: like (2) and report bcdDevice (limited use and benefit) + */ + int link_detail_level = *((int*)arg); + + switch (link_detail_level) { + case 0: + scanopts.report_bus = 0; + scanopts.report_busport = 0; + scanopts.report_device = 0; + scanopts.report_bcdDevice = 0; + break; + + case 1: + scanopts.report_bus = 1; + scanopts.report_busport = 1; + scanopts.report_device = 0; + scanopts.report_bcdDevice = 0; + break; + + case 2: + scanopts.report_bus = 1; + scanopts.report_busport = 1; + scanopts.report_device = 1; + scanopts.report_bcdDevice = 0; + break; - dev[TYPE_USB] = nutscan_scan_usb(); + case 3: + scanopts.report_bus = 1; + scanopts.report_busport = 1; + scanopts.report_device = 1; + scanopts.report_bcdDevice = 1; + break; + + default: + upsdebugx(1, "%s: using library default link_detail_level settings", __func__); + scanopts_ptr = NULL; + } + } + + dev[TYPE_USB] = nutscan_scan_usb(scanopts_ptr); return NULL; } @@ -176,6 +232,14 @@ static void * run_nut_old(void *arg) return NULL; } +static void * run_nut_simulation(void *arg) +{ + NUT_UNUSED_VARIABLE(arg); + + dev[TYPE_NUT_SIMULATION] = nutscan_scan_nut_simulation(); + return NULL; +} + static void * run_avahi(void *arg) { NUT_UNUSED_VARIABLE(arg); @@ -199,17 +263,21 @@ static void * run_eaton_serial(void *arg) dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial(serial_ports); return NULL; } - #endif /* HAVE_PTHREAD */ -static void show_usage() +static void show_usage(void) { /* NOTE: This code uses `nutscan_avail_*` global vars from nutscan-init.c */ puts("nut-scanner : utility for detection of available power devices.\n"); + + nut_report_config_flags(); + puts("OPTIONS:"); printf(" -C, --complete_scan: Scan all available devices except serial ports (default).\n"); if (nutscan_avail_usb) { - printf(" -U, --usb_scan: Scan USB devices.\n"); + printf(" -U, --usb_scan: Scan USB devices. Specify twice or more to report different\n" + " detail levels of (change-prone) physical properties.\n" + " This usage can be combined with '-C' or other scan types.\n"); } else { printf("* Options for USB devices scan not enabled: library not detected.\n"); } @@ -242,6 +310,7 @@ static void show_usage() } else { printf("* Options for NUT devices (avahi method) scan not enabled: library not detected.\n"); } + printf(" -n, --nut_simulation_scan: Scan for NUT simulated devices (.dev files in $CONFPATH).\n"); if (nutscan_avail_ipmi) { printf(" -I, --ipmi_scan: Scan IPMI devices.\n"); } else { @@ -251,11 +320,15 @@ static void show_usage() printf(" -E, --eaton_serial : Scan serial Eaton devices (XCP, SHUT and Q1).\n"); #if (defined HAVE_PTHREAD) && (defined HAVE_PTHREAD_TRYJOIN) - printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (default: %zu).\n", max_threads); + printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (default: %" PRIuSIZE ").\n", max_threads); #else - printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)"); + printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)\n"); #endif + printf("\nNote: many scanning options depend on further loadable libraries.\n"); + /* Note: if debug is enabled, this is prefixed with timestamps */ + upsdebugx_report_search_paths(0, 0); + printf("\nNetwork specific options:\n"); printf(" -t, --timeout : network operation timeout (default %d).\n", DEFAULT_NETWORK_TIMEOUT); printf(" -s, --start_ip : First IP address to scan.\n"); @@ -372,9 +445,11 @@ static void show_usage() printf("\nNUT specific options:\n"); printf(" -p, --port : Port number of remote NUT upsd\n"); printf("\ndisplay specific options:\n"); + printf(" -Q, --disp_nut_conf_with_sanity_check: Display result in the ups.conf format with sanity-check warnings as comments (default)\n"); printf(" -N, --disp_nut_conf: Display result in the ups.conf format\n"); printf(" -P, --disp_parsable: Display result in a parsable format\n"); printf("\nMiscellaneous options:\n"); + printf(" -h, --help: display this help text\n"); printf(" -V, --version: Display NUT version\n"); printf(" -a, --available: Display available bus that can be scanned\n"); printf(" -q, --quiet: Display only scan result. No information on currently scanned bus is displayed.\n"); @@ -393,12 +468,18 @@ int main(int argc, char *argv[]) int allow_snmp = 0; int allow_xml = 0; int allow_oldnut = 0; + int allow_nut_simulation = 0; int allow_avahi = 0; int allow_ipmi = 0; int allow_eaton_serial = 0; /* MUST be requested explicitly! */ int quiet = 0; /* The debugging level for certain upsdebugx() progress messages; 0 = print always, quiet==1 is to require at least one -D */ void (*display_func)(nutscan_device_t * device); int ret_code = EXIT_SUCCESS; +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_t *current_sem; +# endif +#endif #if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) && (defined HAVE_SYS_RESOURCE_H) struct rlimit nofile_limit; @@ -456,7 +537,8 @@ int main(int argc, char *argv[]) nutscan_init(); - display_func = nutscan_display_ups_conf; + /* Default, see -Q/-N/-P below */ + display_func = nutscan_display_ups_conf_with_sanity_check; /* Parse command line options -- Second loop: everything else */ /* Restore error messages... */ @@ -633,10 +715,10 @@ int main(int argc, char *argv[]) && (uintmax_t)val > (uintmax_t)(nofile_limit.rlim_cur - RESERVE_FD_COUNT) ) { upsdebugx(1, "Detected soft limit for " - "file descriptor count is %ju", + "file descriptor count is %" PRIuMAX, (uintmax_t)nofile_limit.rlim_cur); upsdebugx(1, "Detected hard limit for " - "file descriptor count is %ju", + "file descriptor count is %" PRIuMAX, (uintmax_t)nofile_limit.rlim_max); max_threads = (size_t)nofile_limit.rlim_cur; @@ -649,7 +731,7 @@ int main(int argc, char *argv[]) "thread count %s (%ld) exceeds the " "current file descriptor count limit " "(minus reservation), constraining " - "to %zu\n", + "to %" PRIuSIZE "\n", optarg, val, max_threads); } else # endif /* HAVE_SYS_RESOURCE_H */ @@ -658,7 +740,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "WARNING: Requested max scanning " "thread count %s (%ld) is out of range, " - "using default %zu\n", + "using default %" PRIuSIZE "\n", optarg, val, max_threads); } #else @@ -676,6 +758,10 @@ int main(int argc, char *argv[]) goto display_help; } allow_usb = 1; + /* NOTE: Starts as -1, so the first -U sets it to 0 + * (minimal detail); further -U can bump it */ + if (cli_link_detail_level < 3) + cli_link_detail_level++; break; case 'M': if (!nutscan_avail_xml_http) { @@ -692,12 +778,18 @@ int main(int argc, char *argv[]) } allow_avahi = 1; break; + case 'n': + allow_nut_simulation = 1; + break; case 'I': if (!nutscan_avail_ipmi) { goto display_help; } allow_ipmi = 1; break; + case 'Q': + display_func = nutscan_display_ups_conf_with_sanity_check; + break; case 'N': display_func = nutscan_display_ups_conf; break; @@ -709,6 +801,7 @@ int main(int argc, char *argv[]) break; case 'V': printf("Network UPS Tools - %s\n", NUT_VERSION_MACRO); + nut_report_config_flags(); exit(EXIT_SUCCESS); case 'a': printf("OLDNUT\n"); @@ -753,7 +846,7 @@ int main(int argc, char *argv[]) /* FIXME: Currently sem_init already done on nutscan-init for lib need. We need to destroy it before re-init. We currently can't change "sem value" on lib (need to be thread safe). */ - sem_t *current_sem = nutscan_semaphore(); + current_sem = nutscan_semaphore(); sem_destroy(current_sem); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic push @@ -787,7 +880,7 @@ int main(int argc, char *argv[]) upsdebugx(1, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); } - if (!allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && + if (!allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && !allow_nut_simulation && !allow_avahi && !allow_ipmi && !allow_eaton_serial ) { allow_all = 1; @@ -795,9 +888,15 @@ int main(int argc, char *argv[]) if (allow_all) { allow_usb = 1; + /* NOTE: Starts as -1, so when we scan everything - set + * it to 0 (minimal detail); further -U can bump it */ + if (cli_link_detail_level < 0) + cli_link_detail_level++; + allow_snmp = 1; allow_xml = 1; allow_oldnut = 1; + allow_nut_simulation = 1; allow_avahi = 1; allow_ipmi = 1; /* BEWARE: allow_all does not include allow_eaton_serial! */ @@ -809,13 +908,13 @@ int main(int argc, char *argv[]) if (allow_usb && nutscan_avail_usb) { upsdebugx(quiet, "Scanning USB bus."); #ifdef HAVE_PTHREAD - if (pthread_create(&thread[TYPE_USB], NULL, run_usb, NULL)) { + if (pthread_create(&thread[TYPE_USB], NULL, run_usb, &cli_link_detail_level)) { upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_usb = 0; } #else upsdebugx(1, "USB SCAN: no pthread support, starting nutscan_scan_usb..."); - dev[TYPE_USB] = nutscan_scan_usb(); + dev[TYPE_USB] = nutscan_scan_usb(&cli_link_detail_level); #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "USB SCAN: not requested, SKIPPED"); @@ -895,6 +994,22 @@ int main(int argc, char *argv[]) upsdebugx(1, "NUT bus (old) SCAN: not requested, SKIPPED"); } + if (allow_nut_simulation && nutscan_avail_nut_simulation) { + upsdebugx(quiet, "Scanning NUT simulation devices."); +#ifdef HAVE_PTHREAD + upsdebugx(1, "NUT simulation devices SCAN: starting pthread_create with run_nut_simulation..."); + if (pthread_create(&thread[TYPE_NUT_SIMULATION], NULL, run_nut_simulation, NULL)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); + nutscan_avail_nut_simulation = 0; + } +#else + upsdebugx(1, "NUT simulation devices SCAN: no pthread support, starting nutscan_scan_nut_simulation..."); + dev[TYPE_NUT_SIMULATION] = nutscan_scan_nut_simulation(timeout); +#endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "NUT simulation devices SCAN: not requested, SKIPPED"); + } + if (allow_avahi && nutscan_avail_avahi) { upsdebugx(quiet, "Scanning NUT bus (avahi method)."); #ifdef HAVE_PTHREAD @@ -961,6 +1076,10 @@ int main(int argc, char *argv[]) upsdebugx(1, "NUT bus (old) SCAN: join back the pthread"); pthread_join(thread[TYPE_NUT], NULL); } + if (allow_nut_simulation && nutscan_avail_nut_simulation && thread[TYPE_NUT_SIMULATION]) { + upsdebugx(1, "NUT simulation devices SCAN: join back the pthread"); + pthread_join(thread[TYPE_NUT_SIMULATION], NULL); + } if (allow_avahi && nutscan_avail_avahi && thread[TYPE_AVAHI]) { upsdebugx(1, "NUT bus (avahi) SCAN: join back the pthread"); pthread_join(thread[TYPE_AVAHI], NULL); @@ -997,6 +1116,11 @@ int main(int argc, char *argv[]) upsdebugx(1, "SCANS DONE: free resources: NUT bus (old)"); nutscan_free_device(dev[TYPE_NUT]); + upsdebugx(1, "SCANS DONE: display results: NUT simulation devices"); + display_func(dev[TYPE_NUT_SIMULATION]); + upsdebugx(1, "SCANS DONE: free resources: NUT simulation devices"); + nutscan_free_device(dev[TYPE_NUT_SIMULATION]); + upsdebugx(1, "SCANS DONE: display results: NUT bus (avahi)"); display_func(dev[TYPE_AVAHI]); upsdebugx(1, "SCANS DONE: free resources: NUT bus (avahi)"); diff --git a/tools/nut-scanner/nutscan-device.c b/tools/nut-scanner/nutscan-device.c index bde57fed71..c01e229b98 100644 --- a/tools/nut-scanner/nutscan-device.c +++ b/tools/nut-scanner/nutscan-device.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2024 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify @@ -19,6 +20,7 @@ /*! \file nutscan-device.c \brief manipulation of a container describing a NUT device \author Frederic Bohe + \author Arnaud Quette */ #include "config.h" /* must be the first header */ @@ -27,17 +29,32 @@ #include #include -const char * nutscan_device_type_strings[TYPE_END - 1] = { +const char * nutscan_device_type_strings[TYPE_END] = { + "NONE", /* 0 */ "USB", "SNMP", "XML", "NUT", + "NUT_SIMULATION", "IPMI", "Avahi", "serial", }; -nutscan_device_t * nutscan_new_device() +/* lower strings, used for device names */ +const char * nutscan_device_type_lstrings[TYPE_END] = { + "none", /* 0 */ + "usb", + "snmp", + "xml", + "nut", + "simulation", + "ipmi", + "avahi", + "serial", +}; + +nutscan_device_t * nutscan_new_device(void) { nutscan_device_t * device; @@ -77,6 +94,10 @@ static void deep_free_device(nutscan_device_t * device) free(current->value); } + if (current->comment_tag != NULL) { + free(current->comment_tag); + } + free(current); } @@ -106,6 +127,11 @@ void nutscan_free_device(nutscan_device_t * device) } void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char * value) +{ + nutscan_add_commented_option_to_device(device, option, value, NULL); +} + +void nutscan_add_commented_option_to_device(nutscan_device_t * device, char * option, char * value, char * comment_tag) { nutscan_options_t **opt; @@ -135,6 +161,13 @@ void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char else { (*opt)->value = NULL; } + + if (comment_tag != NULL) { + (*opt)->comment_tag = strdup(comment_tag); + } + else { + (*opt)->comment_tag = NULL; + } } nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second) diff --git a/tools/nut-scanner/nutscan-device.h b/tools/nut-scanner/nutscan-device.h index 902339494d..4ab9bc714b 100644 --- a/tools/nut-scanner/nutscan-device.h +++ b/tools/nut-scanner/nutscan-device.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2024 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify @@ -19,6 +20,7 @@ /*! \file nutscan-device.h \brief definition of a container describing a NUT discovered device \author Frederic Bohe + \author Arnaud Quette */ #ifndef SCAN_DEVICE @@ -39,6 +41,8 @@ extern "C" { */ #define nutscan_device_type_string(type) \ (assert(0 < (type) && (type) < TYPE_END), nutscan_device_type_strings[type - 1]) +#define nutscan_device_type_lstring(type) \ + (assert(0 < (type) && (type) < TYPE_END), nutscan_device_type_lstrings[type - 1]) typedef enum nutscan_device_type { TYPE_NONE = 0, @@ -46,6 +50,7 @@ typedef enum nutscan_device_type { TYPE_SNMP, TYPE_XML, TYPE_NUT, + TYPE_NUT_SIMULATION, TYPE_IPMI, TYPE_AVAHI, TYPE_EATON_SERIAL, @@ -53,17 +58,20 @@ typedef enum nutscan_device_type { } nutscan_device_type_t; /** Device type -> string mapping */ -extern const char * nutscan_device_type_strings[TYPE_END - 1]; +extern const char * nutscan_device_type_strings[TYPE_END]; +extern const char * nutscan_device_type_lstrings[TYPE_END]; typedef struct nutscan_options { char * option; char * value; + char * comment_tag; /* if not NULL, this option may be not shown in some output formats, or represented as a comment with this tag in others (and empty string may also be used) */ struct nutscan_options* next; } nutscan_options_t; typedef struct nutscan_device { nutscan_device_type_t type; char * driver; + char * alt_driver_names; char * port; nutscan_options_t * opt; struct nutscan_device * prev; @@ -72,6 +80,8 @@ typedef struct nutscan_device { nutscan_device_t * nutscan_new_device(void); void nutscan_free_device(nutscan_device_t * device); +void nutscan_add_commented_option_to_device(nutscan_device_t * device, char * option, char * value, char * comment_tag); +/* This method calls the one above, using a NULL comment_tag */ void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char * value); nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); diff --git a/tools/nut-scanner/nutscan-display.c b/tools/nut-scanner/nutscan-display.c index 71f2248b15..bae9824bbb 100644 --- a/tools/nut-scanner/nutscan-display.c +++ b/tools/nut-scanner/nutscan-display.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 - EATON + * 2023 - 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 @@ -25,6 +26,7 @@ #include #include "nutscan-device.h" #include "nut-scan.h" +#include "nut_stdint.h" static char * nutscan_device_type_string[TYPE_END] = { "NONE", @@ -37,11 +39,38 @@ static char * nutscan_device_type_string[TYPE_END] = { "EATON_SERIAL" }; +/* Counter incremented by nutscan_display_ups_conf(); later maybe adding other + * renderer methods which could be interested in uniquely numbering the device + * configurations. Note that this is a monotonously increasing number suffixed + * to "nutdev-". Naming of this sort is NOT part of nutscan_device_t or + * nutscan_options_t, it is defined by/appears in the renderer alone so far. + */ +static size_t last_nutdev_num = 0; + +void nutscan_display_ups_conf_with_sanity_check(nutscan_device_t * device) +{ + /* Note: while a single device is passed to the method, it is actually + * used to locate the list of related device types and iterate it all. + */ + upsdebugx(2, "%s: %s", __func__, device + ? (device->type < TYPE_END ? nutscan_device_type_string[device->type] : "") + : ""); + nutscan_display_ups_conf(device); + nutscan_display_sanity_check(device); +} + void nutscan_display_ups_conf(nutscan_device_t * device) { + /* Note: while a single device is passed to the method, it is actually + * used to locate the list of related device types and iterate it all. + */ nutscan_device_t * current_dev = device; nutscan_options_t * opt; - static int nutdev_num = 1; + static size_t nutdev_num = 1; + + upsdebugx(2, "%s: %s", __func__, device + ? (device->type < TYPE_END ? nutscan_device_type_string[device->type] : "") + : ""); if (device == NULL) { return; @@ -54,15 +83,31 @@ void nutscan_display_ups_conf(nutscan_device_t * device) /* Display each device */ do { - printf("[nutdev%i]\n\tdriver = \"%s\"\n\tport = \"%s\"\n", - nutdev_num, current_dev->driver, - current_dev->port); + printf("[nutdev-%s%" PRIuSIZE "]\n\tdriver = \"%s\"", + nutscan_device_type_lstrings[current_dev->type], + nutdev_num, current_dev->driver); + + if (current_dev->alt_driver_names) { + printf("\t# alternately: %s", + current_dev->alt_driver_names); + } + + printf("\n\tport = \"%s\"\n", + current_dev->port); opt = current_dev->opt; while (NULL != opt) { if (opt->option != NULL) { - printf("\t%s", opt->option); + printf("\t"); + if (opt->comment_tag) { + if (opt->comment_tag[0] == '\0') { + printf("# "); + } else { + printf("###%s### ", opt->comment_tag); + } + } + printf("%s", opt->option); if (opt->value != NULL) { printf(" = \"%s\"", opt->value); } @@ -76,13 +121,22 @@ void nutscan_display_ups_conf(nutscan_device_t * device) current_dev = current_dev->next; } while (current_dev != NULL); + + last_nutdev_num = nutdev_num; } void nutscan_display_parsable(nutscan_device_t * device) { + /* Note: while a single device is passed to the method, it is actually + * used to locate the list of related device types and iterate it all. + */ nutscan_device_t * current_dev = device; nutscan_options_t * opt; + upsdebugx(2, "%s: %s", __func__, device + ? (device->type < TYPE_END ? nutscan_device_type_string[device->type] : "") + : ""); + if (device == NULL) { return; } @@ -103,7 +157,7 @@ void nutscan_display_parsable(nutscan_device_t * device) opt = current_dev->opt; while (NULL != opt) { - if (opt->option != NULL) { + if (opt->option != NULL && opt->comment_tag == NULL) { /* Do not separate by whitespace, in case someone already parses this */ printf(",%s", opt->option); if (opt->value != NULL) { @@ -113,9 +167,241 @@ void nutscan_display_parsable(nutscan_device_t * device) opt = opt->next; } + /* NOTE: Currently no handling for current_dev->alt_driver_names + * here, since no driver options maps to this concept */ + printf("\n"); current_dev = current_dev->next; } while (current_dev != NULL); } + +/* TODO: If this is ever a memory-pressure problem, + * e.g. if preparing to monitor hundreds of devices, + * can convert to dynamically allocated (and freed) + * strings. For now go for speed with static arrays. + */ +typedef struct keyval_strings { + char key[SMALLBUF]; + char val[LARGEBUF]; +} keyval_strings_t; + +void nutscan_display_sanity_check_serial(nutscan_device_t * device) +{ + /* Note: while a single device is passed to the method, it is actually + * used to locate the list of related device types and iterate it all. + */ + /* Some devices have useless serial numbers + * (empty strings, all-zeroes, all-spaces etc.) + * and others have identical serial numbers on + * physically different hardware units. + * Warn about these as a possible problem e.g. + * for matching and discerning devices generally. + * Note that we may also have multiple data paths + * to the same device (e.g. monitored over USB + * and SNMP, so the situation is not necessarily + * a problem). + * Also note that not all devices may have/report + * a serial at all (option will be missing). + */ + /* FIXME: Currently this is normally called as part + * of nutscan_display_ups_conf_with_sanity_check() + * and nut-scanner goes over each type separately. + * So with current approach it will not see "issues" + * with multiple data paths (e.g. USB and SNMP) to + * same device. + */ + nutscan_device_t * current_dev = device; + nutscan_options_t * opt; + /* Keep numbering consistent with global entry naming + * as we call the device config renderer and the sanity + * check collector for the same device, then iterate. + * Note its last value is after loop, so says "real + 1". + * Starts with "1", so a discovery of 1 device yields a + * "last_nutdev_num==2" value here. We would rewind the + * number to start at the beginning of the list for the + * current device type. + * FIXME: It now feels beneficial to add a "nutdev_name" + * right into the nutscan_device_t or its "opt" and to + * pre-parse all discovered lists of devices before any + * rendering to consistently pre-set these strings. + */ + size_t nutdev_num = last_nutdev_num - 1; + size_t listlen = 0, count = 0, i; + keyval_strings_t *map = NULL, *entry = NULL; + + upsdebugx(2, "%s: %s", __func__, device + ? (device->type < TYPE_END ? nutscan_device_type_string[device->type] : "") + : ""); + + if (device == NULL) { + return; + } + + /* At least one entry exists, this one (note the 1-based count!)... */ + listlen++; + + /* Find end of the list */ + while (current_dev->next != NULL) { + current_dev = current_dev->next; + } + + /* Find start of the list and count its size */ + while (current_dev->prev != NULL) { + current_dev = current_dev->prev; + nutdev_num--; + listlen++; + } + + /* Process each device: + * Build a map of "serial"=>"nutdev-serialX[,...,nutdev-serialZ]" + * and warn if there are bogus "serial" keys or if + * there are several nutdev's (a comma in value). + */ + + /* Reserve enough slots for all-unique serials */ + map = calloc(sizeof(keyval_strings_t), listlen); + if (map == NULL) { + fprintf(stderr, "%s: Memory allocation error, skipped\n", __func__); + return; + } + + upsdebugx(3, "%s: checking serial numbers for %" PRIuSIZE " device configuration(s)", + __func__, listlen); + + /* NOTE: we start the loop with current_dev == first device in list */ + do { + /* Look for serial option in current device (iterated) */ + char nutdev_name[SMALLBUF]; + opt = current_dev->opt; + snprintf(nutdev_name, sizeof(nutdev_name), "nutdev-%s%" PRIuSIZE, + nutscan_device_type_lstrings[current_dev->type], + nutdev_num); + + while (NULL != opt) { + if (opt->option != NULL && !strcmp(opt->option, "serial")) { + /* This nutdevX has a serial; is it in map already? */ + char keytmp[SMALLBUF]; + snprintf(keytmp, sizeof(keytmp), "%s", + opt->value ? (opt->value[0] ? opt->value : "") : ""); + + for (i = 0, entry = NULL; i < listlen && map[i].key[0] != '\0'; i++) { + if (!strncmp(map[i].key, keytmp, sizeof(map[i].key))) { + entry = &(map[i]); + break; + } + } + + if (entry) { + /* Got a hit => append value */ + upsdebugx(3, "%s: duplicate entry for serial '%s' of '%s': %s", + __func__, keytmp, nutdev_name, entry->val); + + /* TODO: If changing from preallocated LARGEBUF to + * dynamic allocation, malloc data for larger "val". + */ + snprintfcat(entry->val, sizeof(entry->val), + ",%s", nutdev_name); + } else { + /* No hit => new key */ + upsdebugx(3, "%s: new entry for serial '%s' of %s", + __func__, keytmp, nutdev_name); + + /* TODO: If changing from preallocated LARGEBUF to + * dynamic allocation, malloc data for new "entry" + * and its key/val fields. + */ + entry = &(map[i]); + + count++; + if (count != (i + 1) || count > listlen) { + /* Should never get here, but just in case... */ + fprintf(stderr, "%s: Loop overflow, skipped\n", __func__); + upsdebugx(3, "%s: count=%" PRIuSIZE " i=%" PRIuSIZE " listlen%" PRIuSIZE, + __func__, count, i, listlen); + goto exit; + } + + snprintf(entry->key, sizeof(entry->key), + "%s", keytmp); + snprintf(entry->val, sizeof(entry->val), + "%s", nutdev_name); + } + + /* Abort the opt-searching loop for this device */ + goto next; + } + opt = opt->next; + } + +next: + nutdev_num++; + + current_dev = current_dev->next; + } + while (current_dev != NULL); + + if (!count) { + /* No serials in found devices? Oh well */ + goto exit; + } + + /* Now look for red flags in the map (key=sernum, val=device(s)) */ + /* FIXME: Weed out special chars to avoid breaking comment-line markup? + * Thinking of ASCII control codes < 32 including CR/LF, and codes 128+... */ + for (i = 0; i < count; i++) { + size_t j; + entry = &(map[i]); + + /* NULL or empty serials */ + if (!strcmp(entry->key, "") || !strcmp(entry->key, "")) { + printf("\n# WARNING: %s \"serial\" reported in some devices: %s\n", + entry->key, entry->val); + continue; + } + + j = strlen(entry->key); + if (j > 0 && (entry->key[j-1] == '\t' || entry->key[j-1] == ' ')) { + printf("\n# WARNING: trailing blank space in \"serial\" " + "value \"%s\" reported in device configuration(s): %s", + entry->key, entry->val); + } + + /* All chars in "serial" are same (zero, space, etc.) */ + for (j = 0; entry->key[j] != '\0' && entry->key[j] == entry->key[0]; j++); + if (j > 0 && entry->key[j] == '\0') { + printf("\n# WARNING: all-same character \"serial\" " + "with %" PRIuSIZE " copies of '%c' (0x%02X) " + "reported in some devices: %s\n", + j, entry->key[0], entry->key[0], entry->val); + } + + /* Duplicates (maybe same device, maybe not) - see if val has a ',' */ + for (j = 0; entry->val[j] != '\0' && entry->val[j] != ','; j++); + if (j > 0 && entry->val[j] != '\0') { + printf("\n# WARNING: same \"serial\" value \"%s\" " + "reported in several device configurations " + "(maybe okay if multiple drivers for same device, " + "likely a vendor bug if reported by same driver " + "for many devices): %s\n", + entry->key, entry->val); + } + } + +exit: + free (map); +} + +void nutscan_display_sanity_check(nutscan_device_t * device) +{ + /* Note: while a single device is passed to the method, it is actually + * used to locate the list of related device types and iterate it all. + */ + upsdebugx(2, "%s: %s", __func__, device + ? (device->type < TYPE_END ? nutscan_device_type_string[device->type] : "") + : ""); + + /* Extend the list later as more sanity-checking appears */ + nutscan_display_sanity_check_serial(device); +} diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index 9ea3f5d3ef..11efb6c894 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2024 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011-2021 - EATON * * This program is free software; you can redistribute it and/or modify @@ -20,6 +21,7 @@ \brief init functions for nut scanner library \author Frederic Bohe \author Arnaud Quette + \author Arnaud Quette */ #include "common.h" @@ -30,10 +32,21 @@ #include #include "nut-scan.h" +#ifndef WIN32 +#define SOEXT ".so" +#else +#define SOEXT ".dll" +#endif + int nutscan_avail_avahi = 0; int nutscan_avail_ipmi = 0; int nutscan_avail_nut = 0; +int nutscan_avail_nut_simulation = 1; +#ifdef WITH_SNMP_STATIC +int nutscan_avail_snmp = 1; +#else int nutscan_avail_snmp = 0; +#endif int nutscan_avail_usb = 0; int nutscan_avail_xml_http = 0; @@ -87,8 +100,25 @@ size_t max_threads_netsnmp = 0; /* 10240; */ #endif /* HAVE_PTHREAD */ +#ifdef WIN32 +/* Stub for libupsclient */ +void do_upsconf_args(char *confupsname, char *var, char *val) { + NUT_UNUSED_VARIABLE(confupsname); + NUT_UNUSED_VARIABLE(var); + NUT_UNUSED_VARIABLE(val); +} +#endif + void nutscan_init(void) { + char *libname = NULL; + + /* Optional filter to not walk things twice */ + nut_prepare_search_paths(); + + /* Report library paths we would search, at given debug verbosity level */ + upsdebugx_report_search_paths(1, 1); + #ifdef HAVE_PTHREAD /* TOTHINK: Should semaphores to limit thread count * and the more naive but portable methods be an @@ -128,60 +158,213 @@ void nutscan_init(void) # ifdef HAVE_PTHREAD_TRYJOIN pthread_mutex_init(&threadcount_mutex, NULL); # endif -#endif /* HAVE_PTHREAD */ +#endif /* HAVE_PTHREAD */ - char *libname = NULL; #ifdef WITH_USB #if WITH_LIBUSB_1_0 - libname = get_libname("libusb-1.0.so"); + libname = get_libname("libusb-1.0" SOEXT); #else - libname = get_libname("libusb-0.1.so"); + libname = get_libname("libusb-0.1" SOEXT); + #ifdef WIN32 + /* TODO: Detect DLL name at build time, or rename it at install time? */ + /* libusb-compat built for mingw per NUT instructions */ if (!libname) { - /* We can also use libusb-compat from newer libusb-1.0 releases */ - libname = get_libname("libusb.so"); + libname = get_libname("libusb-0-1-4" SOEXT); } + #endif #endif + if (!libname) { + /* We can also use libusb-compat from newer libusb-1.0 releases */ + libname = get_libname("libusb" SOEXT); + } if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "LibUSB"); nutscan_avail_usb = nutscan_load_usb_library(libname); free(libname); + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "LibUSB"); + #if WITH_LIBUSB_1_0 + nutscan_avail_usb = nutscan_load_usb_library("libusb-1.0" SOEXT); + #else + nutscan_avail_usb = nutscan_load_usb_library("libusb-0.1" SOEXT); + #ifdef WIN32 + if (!nutscan_avail_usb) { + nutscan_avail_usb = nutscan_load_usb_library("libusb-0-1-4" SOEXT); + } + #endif + #endif + if (!nutscan_avail_usb) { + nutscan_avail_usb = nutscan_load_usb_library("libusb" SOEXT); + } } -#endif + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_usb ? "succeeded" : "failed", "LibUSB"); +#else + upsdebugx(1, "%s: skipped loading the library for %s: was absent during NUT build", + __func__, "LibUSB"); +#endif /* WITH_USB */ + #ifdef WITH_SNMP - libname = get_libname("libnetsnmp.so"); + libname = get_libname("libnetsnmp" SOEXT); + #ifdef WIN32 + if (!libname) { + libname = get_libname("libnetsnmp-40" SOEXT); + } + #endif if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "LibSNMP"); nutscan_avail_snmp = nutscan_load_snmp_library(libname); free(libname); - } + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "LibSNMP"); + nutscan_avail_snmp = nutscan_load_snmp_library("libnetsnmp" SOEXT); +#ifdef WIN32 + if (!nutscan_avail_snmp) { + nutscan_avail_snmp = nutscan_load_snmp_library("libnetsnmp-40" SOEXT); + } #endif + } + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_snmp ? "succeeded" : "failed", "LibSNMP"); +#else + upsdebugx(1, "%s: skipped loading the library for %s: was absent during NUT build", + __func__, "LibSNMP"); +#endif /* WITH_SNMP */ + #ifdef WITH_NEON - libname = get_libname("libneon.so"); + libname = get_libname("libneon" SOEXT); + if (!libname) { + libname = get_libname("libneon-gnutls" SOEXT); + } + #ifdef WIN32 if (!libname) { - libname = get_libname("libneon-gnutls.so"); + libname = get_libname("libneon-27" SOEXT); } + if (!libname) { + libname = get_libname("libneon-gnutls-27" SOEXT); + } + #endif if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "LibNeon"); nutscan_avail_xml_http = nutscan_load_neon_library(libname); free(libname); - } + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "LibNeon"); + nutscan_avail_xml_http = nutscan_load_neon_library("libneon" SOEXT); + if (!nutscan_avail_xml_http) { + nutscan_avail_xml_http = nutscan_load_neon_library("libneon-gnutls" SOEXT); + } +#ifdef WIN32 + if (!nutscan_avail_xml_http) { + nutscan_avail_xml_http = nutscan_load_neon_library("libneon-27" SOEXT); + } + if (!nutscan_avail_xml_http) { + nutscan_avail_xml_http = nutscan_load_neon_library("libneon-gnutls-27" SOEXT); + } #endif + } + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_xml_http ? "succeeded" : "failed", "LibNeon"); +#else + upsdebugx(1, "%s: skipped loading the library for %s: was absent during NUT build", + __func__, "LibNeon"); +#endif /* WITH_NEON */ + #ifdef WITH_AVAHI - libname = get_libname("libavahi-client.so"); + libname = get_libname("libavahi-client" SOEXT); if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "LibAvahi"); nutscan_avail_avahi = nutscan_load_avahi_library(libname); free(libname); + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "LibAvahi"); + nutscan_avail_avahi = nutscan_load_avahi_library("libavahi-client" SOEXT); } -#endif + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_avahi ? "succeeded" : "failed", "LibAvahi"); +#else + upsdebugx(1, "%s: skipped loading the library for %s: was absent during NUT build", + __func__, "LibAvahi"); +#endif /* WITH_AVAHI */ + #ifdef WITH_FREEIPMI - libname = get_libname("libfreeipmi.so"); + libname = get_libname("libfreeipmi" SOEXT); if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "LibFreeIPMI"); nutscan_avail_ipmi = nutscan_load_ipmi_library(libname); free(libname); + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "LibFreeIPMI"); + nutscan_avail_ipmi = nutscan_load_ipmi_library("libfreeipmi" SOEXT); + } + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_ipmi ? "succeeded" : "failed", "LibFreeIPMI"); +#else + upsdebugx(1, "%s: skipped loading the library for %s: was absent during NUT build", + __func__, "LibFreeIPMI"); +#endif /* WITH_FREEIPMI */ + +/* start of libupsclient for "old NUT" (vs. Avahi) protocol - unconditional */ + libname = get_libname("libupsclient" SOEXT); +#ifdef WIN32 + /* TODO: Detect DLL name at build time, or rename it at install time? */ + /* e.g. see clients/Makefile.am for version-info value */ + if (!libname) { + libname = get_libname("libupsclient-6" SOEXT); + } + if (!libname) { + libname = get_libname("libupsclient-3" SOEXT); } #endif - libname = get_libname("libupsclient.so"); if (libname) { + upsdebugx(1, "%s: get_libname() resolved '%s' for %s, loading it", + __func__, libname, "NUT Client library"); nutscan_avail_nut = nutscan_load_upsclient_library(libname); free(libname); + } else { + /* let libtool (lt_dlopen) do its default magic maybe better */ + upsdebugx(1, "%s: get_libname() did not resolve libname for %s, " + "trying to load it with libtool default resolver", + __func__, "NUT Client library"); + nutscan_avail_nut = nutscan_load_upsclient_library("libupsclient" SOEXT); +#ifdef WIN32 + if (!nutscan_avail_nut) { + nutscan_avail_nut = nutscan_load_upsclient_library("libupsclient-6" SOEXT); + } + if (!nutscan_avail_nut) { + nutscan_avail_nut = nutscan_load_upsclient_library("libupsclient-3" SOEXT); + } +#endif } + upsdebugx(1, "%s: %s to load the library for %s", + __func__, nutscan_avail_nut ? "succeeded" : "failed", "NUT Client library"); +/* end of libupsclient for "old NUT" (vs. Avahi) protocol */ + + +/* start of "NUT Simulation" - unconditional */ +/* no need for additional library */ + nutscan_avail_nut = 1; + } void nutscan_free(void) diff --git a/tools/nut-scanner/nutscan-init.h b/tools/nut-scanner/nutscan-init.h index 4ff5f39d16..b9531b1cdc 100644 --- a/tools/nut-scanner/nutscan-init.h +++ b/tools/nut-scanner/nutscan-init.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2024 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify @@ -19,6 +20,7 @@ /*! \file nutscan-init.h \brief initialisation data \author Frederic Bohe + \author Arnaud Quette */ #ifndef SCAN_INIT @@ -33,6 +35,7 @@ extern "C" { extern int nutscan_avail_avahi; extern int nutscan_avail_ipmi; extern int nutscan_avail_nut; +extern int nutscan_avail_nut_simulation; extern int nutscan_avail_snmp; extern int nutscan_avail_usb; extern int nutscan_avail_xml_http; diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index f064534cd2..4223bfa868 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -23,12 +23,24 @@ #include "config.h" /* must be first */ +#include "nut_stdint.h" +#include "common.h" #include "nutscan-ip.h" #include -#include "common.h" #include -#include -#include +#ifndef WIN32 +# include +# include +#else +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +# include +# include +# ifndef AI_NUMERICSERV +# define AI_NUMERICSERV NI_NUMERICSERV +# endif +# include "wincompat.h" +#endif static void increment_IPv6(struct in6_addr * addr) { @@ -257,6 +269,7 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) nutscan_ip_iter_t ip; int mask_val; int mask_byte; + int ret; uint32_t mask_bit; /* 32-bit IPv4 address bitmask */ char host[SMALLBUF]; struct addrinfo hints; @@ -266,6 +279,9 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) *start_ip = NULL; *stop_ip = NULL; +#ifdef WIN32 + WSADATA WSAdata; +#endif cidr_tok = strdup(cidr); first_ip = strdup(strtok_r(cidr_tok, "/", &saveptr)); @@ -307,13 +323,23 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) hints.ai_family = AF_INET; ip.type = IPv4; - /* Detecting IPv4 vs IPv6 */ - if (getaddrinfo(first_ip, NULL, &hints, &res) != 0) { - /*Try IPv6 detection */ + +#ifdef WIN32 + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + + if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { + /* EAI_ADDRFAMILY? */ + upsdebugx(5, "%s: getaddrinfo() failed for AF_INET (IPv4): %d", + __func__, ret); + + /* Try IPv6 detection */ ip.type = IPv6; hints.ai_family = AF_INET6; - int ret; if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { + upsdebugx(5, "%s: getaddrinfo() failed for AF_INET6 (IPv6): %d", + __func__, ret); free(first_ip); return 0; } diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index 6fa868db80..8cdf6d8498 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -24,8 +24,14 @@ #ifndef SCAN_IP #define SCAN_IP +#ifndef WIN32 #include #include +#else +#include +#include +#include +#endif #ifdef __cplusplus /* *INDENT-OFF* */ diff --git a/tools/nut-scanner/nutscan-serial.c b/tools/nut-scanner/nutscan-serial.c index bf4ac29ca2..a98b4dcb29 100644 --- a/tools/nut-scanner/nutscan-serial.c +++ b/tools/nut-scanner/nutscan-serial.c @@ -30,6 +30,9 @@ #include #include "nut_platform.h" #include "common.h" +#ifdef WIN32 +#include "wincompat.h" +#endif #ifdef WIN32 /* Windows: all serial port names start with "COM" */ @@ -217,4 +220,3 @@ char ** nutscan_get_serial_ports_list(const char *ports_range) } return ports_list; } - diff --git a/tools/nut-scanner/nutscan-snmp.h b/tools/nut-scanner/nutscan-snmp.h index 487581bd11..7d5898b632 100644 --- a/tools/nut-scanner/nutscan-snmp.h +++ b/tools/nut-scanner/nutscan-snmp.h @@ -2,6 +2,7 @@ * Copyright (C) 2011 - Frederic Bohe * Copyright (C) 2016 - Arnaud Quette * Copyright (C) 2016 - Jim Klimov + * Copyright (C) 2024 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 @@ -45,7 +46,7 @@ typedef struct { extern "C" { #endif -#if WANT_DEVSCAN_SNMP_BUILTIN == 1 +#if defined WANT_DEVSCAN_SNMP_BUILTIN && WANT_DEVSCAN_SNMP_BUILTIN == 1 # ifndef DEVSCAN_SNMP_BUILTIN # define DEVSCAN_SNMP_BUILTIN /* Can use a copy of the structure that was pre-compiled into the binary */ @@ -53,7 +54,7 @@ extern "C" { # endif /* DEVSCAN_SNMP_BUILTIN */ #endif /* WANT_DEVSCAN_SNMP_BUILTIN */ -#if WANT_DEVSCAN_SNMP_DMF == 1 +#if defined WANT_DEVSCAN_SNMP_DMF && WANT_DEVSCAN_SNMP_DMF == 1 # ifndef DEVSCAN_SNMP_DMF # define DEVSCAN_SNMP_DMF /* Can use a copy of the structure that will be populated dynamically */ @@ -61,7 +62,7 @@ extern "C" { # endif /* DEVSCAN_SNMP_DMF */ #endif /* WANT_DEVSCAN_SNMP_DMF */ -#if WANT_LIBNUTSCAN_SNMP_DMF == 1 +#if defined WANT_LIBNUTSCAN_SNMP_DMF && WANT_LIBNUTSCAN_SNMP_DMF == 1 # ifndef LIBNUTSCAN_SNMP_DMF # ifdef DMF_SNMP_H # define LIBNUTSCAN_SNMP_DMF @@ -70,7 +71,7 @@ extern "C" { extern char *dmfnutscan_snmp_dir; extern mibdmf_parser_t *dmfnutscan_snmp_dmp; /* Just reference this to NULLify when client quits and frees DMF stuff */ - void uninit_snmp_device_table(); + void uninit_snmp_device_table(void); # endif /* DMF_SNMP_H already included */ # endif /* LIBNUTSCAN_SNMP_DMF */ #endif /* WANT_LIBNUTSCAN_SNMP_DMF */ diff --git a/tools/nut-scanner/scan_avahi.c b/tools/nut-scanner/scan_avahi.c index 67530a77a2..1c41fc0d40 100644 --- a/tools/nut-scanner/scan_avahi.c +++ b/tools/nut-scanner/scan_avahi.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2024 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify @@ -19,6 +20,7 @@ /*! \file scan_avahi.c \brief detect NUT through Avahi mDNS / DNS-SD services \author Frederic Bohe + \author Arnaud Quette */ #include "common.h" @@ -40,6 +42,8 @@ #include +#define SCAN_AVAHI_DRIVERNAME "dummy-ups" + /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; @@ -264,7 +268,9 @@ static void update_device(const char * host_name, const char *ip, uint16_t port, device_found = 1; dev = nutscan_new_device(); dev->type = TYPE_NUT; - dev->driver = strdup("nutclient"); + /* NOTE: There is no driver by such name, in practice it could + * be a dummy-ups relay, a clone driver, or part of upsmon config */ + dev->driver = strdup(SCAN_AVAHI_DRIVERNAME); if (proto == AVAHI_PROTO_INET) { nutscan_add_option_to_device(dev, "desc", "IPv4"); } @@ -282,7 +288,7 @@ static void update_device(const char * host_name, const char *ip, uint16_t port, 5 + 1 + 1 + 1; dev->port = malloc(buf_size); if (dev->port) { - snprintf(dev->port, buf_size, "%s@%s:%u", + snprintf(dev->port, buf_size, "%s@%s:%" PRIu16, device, host_name, port); } } @@ -320,7 +326,7 @@ static void update_device(const char * host_name, const char *ip, uint16_t port, else { dev = nutscan_new_device(); dev->type = TYPE_NUT; - dev->driver = strdup("nutclient"); + dev->driver = strdup(SCAN_AVAHI_DRIVERNAME); if (proto == AVAHI_PROTO_INET) { nutscan_add_option_to_device(dev, "desc", "IPv4"); } @@ -519,7 +525,7 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) /* Allocate main loop object */ if (!(simple_poll = (*nut_avahi_simple_poll_new)())) { - fprintf(stderr, "Failed to create simple poll object.\n"); + fprintf(stderr, "Failed to create Avahi simple poll object.\n"); goto fail; } @@ -541,7 +547,7 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) /* Check wether creating the client object succeeded */ if (!client) { fprintf(stderr, - "Failed to create client: %s\n", + "Failed to create Avahi client: %s\n", (*nut_avahi_strerror)(error)); goto fail; } @@ -560,7 +566,7 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) # pragma GCC diagnostic pop #endif fprintf(stderr, - "Failed to create service browser: %s\n", + "Failed to create Avahi service browser: %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)(client))); goto fail; } @@ -582,7 +588,9 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) return nutscan_rewind_device(dev_ret); } -#else /* WITH_AVAHI */ + +#else /* not WITH_AVAHI */ + /* stub function */ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) { @@ -590,4 +598,5 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) return NULL; } + #endif /* WITH_AVAHI */ diff --git a/tools/nut-scanner/scan_eaton_serial.c b/tools/nut-scanner/scan_eaton_serial.c index 315744558f..fad55f3e11 100644 --- a/tools/nut-scanner/scan_eaton_serial.c +++ b/tools/nut-scanner/scan_eaton_serial.c @@ -25,6 +25,7 @@ #include "common.h" #include "nut-scan.h" +#include "nut_stdint.h" /* Need this on AIX when using xlc to get alloca */ #ifdef _AIX @@ -89,7 +90,7 @@ static pthread_mutex_t dev_mutex; /* Fake driver main, for using serial functions, needed for bcmxcp_ser.c */ char *device_path; -int upsfd; +TYPE_FD upsfd; int exit_flag = 0; int do_lock_port; @@ -131,7 +132,7 @@ unsigned char calc_checksum(const unsigned char *buf) /* Light version of of drivers/libshut.c->shut_synchronise() * return 1 if OK, 0 otherwise */ -static int shut_synchronise(int arg_upsfd) +static int shut_synchronise(TYPE_FD_SER arg_upsfd) { int try; unsigned char reply = '\0'; @@ -161,9 +162,9 @@ static int shut_synchronise(int arg_upsfd) static nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) { nutscan_device_t * dev = NULL; - int devfd = -1; + TYPE_FD_SER devfd = ser_open_nf(port_name); - if ((devfd = ser_open_nf(port_name)) != -1) { + if (VALID_FD_SER(devfd)) { /* set RTS to off and DTR to on to allow correct behavior * with UPS using PnP feature */ if (ser_set_dtr(devfd, 1) != -1) { @@ -210,14 +211,15 @@ static nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) static nutscan_device_t * nutscan_scan_eaton_serial_xcp(const char* port_name) { nutscan_device_t * dev = NULL; - int i, devfd = -1; + int i; ssize_t ret; unsigned char answer[256]; unsigned char sbuf[128]; + TYPE_FD_SER devfd = ser_open_nf(port_name); memset(sbuf, 0, 128); - if ((devfd = ser_open_nf(port_name)) != -1) { + if (VALID_FD_SER(devfd)) { #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif @@ -303,10 +305,10 @@ static nutscan_device_t * nutscan_scan_eaton_serial_q1(const char* port_name) struct termios tio; ssize_t ret = 0; int retry; - int devfd = -1; char buf[128]; + TYPE_FD_SER devfd = ser_open_nf(port_name); - if ((devfd = ser_open_nf(port_name)) != -1) { + if (VALID_FD_SER(devfd)) { if (ser_set_speed_nf(devfd, port_name, B2400) != -1) { if (!tcgetattr(devfd, &tio)) { @@ -399,8 +401,10 @@ static void * nutscan_scan_eaton_serial_device(void * port_arg) nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) { bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ +#ifndef WIN32 struct sigaction oldact; int change_action_handler = 0; +#endif char *current_port_name = NULL; char **serial_ports_list; int current_port_nb; @@ -421,6 +425,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) return NULL; } +#ifndef WIN32 /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if (sigaction(SIGPIPE, NULL, &oldact) == 0) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) @@ -435,6 +440,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) # pragma GCC diagnostic pop #endif } +#endif /* port(s) iterator */ current_port_nb = 0; @@ -474,8 +480,8 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) * other protocol scanners... */ if (curr_threads >= max_threads) { - upsdebugx(2, "%s: already running %zu scanning threads " - "(launched overall: %zu), " + upsdebugx(2, "%s: already running %" PRIuSIZE " scanning threads " + "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); while (curr_threads >= max_threads) { @@ -489,12 +495,12 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) ret = pthread_tryjoin_np(thread_array[i].thread, NULL); switch (ret) { case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ - upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + upsdebugx(5, "%s: Was thread #%" PRIuSIZE " joined earlier?", __func__, i); break; case 0: /* thread exited */ if (curr_threads > 0) { curr_threads --; - upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { /* threadcount_mutex fault? */ upsdebugx(0, "WARNING: %s: Accounting of thread count " @@ -503,13 +509,13 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) thread_array[i].active = FALSE; break; case EBUSY: /* actively running */ - upsdebugx(6, "%s: thread #%zu still busy (%i)", + upsdebugx(6, "%s: thread #%" PRIuSIZE " still busy (%i)", __func__, i, ret); break; case EDEADLK: /* Errors with thread interactions... bail out? */ case EINVAL: /* Errors with thread interactions... bail out? */ default: /* new pthreads abilities? */ - upsdebugx(5, "%s: thread #%zu reported code %i", + upsdebugx(5, "%s: thread #%" PRIuSIZE " reported code %i", __func__, i, ret); break; } @@ -533,13 +539,14 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) #ifdef HAVE_PTHREAD if (pthread_create(&thread, NULL, nutscan_scan_eaton_serial_device, (void*)current_port_name) == 0) { + nutscan_thread_t *new_thread_array; # ifdef HAVE_PTHREAD_TRYJOIN pthread_mutex_lock(&threadcount_mutex); curr_threads++; # endif /* HAVE_PTHREAD_TRYJOIN */ thread_count++; - nutscan_thread_t *new_thread_array = realloc(thread_array, + new_thread_array = realloc(thread_array, thread_count * sizeof(nutscan_thread_t)); if (new_thread_array == NULL) { upsdebugx(1, "%s: Failed to realloc thread array", __func__); @@ -572,7 +579,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) if (!thread_array[i].active) { /* Probably should not get here, * but handle it just in case */ - upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %" PRIuSIZE " to be not active", __func__, i); sem_post(semaphore); continue; @@ -619,7 +626,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) pthread_mutex_lock(&threadcount_mutex); if (curr_threads > 0) { curr_threads --; - upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " @@ -635,6 +642,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) pthread_mutex_destroy(&dev_mutex); #endif /* HAVE_PTHREAD */ +#ifndef WIN32 if (change_action_handler) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) # pragma GCC diagnostic push @@ -645,6 +653,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) # pragma GCC diagnostic pop #endif } +#endif /* WIN32 */ /* free everything... */ i = 0; diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index f7ed1c3c80..ba4e50776f 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -244,7 +244,9 @@ int nutscan_load_ipmi_library(const char *libname_path) return 1; err: - fprintf(stderr, "Cannot load IPMI library (%s) : %s. IPMI search disabled.\n", libname_path, dl_error); + fprintf(stderr, + "Cannot load IPMI library (%s) : %s. IPMI search disabled.\n", + libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; @@ -631,7 +633,9 @@ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip return nutscan_rewind_device(current_nut_dev); } -#else /* WITH_IPMI */ + +#else /* not WITH_IPMI */ + /* stub function */ nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec) { @@ -641,4 +645,5 @@ nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, return NULL; } + #endif /* WITH_IPMI */ diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index a0f2b3e030..cfff369f8e 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 - 2023 Arnaud Quette (Design and part of implementation) * Copyright (C) 2011 - EATON * Copyright (C) 2016-2021 - EATON - Various threads-related improvements * @@ -21,19 +22,23 @@ \brief detect remote NUT services \author Frederic Bohe \author Jim Klimov + \author Arnaud Quette */ #include "common.h" #include "upsclient.h" #include "nut-scan.h" +#include "nut_stdint.h" #include +#define SCAN_NUT_DRIVERNAME "dummy-ups" + /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; -static int (*nut_upscli_splitaddr)(const char *buf, char **hostname, int *port); -static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, int port, +static int (*nut_upscli_splitaddr)(const char *buf, char **hostname, uint16_t *port); +static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, uint16_t port, int flags, struct timeval * timeout); static int (*nut_upscli_list_start)(UPSCONN_t *ups, size_t numq, const char **query); @@ -90,7 +95,7 @@ int nutscan_load_upsclient_library(const char *libname_path) lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_upscli_splitaddr) = lt_dlsym(dl_handle, - "upscli_splitaddr"); + "upscli_splitaddr"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } @@ -120,8 +125,11 @@ int nutscan_load_upsclient_library(const char *libname_path) } return 1; + err: - fprintf(stderr, "Cannot load NUT library (%s) : %s. NUT search disabled.\n", libname_path, dl_error); + fprintf(stderr, + "Cannot load NUT library (%s) : %s. NUT search disabled.\n", + libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; @@ -133,7 +141,7 @@ static void * list_nut_devices(void * arg) struct scan_nut_arg * nut_arg = (struct scan_nut_arg*)arg; char *target_hostname = nut_arg->hostname; struct timeval tv; - int port; + uint16_t port; size_t numq, numa; const char *query[4]; char **answer; @@ -186,14 +194,27 @@ static void * list_nut_devices(void * arg) * - for upsmon.conf or ups.conf (using dummy-ups)? */ dev = nutscan_new_device(); dev->type = TYPE_NUT; - dev->driver = strdup("nutclient"); + /* NOTE: There is no driver by such name, in practice it could + * be a dummy-ups relay, a clone driver, or part of upsmon config */ + dev->driver = strdup(SCAN_NUT_DRIVERNAME); /* +1+1 is for '@' character and terminating 0 */ buf_size = strlen(answer[1]) + strlen(hostname) + 1 + 1; + if (port != PORT) { + /* colon and up to 5 digits */ + buf_size += 6; + } + dev->port = malloc(buf_size); if (dev->port) { - snprintf(dev->port, buf_size, "%s@%s", answer[1], - hostname); + if (port != PORT) { + snprintf(dev->port, buf_size, "%s@%s:%" PRIu16, + answer[1], hostname, port); + } else { + /* Standard port, not suffixed */ + snprintf(dev->port, buf_size, "%s@%s", + answer[1], hostname); + } #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif @@ -219,9 +240,17 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con char * ip_str = NULL; char * ip_dest = NULL; char buf[SMALLBUF]; +#ifndef WIN32 struct sigaction oldact; int change_action_handler = 0; +#endif struct scan_nut_arg *nut_arg; +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE sem_t * semaphore = nutscan_semaphore(); @@ -272,6 +301,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con return NULL; } +#ifndef WIN32 /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if (sigaction(SIGPIPE, NULL, &oldact) == 0) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) @@ -286,6 +316,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con # pragma GCC diagnostic pop #endif } +#endif ip_str = nutscan_ip_iter_init(&ip, startIP, stopIP); @@ -330,8 +361,8 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con if (curr_threads >= max_threads || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { - upsdebugx(2, "%s: already running %zu scanning threads " - "(launched overall: %zu), " + upsdebugx(2, "%s: already running %" PRIuSIZE " scanning threads " + "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); while (curr_threads >= max_threads @@ -347,12 +378,12 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con ret = pthread_tryjoin_np(thread_array[i].thread, NULL); switch (ret) { case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ - upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + upsdebugx(5, "%s: Was thread #%" PRIuSIZE " joined earlier?", __func__, i); break; case 0: /* thread exited */ if (curr_threads > 0) { curr_threads --; - upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { /* threadcount_mutex fault? */ upsdebugx(0, "WARNING: %s: Accounting of thread count " @@ -361,13 +392,13 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con thread_array[i].active = FALSE; break; case EBUSY: /* actively running */ - upsdebugx(6, "%s: thread #%zu still busy (%i)", + upsdebugx(6, "%s: thread #%" PRIuSIZE " still busy (%i)", __func__, i, ret); break; case EDEADLK: /* Errors with thread interactions... bail out? */ case EINVAL: /* Errors with thread interactions... bail out? */ default: /* new pthreads abilities? */ - upsdebugx(5, "%s: thread #%zu reported code %i", + upsdebugx(5, "%s: thread #%" PRIuSIZE " reported code %i", __func__, i, ret); break; } @@ -413,13 +444,14 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con #ifdef HAVE_PTHREAD if (pthread_create(&thread, NULL, list_nut_devices, (void*)nut_arg) == 0) { + nutscan_thread_t *new_thread_array; # ifdef HAVE_PTHREAD_TRYJOIN pthread_mutex_lock(&threadcount_mutex); curr_threads++; # endif /* HAVE_PTHREAD_TRYJOIN */ thread_count++; - nutscan_thread_t *new_thread_array = realloc(thread_array, + new_thread_array = realloc(thread_array, thread_count * sizeof(nutscan_thread_t)); if (new_thread_array == NULL) { upsdebugx(1, "%s: Failed to realloc thread array", __func__); @@ -453,7 +485,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con if (!thread_array[i].active) { /* Probably should not get here, * but handle it just in case */ - upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %" PRIuSIZE " to be not active", __func__, i); sem_post(semaphore); if (max_threads_scantype > 0) @@ -506,7 +538,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con pthread_mutex_lock(&threadcount_mutex); if (curr_threads > 0) { curr_threads --; - upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " @@ -527,6 +559,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ +#ifndef WIN32 if (change_action_handler) { #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) # pragma GCC diagnostic push @@ -537,6 +570,7 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con # pragma GCC diagnostic pop #endif } +#endif return nutscan_rewind_device(dev_ret); } diff --git a/tools/nut-scanner/scan_nut_simulation.c b/tools/nut-scanner/scan_nut_simulation.c new file mode 100644 index 0000000000..b2c4cddabb --- /dev/null +++ b/tools/nut-scanner/scan_nut_simulation.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023-2024 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! \file scan_nut_simulation.c + \brief detect local NUT simulation devices (.dev & .seq) + \author Arnaud Quette +*/ + +#include "common.h" +#include "nut-scan.h" +#include "nut_stdint.h" +#include + +#define SCAN_NUT_SIMULATION_DRIVERNAME "dummy-ups" + +/* dynamic link library stuff */ +static nutscan_device_t * dev_ret = NULL; + +#ifdef HAVE_PTHREAD +static pthread_mutex_t dev_mutex; +#endif + +nutscan_device_t * nutscan_scan_nut_simulation(void) +{ + DIR *dp; + struct dirent *dirp; + nutscan_device_t * dev = NULL; + +#if HAVE_PTHREAD + pthread_mutex_init(&dev_mutex, NULL); +#endif /* HAVE_PTHREAD */ + + upsdebugx(1, "Scanning: %s", CONFPATH); + + if ((dp = opendir(CONFPATH)) == NULL) { + upsdebugx(1, "%s: Failed to open %s: %s (%d)", + __func__, CONFPATH, strerror(errno), errno); + upsdebugx(0, "Failed to open %s, skip NUT simulation scan", + CONFPATH); + return NULL; + } + + while ((dirp = readdir(dp)) != NULL) + { + const char *ext; + + upsdebugx(5, "Comparing file %s with simulation file extensions", dirp->d_name); + ext = strrchr(dirp->d_name, '.'); + if((!ext) || (ext == dirp->d_name)) + continue; + + /* Filter on '.dev' and '.seq' extensions' */ + if ((strcmp(ext, ".dev") == 0) || (strcmp(ext, ".seq") == 0)) { + upsdebugx(1, "Found simulation file: %s", dirp->d_name); + + dev = nutscan_new_device(); + dev->type = TYPE_NUT_SIMULATION; + dev->driver = strdup(SCAN_NUT_SIMULATION_DRIVERNAME); + dev->port = strdup(dirp->d_name); + +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + dev_ret = nutscan_add_device_to_device(dev_ret, dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + } + } + closedir(dp); + +#if HAVE_PTHREAD + pthread_mutex_destroy(&dev_mutex); +#endif /* HAVE_PTHREAD */ + + return nutscan_rewind_device(dev_ret); +} diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 1f95b3a25d..254cf90ec0 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -27,10 +27,16 @@ #include "common.h" #include "nut-scan.h" +#include "nut_stdint.h" #ifdef WITH_SNMP +#ifndef WIN32 #include +#else +#undef _WIN32_WINNT +#endif + #include #include #include @@ -60,17 +66,43 @@ #include #include + +#ifndef WITH_DMFMIB +# define WITH_DMFMIB 0 +#endif + +#if WITH_DMFMIB +# ifdef WANT_LIBNUTSCAN_SNMP_DMF +# undef WANT_LIBNUTSCAN_SNMP_DMF +# endif + +/* This chains to also include nutscan-snmp.h and the desired + * variables need structures defined lower in the dmf.h file. + * But there is protection in nutscan-snmp.h to only declare + * those vars if dmf.h was already completely imported. + */ +# include "dmf.h" + +/* Now we may "want" the variables from libnutscan with types from dmf.h */ +# define WANT_LIBNUTSCAN_SNMP_DMF 1 +# ifndef WANT_DEVSCAN_SNMP_DMF +# define WANT_DEVSCAN_SNMP_DMF 1 +# endif +#endif /* WITH_DMFMIB */ + #include "nutscan-snmp.h" -// Cause the header to also declare the external reference to pre-generated -// compilable structure with the subset of MIB mappings needed by nut-scanner +/* Cause the header to also declare the external reference to pre-generated + * compilable structure with the subset of MIB mappings needed by nut-scanner + */ #ifndef WANT_DEVSCAN_SNMP_BUILTIN #define WANT_DEVSCAN_SNMP_BUILTIN 1 #endif -// Caller defined this macro to not 1, or undefined it somehow. -// Maybe a developer might want to disable it as an experiment. -// Or some patchwork or script made a mistake... Tell them! +/* Caller defined this macro to not 1, or undefined it somehow. + * Maybe a developer might want to disable it as an experiment. + * Or some patchwork or script made a mistake... Tell them! + */ #if WANT_DEVSCAN_SNMP_BUILTIN != 1 # if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) || defined(_MSC_VER) # if defined(__GNUC__) || defined(__GNUG__) @@ -86,24 +118,28 @@ # endif #endif -#if WITH_DMFMIB -# include "dmf.h" -#endif - /* Address API change */ #if ( ! NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol ) && ( ! defined usmAESPrivProtocol ) #define USMAESPRIVPROTOCOL "usmAES128PrivProtocol" +#define USMAESPRIVPROTOCOL_PTR usmAES128PrivProtocol #else #define USMAESPRIVPROTOCOL "usmAESPrivProtocol" +#define USMAESPRIVPROTOCOL_PTR usmAESPrivProtocol #endif #define SysOID ".1.3.6.1.2.1.1.2.0" /* use explicit booleans */ -#ifndef FALSE +#if !(defined HAVE_BOOL_T) || !HAVE_BOOL_T +# ifndef FALSE typedef enum ebool { FALSE = 0, TRUE } bool_t; -#else +# else typedef int bool_t; +# endif +# ifdef HAVE_BOOL_T +# undef HAVE_BOOL_T +# endif +# define HAVE_BOOL_T 1 #endif static nutscan_device_t * dev_ret = NULL; @@ -113,7 +149,7 @@ static pthread_mutex_t dev_mutex; static useconds_t g_usec_timeout ; /* Pointer to the array we ultimately use (builtin or dynamic) */ -snmp_device_id_t *snmp_device_table = NULL; +static snmp_device_id_t *snmp_device_table = NULL; #if WITH_DMFMIB /* This would point to DMF data loaded to by this library, if loaded */ @@ -130,11 +166,13 @@ char *dmfnutscan_snmp_dir = NULL; # define SU_VAR_DMFDIR "dmfdir" # endif -#endif /* if WITH_DMFMIB */ +#endif /* if WITH_DMFMIB */ +#ifndef WITH_SNMP_STATIC /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +#endif /* WITH_SNMP_STATIC */ static void (*nut_init_snmp)(const char *type); static void (*nut_snmp_sess_init)(netsnmp_session * session); @@ -198,7 +236,7 @@ static oid *nut_usmHMAC384SHA512AuthProtocol; /* return 0 on error; visible externally */ int nutscan_load_snmp_library(const char *libname_path); -void uninit_snmp_device_table() { +void uninit_snmp_device_table(void) { #if WITH_DMFMIB if (snmp_device_table == snmp_device_table_dmf) snmp_device_table = NULL; @@ -211,31 +249,34 @@ void uninit_snmp_device_table() { } /* return 0 on error */ -int init_snmp_device_table() +static +int init_snmp_device_table(void) { - // A simple routine to load nutscan DMFs, safe to call several times + /* A simple routine to load nutscan DMFs, safe to call several times */ if (snmp_device_table != NULL) return 1; #if WITH_DMFMIB if (dmfnutscan_snmp_dir != NULL) { - // parse_dir, check success, assign var + /* parse_dir, check success, assign var */ upsdebugx(1, "init_snmp_device_table() trying to load DMF from %s", dmfnutscan_snmp_dir); dmfnutscan_snmp_dmp = mibdmf_parser_new(); if (dmfnutscan_snmp_dmp == NULL) { upsdebugx(1, "PROBLEM: Can not allocate the DMF parsing structures"); } else { + int device_table_counter; + mibdmf_parse_dir(dmfnutscan_snmp_dir, dmfnutscan_snmp_dmp); snmp_device_table_dmf = mibdmf_get_device_table(dmfnutscan_snmp_dmp); - int device_table_counter = mibdmf_get_device_table_counter(dmfnutscan_snmp_dmp); + device_table_counter = mibdmf_get_device_table_counter(dmfnutscan_snmp_dmp); if (snmp_device_table_dmf != NULL && device_table_counter > 1) { snmp_device_table = snmp_device_table_dmf; upsdebugx(1, "SUCCESS: Can use the SNMP device mapping parsed from " "DMF library with %d definitions", device_table_counter-1); - // Note: caller should free these structures in the end, just like below + /* Note: caller should free these structures in the end, just like below */ } else { upsdebugx(1, "PROBLEM: Can not access the SNMP device mapping " "parsed from DMF library, or loaded an empty table"); @@ -266,6 +307,91 @@ int init_snmp_device_table() /* return 0 on error */ int nutscan_load_snmp_library(const char *libname_path) { +#ifdef WITH_SNMP_STATIC + /* With MinGW, the netsnmp library may be linked statically (no dll) */ + NUT_UNUSED_VARIABLE(libname_path); + + /* Assignments were parsed from code below with: + * grep -A1 dlsym tools/nut-scanner/scan_snmp.c | grep -E 'dlsym|")' | sed -e 's| *lt_dlsym(dl_handle, *| |' -e 's,");,;,' -e 's,",,' -e 's,= *$,=,' + */ + +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# endif + *(void **) (&nut_init_snmp) = init_snmp; + *(void **) (&nut_snmp_sess_init) = + snmp_sess_init; + *(void **) (&nut_snmp_sess_open) = + snmp_sess_open; + *(void **) (&nut_snmp_sess_close) = + snmp_sess_close; + *(void **) (&nut_snmp_sess_session) = + snmp_sess_session; + *(void **) (&nut_snmp_parse_oid) = + snmp_parse_oid; + *(void **) (&nut_snmp_pdu_create) = + snmp_pdu_create; + *(void **) (&nut_snmp_add_null_var) = + snmp_add_null_var; + *(void **) (&nut_snmp_sess_synch_response) = + snmp_sess_synch_response; + *(void **) (&nut_snmp_oid_compare) = + snmp_oid_compare; + *(void **) (&nut_snmp_free_pdu) = snmp_free_pdu; + *(void **) (&nut_generate_Ku) = generate_Ku; + *(void **) (&nut_snmp_out_toggle_options) = + snmp_out_toggle_options; + *(void **) (&nut_snmp_api_errstring) = + snmp_api_errstring; + + /* Note: this one is an (int) exposed by netsnmp, not a function! */ + nut_snmp_errno = &snmp_errno; + +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol + *(void **) (&nut_usmAESPrivProtocol) = + USMAESPRIVPROTOCOL_PTR; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol + *(void **) (&nut_usmHMACMD5AuthProtocol) = + usmHMACMD5AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol + *(void **) (&nut_usmHMACSHA1AuthProtocol) = + usmHMACSHA1AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol + *(void **) (&nut_usmDESPrivProtocol) = + usmDESPrivProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + *(void **) (&nut_usmAES192PrivProtocol) = + usmAES192PrivProtocol; +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + *(void **) (&nut_usmAES256PrivProtocol) = + usmAES256PrivProtocol; +# endif +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + *(void **) (&nut_usmHMAC192SHA256AuthProtocol) = + usmHMAC192SHA256AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + *(void **) (&nut_usmHMAC256SHA384AuthProtocol) = + usmHMAC256SHA384AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + *(void **) (&nut_usmHMAC384SHA512AuthProtocol) = + usmHMAC384SHA512AuthProtocol; +#endif + +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) +# pragma GCC diagnostic pop +# endif + +#else /* not WITH_SNMP_STATIC */ if (dl_handle != NULL) { /* if previous init failed */ if (dl_handle == (void *)1) { @@ -452,14 +578,19 @@ int nutscan_load_snmp_library(const char *libname_path) } #endif /* NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol */ +#endif /* WITH_SNMP_STATIC */ + return 1; +#ifndef WITH_SNMP_STATIC err: - fprintf(stderr, "Cannot load SNMP library (%s) : %s. SNMP search disabled.\n", + fprintf(stderr, + "Cannot load SNMP library (%s) : %s. SNMP search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; +#endif /* not WITH_SNMP_STATIC */ } /* end of dynamic link library stuff */ @@ -482,6 +613,7 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response size_t mib2nut_count_total = 0; size_t mib2nut_count_useful = 0; bool_t mib2nut_has_data = FALSE; + mib2nut_info_t **mib2nut; /* DMF is loaded thus used, successfully (with at least a collection * of tags - entries needed for supportability discovery). @@ -495,7 +627,7 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response * or just an excerpt with lots of tags for quicker load * during nut-scanning?) */ - mib2nut_info_t **mib2nut = *(mibdmf_get_mib2nut_table_ptr)(dmfnutscan_snmp_dmp); + mib2nut = *(mibdmf_get_mib2nut_table_ptr)(dmfnutscan_snmp_dmp); if (mib2nut == NULL) { upsdebugx(4, "%s: WARNING: Could not access the mib2nut index table", __func__); @@ -529,12 +661,13 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response __func__, mib2nut_count_total, mib2nut_count_useful); if (mib && strcmp(mib, "eaton_epdu") == 0) { - // FIXME (WITH_SNMP_LKP_FUN): When support for lookup functions - // in DMF is fixed, this clause has to be amended back, too. - // Also note that currently this suggestion concerns just one - // mapping table (for Eaton Marlin ePDUs), and that developers - // or validators are not forbidden to configure any driver they - // want to explicitly -- this failsafe is just for nut-scanner. + /* FIXME (WITH_SNMP_LKP_FUN): When support for lookup functions + * in DMF is fixed, this clause has to be amended back, too. + * Also note that currently this suggestion concerns just one + * mapping table (for Eaton Marlin ePDUs), and that developers + * or validators are not forbidden to configure any driver they + * want to explicitly -- this failsafe is just for nut-scanner. + */ upslogx(1, "This device mapping uses lookup functions which is not yet supported by DMF driver"); } else { dev->driver = strdup("snmp-ups-dmf"); @@ -670,6 +803,7 @@ static struct snmp_pdu * scan_snmp_get_oid(char* oid_str, void* handle) return NULL; } + upsdebugx(3, "%s: collected index: %i", __func__, index); return response; } @@ -861,7 +995,7 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) # pragma GCC diagnostic pop #endif - fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu", + fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %" PRIuSIZE, snmp_sess->securityAuthProtoLen); return 0; } @@ -949,7 +1083,7 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) # pragma GCC diagnostic pop #endif - fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu", + fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %" PRIuSIZE, snmp_sess->securityAuthProtoLen); return 0; } @@ -1107,6 +1241,7 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip useconds_t usec_timeout, nutscan_snmp_t * sec) { bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ + nutscan_device_t * result; nutscan_snmp_t * tmp_sec; nutscan_ip_iter_t ip; char * ip_str = NULL; @@ -1116,6 +1251,13 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip sem_t semaphore_scantype_inst; sem_t * semaphore_scantype = &semaphore_scantype_inst; # endif /* HAVE_SEMAPHORE */ + +# ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +# endif + pthread_t thread; nutscan_thread_t * thread_array = NULL; size_t thread_count = 0, i; @@ -1202,8 +1344,8 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip if (curr_threads >= max_threads || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { - upsdebugx(2, "%s: already running %zu scanning threads " - "(launched overall: %zu), " + upsdebugx(2, "%s: already running %" PRIuSIZE " scanning threads " + "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); while (curr_threads >= max_threads @@ -1219,12 +1361,12 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip ret = pthread_tryjoin_np(thread_array[i].thread, NULL); switch (ret) { case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ - upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + upsdebugx(5, "%s: Was thread #%" PRIuSIZE " joined earlier?", __func__, i); break; case 0: /* thread exited */ if (curr_threads > 0) { curr_threads --; - upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { /* threadcount_mutex fault? */ upsdebugx(0, "WARNING: %s: Accounting of thread count " @@ -1233,13 +1375,13 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip thread_array[i].active = FALSE; break; case EBUSY: /* actively running */ - upsdebugx(6, "%s: thread #%zu still busy (%i)", + upsdebugx(6, "%s: thread #%" PRIuSIZE " still busy (%i)", __func__, i, ret); break; case EDEADLK: /* Errors with thread interactions... bail out? */ case EINVAL: /* Errors with thread interactions... bail out? */ default: /* new pthreads abilities? */ - upsdebugx(5, "%s: thread #%zu reported code %i", + upsdebugx(5, "%s: thread #%" PRIuSIZE " reported code %i", __func__, i, ret); break; } @@ -1267,13 +1409,14 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip #ifdef HAVE_PTHREAD if (pthread_create(&thread, NULL, try_SysOID, (void*)tmp_sec) == 0) { + nutscan_thread_t *new_thread_array; # ifdef HAVE_PTHREAD_TRYJOIN pthread_mutex_lock(&threadcount_mutex); curr_threads++; # endif /* HAVE_PTHREAD_TRYJOIN */ thread_count++; - nutscan_thread_t *new_thread_array = realloc(thread_array, + new_thread_array = realloc(thread_array, thread_count * sizeof(nutscan_thread_t)); if (new_thread_array == NULL) { upsdebugx(1, "%s: Failed to realloc thread array", __func__); @@ -1308,7 +1451,7 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip if (!thread_array[i].active) { /* Probably should not get here, * but handle it just in case */ - upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %" PRIuSIZE " to be not active", __func__, i); sem_post(semaphore); if (max_threads_scantype > 0) @@ -1361,7 +1504,7 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip pthread_mutex_lock(&threadcount_mutex); if (curr_threads > 0) { curr_threads --; - upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " @@ -1382,13 +1525,14 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ - nutscan_device_t * result = nutscan_rewind_device(dev_ret); + result = nutscan_rewind_device(dev_ret); dev_ret = NULL; return result; } -#else /* WITH_SNMP */ +#else /* not WITH_SNMP */ +/* stub function */ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, useconds_t usec_timeout, nutscan_snmp_t * sec) { diff --git a/tools/nut-scanner/scan_usb.c b/tools/nut-scanner/scan_usb.c index a828827bb0..dbe2aa5c30 100644 --- a/tools/nut-scanner/scan_usb.c +++ b/tools/nut-scanner/scan_usb.c @@ -53,9 +53,11 @@ static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, static ssize_t (*nut_usb_get_device_list)(libusb_context *ctx, libusb_device ***list); static void (*nut_usb_free_device_list)(libusb_device **list, int unref_devices); static uint8_t (*nut_usb_get_bus_number)(libusb_device *dev); + static uint8_t (*nut_usb_get_device_address)(libusb_device *dev); + static uint8_t (*nut_usb_get_port_number)(libusb_device *dev); static int (*nut_usb_get_device_descriptor)(libusb_device *dev, struct libusb_device_descriptor *desc); -#else +#else /* => WITH_LIBUSB_0_1 */ #define USB_INIT_SYMBOL "usb_init" #define USB_OPEN_SYMBOL "usb_open" #define USB_CLOSE_SYMBOL "usb_close" @@ -63,10 +65,14 @@ static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, static libusb_device_handle * (*nut_usb_open)(struct usb_device *dev); static void (*nut_usb_init)(void); static int (*nut_usb_find_busses)(void); +# ifndef WIN32 static struct usb_bus * (*nut_usb_busses); +# else + static struct usb_bus * (*nut_usb_get_busses)(void); +# endif /* WIN32 */ static int (*nut_usb_find_devices)(void); static char * (*nut_usb_strerror)(void); -#endif +#endif /* WITH_LIBUSB_1_0 */ /* return 0 on error; visible externally */ int nutscan_load_usb_library(const char *libname_path); @@ -96,6 +102,7 @@ int nutscan_load_usb_library(const char *libname_path) dl_error = lt_dlerror(); goto err; } + lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_usb_init) = lt_dlsym(dl_handle, USB_INIT_SYMBOL); if ((dl_error = lt_dlerror()) != NULL) { @@ -107,7 +114,6 @@ int nutscan_load_usb_library(const char *libname_path) goto err; } - lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_usb_close) = lt_dlsym(dl_handle, USB_CLOSE_SYMBOL); if ((dl_error = lt_dlerror()) != NULL) { goto err; @@ -124,22 +130,50 @@ int nutscan_load_usb_library(const char *libname_path) goto err; } - *(void **) (&nut_usb_get_device_list) = lt_dlsym(dl_handle, "libusb_get_device_list"); + *(void **) (&nut_usb_get_device_list) = lt_dlsym(dl_handle, + "libusb_get_device_list"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_free_device_list) = lt_dlsym(dl_handle, + "libusb_free_device_list"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_usb_free_device_list) = lt_dlsym(dl_handle, "libusb_free_device_list"); + *(void **) (&nut_usb_get_bus_number) = lt_dlsym(dl_handle, + "libusb_get_bus_number"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_usb_get_bus_number) = lt_dlsym(dl_handle, "libusb_get_bus_number"); + /* Note: per https://nxmnpg.lemoda.net/3/libusb_get_device_address there + * was a libusb_get_port_path() equivalent with different arguments, but + * not for too long (libusb-1.0.12...1.0.16) and now it is deprecated. + */ + *(void **) (&nut_usb_get_device_address) = lt_dlsym(dl_handle, + "libusb_get_device_address"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_usb_get_device_descriptor) = lt_dlsym(dl_handle, "libusb_get_device_descriptor"); + /* This method may be absent in some libusb versions, and we should + * tolerate that! In run-time driver code see also blocks fenced by: + * #if (defined WITH_USB_BUSPORT) && (WITH_USB_BUSPORT) + */ + *(void **) (&nut_usb_get_port_number) = lt_dlsym(dl_handle, + "libusb_get_port_number"); + if ((dl_error = lt_dlerror()) != NULL) { + fprintf(stderr, + "While loading USB library (%s), failed to find libusb_get_port_number() : %s. " + "The \"busport\" USB matching option will be disabled.\n", + libname_path, dl_error); + nut_usb_get_port_number = NULL; + } + + *(void **) (&nut_usb_get_device_descriptor) = lt_dlsym(dl_handle, + "libusb_get_device_descriptor"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } @@ -150,17 +184,28 @@ int nutscan_load_usb_library(const char *libname_path) goto err; } #else /* for libusb 0.1 */ - *(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, "usb_find_busses"); + *(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, + "usb_find_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); +# ifndef WIN32 + *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, + "usb_busses"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +# else + *(void **) (&nut_usb_get_busses) = lt_dlsym(dl_handle, + "usb_get_busses"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } +# endif /* WIN32 */ - *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle, "usb_find_devices"); + *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle, + "usb_find_devices"); if ((dl_error = lt_dlerror()) != NULL) { goto err; } @@ -175,7 +220,9 @@ int nutscan_load_usb_library(const char *libname_path) return 1; err: - fprintf(stderr, "Cannot load USB library (%s) : %s. USB search disabled.\n", libname_path, dl_error); + fprintf(stderr, + "Cannot load USB library (%s) : %s. USB search disabled.\n", + libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; @@ -183,7 +230,7 @@ int nutscan_load_usb_library(const char *libname_path) /* end of dynamic link library stuff */ static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list, - int dev_VendorID, int dev_ProductID) + int dev_VendorID, int dev_ProductID, char **alt) { usb_device_id_t *usbdev; @@ -191,6 +238,8 @@ static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list, if ((usbdev->vendorID == dev_VendorID) && (usbdev->productID == dev_ProductID) ) { + if (alt) + *alt = usbdev->alt_driver_names; return usbdev->driver_name; } } @@ -199,22 +248,57 @@ static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list, } /* return NULL if error */ -nutscan_device_t * nutscan_scan_usb() +nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts) { int ret; char string[256]; + /* Items below are learned by libusbN version-specific API code + * Keep in sync with items matched by drivers/libusb{0,1}.c + * (nut)libusb_open methods, and fields of USBDevice_t struct + * (drivers/usb-common.h). + */ char *driver_name = NULL; + char *alt_driver_names = NULL; char *serialnumber = NULL; char *device_name = NULL; char *vendor_name = NULL; uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; - uint16_t VendorID; - uint16_t ProductID; - char *busname; + uint16_t VendorID = 0; + uint16_t ProductID = 0; + char *busname = NULL; + /* device_port physical meaning: connection port on that bus; + * different consumers plugged into same socket should have + * the same port value. However in practice such functionality + * depends on platform and HW involved and may mean logical + * enumeration results. + * In libusb1 API: first libusb_get_port_numbers() earlier known + * as libusb_get_port_path() for physical port number on the bus, see + * https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga14879a0ea7daccdcddb68852d86c00c4 + * later changed to logical libusb_get_bus_number() (which + * often yields same numeric value, except on systems that + * can not see or tell about physical topology) + * In libusb0 API: "device filename" + */ + char *device_port = NULL; + + /* bcdDevice: aka "Device release number" - note we currently do not match by it */ + uint16_t bcdDevice = 0; + + nutscan_usb_t default_scanopts; + #if WITH_LIBUSB_1_0 libusb_device *dev; libusb_device **devlist; - uint8_t bus; + uint8_t bus_num; + /* Sort of like device_port above, but different (should be + * more closely about physical port number than logical device + * enumeration results). Uses libusb_get_port_number() where + * available in libusb (and hoping the OS and HW honour it). + */ + char *bus_port = NULL; + ssize_t devcount = 0; + struct libusb_device_descriptor dev_desc; + int i; #else /* => WITH_LIBUSB_0_1 */ struct usb_device *dev; struct usb_bus *bus; @@ -228,6 +312,18 @@ nutscan_device_t * nutscan_scan_usb() return NULL; } + if (!scanopts) { + default_scanopts.report_bus = 1; + default_scanopts.report_busport = 1; + /* AQU note: disabled by default, since it may lead to instabilities + * and give more issues than solutions! */ + default_scanopts.report_device = 0; + /* Generally not useful at the moment, and coded to be commented away + * in formats that support it (e.g. ups.conf) or absent in others. */ + default_scanopts.report_bcdDevice = 0; + scanopts = &default_scanopts; + } + /* libusb base init */ /* Initialize Libusb */ #if WITH_LIBUSB_1_0 @@ -242,10 +338,6 @@ nutscan_device_t * nutscan_scan_usb() #endif /* WITH_LIBUSB_1_0 */ #if WITH_LIBUSB_1_0 - ssize_t devcount = 0; - struct libusb_device_descriptor dev_desc; - int i; - devcount = (*nut_usb_get_device_list)(NULL, &devlist); if (devcount <= 0) { (*nut_usb_exit)(NULL); @@ -263,16 +355,53 @@ nutscan_device_t * nutscan_scan_usb() iManufacturer = dev_desc.iManufacturer; iProduct = dev_desc.iProduct; iSerialNumber = dev_desc.iSerialNumber; - bus = (*nut_usb_get_bus_number)(dev); + bus_num = (*nut_usb_get_bus_number)(dev); + busname = (char *)malloc(4); if (busname == NULL) { (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); fatal_with_errno(EXIT_FAILURE, "Out of memory"); } - snprintf(busname, 4, "%03d", bus); + snprintf(busname, 4, "%03d", bus_num); + + device_port = (char *)malloc(4); + if (device_port == NULL) { + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } else { + uint8_t device_addr = (*nut_usb_get_device_address)(dev); + if (device_addr > 0) { + snprintf(device_port, 4, "%03d", device_addr); + } else { + snprintf(device_port, 4, ".*"); + } + } + + if (nut_usb_get_port_number != NULL) { + bus_port = (char *)malloc(4); + if (bus_port == NULL) { + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } else { + uint8_t port_num = (*nut_usb_get_port_number)(dev); + if (port_num > 0) { + snprintf(bus_port, 4, "%03d", port_num); + } else { + snprintf(bus_port, 4, ".*"); + } + } + } + + bcdDevice = dev_desc.bcdDevice; #else /* => WITH_LIBUSB_0_1 */ +# ifndef WIN32 for (bus = (*nut_usb_busses); bus; bus = bus->next) { +# else + for (bus = (*nut_usb_get_busses)(); bus; bus = bus->next) { +# endif /* WIN32 */ for (dev = bus->devices; dev; dev = dev->next) { VendorID = dev->descriptor.idVendor; @@ -282,18 +411,20 @@ nutscan_device_t * nutscan_scan_usb() iProduct = dev->descriptor.iProduct; iSerialNumber = dev->descriptor.iSerialNumber; busname = bus->dirname; + device_port = dev->filename; + bcdDevice = dev->descriptor.bcdDevice; #endif if ((driver_name = is_usb_device_supported(usb_device_table, - VendorID, ProductID)) != NULL) { + VendorID, ProductID, &alt_driver_names)) != NULL) { /* open the device */ #if WITH_LIBUSB_1_0 ret = (*nut_usb_open)(dev, &udev); if (!udev || ret != LIBUSB_SUCCESS) { - fprintf(stderr,"Failed to open device " - "bus '%s', skipping: %s\n", - busname, + fprintf(stderr, "Failed to open device " + "bus '%s' device/port '%s' bus/port '%s', skipping: %s\n", + busname, device_port, bus_port, (*nut_usb_strerror)(ret)); /* Note: closing is not applicable @@ -302,7 +433,12 @@ nutscan_device_t * nutscan_scan_usb() * when e.g. permissions problem) */ - free (busname); + free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } continue; } @@ -311,8 +447,8 @@ nutscan_device_t * nutscan_scan_usb() if (!udev) { /* TOTHINK: any errno or similar to test? */ fprintf(stderr, "Failed to open device " - "bus '%s',skipping: %s\n", - busname, + "bus '%s' device/port '%s', skipping: %s\n", + busname, device_port, (*nut_usb_strerror)()); continue; } @@ -328,6 +464,11 @@ nutscan_device_t * nutscan_scan_usb() (*nut_usb_close)(udev); #if WITH_LIBUSB_1_0 free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); #endif /* WITH_LIBUSB_1_0 */ @@ -347,6 +488,11 @@ nutscan_device_t * nutscan_scan_usb() (*nut_usb_close)(udev); #if WITH_LIBUSB_1_0 free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); #endif /* WITH_LIBUSB_1_0 */ @@ -367,6 +513,11 @@ nutscan_device_t * nutscan_scan_usb() (*nut_usb_close)(udev); #if WITH_LIBUSB_1_0 free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); #endif /* WITH_LIBUSB_1_0 */ @@ -386,6 +537,11 @@ nutscan_device_t * nutscan_scan_usb() (*nut_usb_close)(udev); #if WITH_LIBUSB_1_0 free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } (*nut_usb_free_device_list)(devlist, 1); (*nut_usb_exit)(NULL); #endif /* WITH_LIBUSB_1_0 */ @@ -396,6 +552,9 @@ nutscan_device_t * nutscan_scan_usb() if (driver_name) { nut_dev->driver = strdup(driver_name); } + if (alt_driver_names) { + nut_dev->alt_driver_names = strdup(alt_driver_names); + } nut_dev->port = strdup("auto"); sprintf(string, "%04X", VendorID); @@ -432,9 +591,38 @@ nutscan_device_t * nutscan_scan_usb() vendor_name = NULL; } - nutscan_add_option_to_device(nut_dev, + nutscan_add_commented_option_to_device(nut_dev, "bus", - busname); + busname, + scanopts->report_bus ? NULL : ""); + + nutscan_add_commented_option_to_device(nut_dev, + "device", + device_port, + scanopts->report_device ? NULL : ""); + +#if WITH_LIBUSB_1_0 + if (bus_port) { + nutscan_add_commented_option_to_device(nut_dev, + "busport", + bus_port, + scanopts->report_busport ? NULL : ""); + free(bus_port); + bus_port = NULL; + } +#endif /* WITH_LIBUSB_1_0 */ + + /* FIXME: Detect and suggest HID index, interface number, etc. */ + + if (scanopts->report_bcdDevice) { + /* Not currently matched by drivers, hence commented + * for now even if requested via scanopts */ + sprintf(string, "%04X", bcdDevice); + nutscan_add_commented_option_to_device(nut_dev, + "bcdDevice", + string, + "NOTMATCHED-YET"); + } current_nut_dev = nutscan_add_device_to_device( current_nut_dev, @@ -449,6 +637,11 @@ nutscan_device_t * nutscan_scan_usb() } #else /* not WITH_LIBUSB_0_1 */ free(busname); + free(device_port); + if (bus_port != NULL) { + free(bus_port); + bus_port = NULL; + } } (*nut_usb_free_device_list)(devlist, 1); @@ -457,9 +650,15 @@ nutscan_device_t * nutscan_scan_usb() return nutscan_rewind_device(current_nut_dev); } + #else /* not WITH_USB */ -nutscan_device_t * nutscan_scan_usb() + +/* stub function */ +nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts) { + NUT_UNUSED_VARIABLE(scanopts); + return NULL; } + #endif /* WITH_USB */ diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index 157db366e8..490d2b9b21 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -27,22 +27,35 @@ #include "common.h" #include "nut-scan.h" +#include "nut_stdint.h" #ifdef WITH_NEON +#ifndef WIN32 #include #include #include #include #include +#include +#define SOCK_OPT_CAST +#else +#define SOCK_OPT_CAST (char*) +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +#include +#include +# if ! HAVE_INET_PTON +# include "wincompat.h" /* fallback inet_ntop where needed */ +# endif +#endif + #include #include -#include #include #include #include /* dynamic link library stuff */ -static char * libname = "libneon"; /* Note: this is for info messages, not the SONAME */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; @@ -126,7 +139,9 @@ int nutscan_load_neon_library(const char *libname_path) return 1; err: - fprintf(stderr, "Cannot load XML library (%s) : %s. XML search disabled.\n", libname, dl_error); + fprintf(stderr, + "Cannot load XML library (%s) : %s. XML search disabled.\n", + libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); return 0; @@ -177,7 +192,6 @@ static void * nutscan_scan_xml_http_generic(void * arg) int sockopt_on = 1; struct sockaddr_in sockAddress_udp; socklen_t sockAddressLength = sizeof(sockAddress_udp); - memset(&sockAddress_udp, 0, sizeof(sockAddress_udp)); fd_set fds; struct timeval timeout; int ret; @@ -185,8 +199,18 @@ static void * nutscan_scan_xml_http_generic(void * arg) char string[SMALLBUF]; ssize_t recv_size; int i; - nutscan_device_t * nut_dev = NULL; +#ifdef WIN32 + WSADATA WSAdata; +#endif + + memset(&sockAddress_udp, 0, sizeof(sockAddress_udp)); + +#ifdef WIN32 + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + if (sec != NULL) { /* if (sec->port_http > 0 && sec->port_http <= 65534) * port_http = sec->port_http; */ @@ -218,15 +242,16 @@ static void * nutscan_scan_xml_http_generic(void * arg) if (ip == NULL) { upsdebugx(2, "nutscan_scan_xml_http_generic() : scanning connected network segment(s) " - "with a broadcast, attempt %d of %d with a timeout of %jd usec", + "with a broadcast, attempt %d of %d with a timeout of %" PRIdMAX " usec", (i + 1), MAX_RETRIES, (uintmax_t)usec_timeout); sockAddress_udp.sin_addr.s_addr = INADDR_BROADCAST; - setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, &sockopt_on, + setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, + SOCK_OPT_CAST &sockopt_on, sizeof(sockopt_on)); } else { upsdebugx(2, "nutscan_scan_xml_http_generic() : scanning IP '%s' with a unicast, " - "attempt %d of %d with a timeout of %jd usec", + "attempt %d of %d with a timeout of %" PRIdMAX " usec", ip, (i + 1), MAX_RETRIES, (uintmax_t)usec_timeout); inet_pton(AF_INET, ip, &(sockAddress_udp.sin_addr)); } @@ -258,6 +283,9 @@ static void * nutscan_scan_xml_http_generic(void * arg) while ((ret = select(peerSocket + 1, &fds, NULL, NULL, &timeout)) ) { + ne_xml_parser *parser; + int parserFailed; + retNum ++; upsdebugx(5, "nutscan_scan_xml_http_generic() : request to %s, " "loop #%d/%d, response #%d", @@ -314,12 +342,12 @@ static void * nutscan_scan_xml_http_generic(void * arg) string, port_udp); nut_dev->type = TYPE_XML; /* Try to read device type */ - ne_xml_parser *parser = (*nut_ne_xml_create)(); + parser = (*nut_ne_xml_create)(); (*nut_ne_xml_push_handler)(parser, startelm_cb, NULL, NULL, nut_dev); /* recv_size is a ssize_t, so in range of size_t */ (*nut_ne_xml_parse)(parser, buf, (size_t)recv_size); - int parserFailed = (*nut_ne_xml_failed)(parser); /* 0 = ok, nonzero = fail */ + parserFailed = (*nut_ne_xml_failed)(parser); /* 0 = ok, nonzero = fail */ (*nut_ne_xml_destroy)(parser); if (parserFailed == 0) { @@ -481,8 +509,8 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char if (curr_threads >= max_threads || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { - upsdebugx(2, "%s: already running %zu scanning threads " - "(launched overall: %zu), " + upsdebugx(2, "%s: already running %" PRIuSIZE " scanning threads " + "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); while (curr_threads >= max_threads @@ -498,12 +526,12 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char ret = pthread_tryjoin_np(thread_array[i].thread, NULL); switch (ret) { case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ - upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + upsdebugx(5, "%s: Was thread #%" PRIuSIZE " joined earlier?", __func__, i); break; case 0: /* thread exited */ if (curr_threads > 0) { curr_threads --; - upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { /* threadcount_mutex fault? */ upsdebugx(0, "WARNING: %s: Accounting of thread count " @@ -512,13 +540,13 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char thread_array[i].active = FALSE; break; case EBUSY: /* actively running */ - upsdebugx(6, "%s: thread #%zu still busy (%i)", + upsdebugx(6, "%s: thread #%" PRIuSIZE " still busy (%i)", __func__, i, ret); break; case EDEADLK: /* Errors with thread interactions... bail out? */ case EINVAL: /* Errors with thread interactions... bail out? */ default: /* new pthreads abilities? */ - upsdebugx(5, "%s: thread #%zu reported code %i", + upsdebugx(5, "%s: thread #%" PRIuSIZE " reported code %i", __func__, i, ret); break; } @@ -554,13 +582,14 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char #ifdef HAVE_PTHREAD if (pthread_create(&thread, NULL, nutscan_scan_xml_http_generic, (void *)tmp_sec) == 0) { + nutscan_thread_t *new_thread_array; # ifdef HAVE_PTHREAD_TRYJOIN pthread_mutex_lock(&threadcount_mutex); curr_threads++; # endif /* HAVE_PTHREAD_TRYJOIN */ thread_count++; - nutscan_thread_t *new_thread_array = realloc(thread_array, + new_thread_array = realloc(thread_array, thread_count*sizeof(nutscan_thread_t)); if (new_thread_array == NULL) { upsdebugx(1, "%s: Failed to realloc thread array", __func__); @@ -596,7 +625,7 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char if (!thread_array[i].active) { /* Probably should not get here, * but handle it just in case */ - upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %" PRIuSIZE " to be not active", __func__, i); sem_post(semaphore); if (max_threads_scantype > 0) @@ -649,7 +678,7 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char pthread_mutex_lock(&threadcount_mutex); if (curr_threads > 0) { curr_threads --; - upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%" PRIuSIZE, __func__, i); } else { upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " @@ -701,8 +730,9 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char return result; } -#else /* WITH_NEON */ +#else /* not WITH_NEON */ +/* stub function */ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char * end_ip, useconds_t usec_timeout, nutscan_xml_t * sec) { NUT_UNUSED_VARIABLE(start_ip); diff --git a/tools/nut-snmpinfo.py.in b/tools/nut-snmpinfo.py.in index 8e02d82ce0..84f60dab3b 100755 --- a/tools/nut-snmpinfo.py.in +++ b/tools/nut-snmpinfo.py.in @@ -90,14 +90,24 @@ output_file.write( " * along with this program; if not, write to the Free Softw output_file.write( " * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" ) output_file.write( " */\n" ) output_file.write( "\n" ) +output_file.write( "/* Ensure the table is known as 'extern' so this file builds without\n" ) +output_file.write( " * warnings, and note that the header definition is a pointer (for\n" ) +output_file.write( " * general-purpose comparisons with possible other implementations),\n" ) +output_file.write( " * but here it is initialized as an array and this also causes warnings\n" ) +output_file.write( " * about redefinitions */\n" ) +output_file.write( "#ifdef WANT_DEVSCAN_SNMP_BUILTIN\n" ) +output_file.write( "# undef WANT_DEVSCAN_SNMP_BUILTIN\n" ) +output_file.write( "#endif\n" ) +output_file.write( "#define WANT_DEVSCAN_SNMP_BUILTIN 0\n" ) output_file.write( "#include \"nutscan-snmp.h\"\n" ) +output_file.write( "extern snmp_device_id_t snmp_device_table_builtin[];\n" ) output_file.write( "\n" ) output_file.write( "#ifndef NULL\n" ) -output_file.write( "#define NULL (void*)0ULL\n" ) +output_file.write( "# define NULL (void*)0ULL\n" ) output_file.write( "#endif\n" ) output_file.write( "\n" ) -output_file.write( "// marker to tell humans and GCC that the unused parameter is there for some\n" ) -output_file.write( "// reason (i.e. API compatibility) and compiler should not warn if not used\n" ) +output_file.write( "/* marker to tell humans and GCC that the unused parameter is there for some\n" ) +output_file.write( " * reason (i.e. API compatibility) and compiler should not warn if not used */\n" ) output_file.write( "#ifndef UNUSED_PARAM\n" ) output_file.write( "# ifdef __GNUC__\n" ) output_file.write( "# define UNUSED_PARAM __attribute__ ((__unused__))\n" ) diff --git a/tools/nut-usbinfo.pl b/tools/nut-usbinfo.pl index 63cd074484..fa6069b0a2 100755 --- a/tools/nut-usbinfo.pl +++ b/tools/nut-usbinfo.pl @@ -1,11 +1,13 @@ #!/usr/bin/env perl -# Current Version : 1.3 +# Current Version : 1.5 # Copyright (C) 2008 - 2012 dloic (loic.dardant AT gmail DOT com) # Copyright (C) 2008 - 2015 Arnaud Quette # Copyright (C) 2013 - 2014 Charles Lepple +# Copyright (C) 2014 - 2024 Jim Klimov # -# Based on the usbdevice.pl script, made for the Ubuntu Media Center +# Based on the usbdevice.pl script, made for the Ubuntu Media Center # for the final use of the LIRC project. +# Syntax dumbed down a notch to support old perl interpreters. # # 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 @@ -51,13 +53,11 @@ # BSD devd output file my $output_devd="$TOP_BUILDDIR/scripts/devd/nut-usb.conf.in"; -# UPower output file -my $outputUPower="$TOP_BUILDDIR/scripts/upower/95-upower-hid.rules"; +# FreeBSD/pfSense/... quirks output file +my $output_freebsd_quirks="$TOP_BUILDDIR/scripts/devd/nut-usb.quirks"; -# tmp output, to allow generating the ENV{UPOWER_VENDOR} header list -my $tmpOutputUPower; -# mfr header flag -my $upowerMfrHeaderDone = 0; +# UPower output file +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"; @@ -94,7 +94,8 @@ sub gen_usb_files { # Hotplug file header - open my $outHotplug, ">$outputHotplug" || die "error $outputHotplug : $!"; + my $outHotplug = do {local *OUT_HOTPLUG}; + open $outHotplug, ">$outputHotplug" || die "error $outputHotplug : $!"; print $outHotplug '# This file is generated and installed by the Network UPS Tools package.'."\n"; print $outHotplug "#\n"; print $outHotplug '# Sample entry (replace 0xVVVV and 0xPPPP with vendor ID and product ID respectively) :'."\n"; @@ -106,7 +107,8 @@ sub gen_usb_files print $outHotplug ' bInterfaceProtocol driver_info'."\n"; # Udev file header - open my $outUdev, ">$outputUdev" || die "error $outputUdev : $!"; + my $outUdev = do {local *OUT_UDEV}; + open $outUdev, ">$outputUdev" || die "error $outputUdev : $!"; print $outUdev '# This file is generated and installed by the Network UPS Tools package.'."\n\n"; print $outUdev 'ACTION=="remove", GOTO="nut-usbups_rules_end"'."\n"; print $outUdev 'SUBSYSTEM=="usb_device", GOTO="nut-usbups_rules_real"'."\n"; @@ -114,55 +116,68 @@ sub gen_usb_files print $outUdev 'GOTO="nut-usbups_rules_end"'."\n\n"; print $outUdev 'LABEL="nut-usbups_rules_real"'."\n"; - open my $out_devd, ">$output_devd" || die "error $output_devd : $!"; + my $out_devd = do {local *OUT_DEVD}; + open $out_devd, ">$output_devd" || die "error $output_devd : $!"; print $out_devd '# This file is generated and installed by the Network UPS Tools package.'."\n"; - print $out_devd "# Homepage: http://www.networkupstools.org/\n\n"; + print $out_devd "# Homepage: https://www.networkupstools.org/\n\n"; + + my $out_freebsd_quirks = do {local *OUT_FREEBSD_QUIRKS}; + open $out_freebsd_quirks, ">$output_freebsd_quirks" || die "error $output_freebsd_quirks : $!"; + print $out_freebsd_quirks '# This file is generated and installed by the Network UPS Tools package.'."\n"; + print $out_freebsd_quirks "# Homepage: https://www.networkupstools.org/\n"; + print $out_freebsd_quirks "# Contents should be added to /boot/loader.conf.local (watch out for unique quirk numbers!)\n"; + print $out_freebsd_quirks "# Inspired by 'Notes on USB quirks' under https://forum.netgate.com/topic/183961/nut-package-2-8-1-and-above\n"; + print $out_freebsd_quirks "# and https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/quirk/usb_quirk.c\n\n"; # UPower file header - open my $outputUPower, ">$outputUPower" || die "error $outputUPower : $!"; - print $outputUPower '##############################################################################################################'."\n"; - print $outputUPower '# Uninterruptible Power Supplies with USB HID interfaces'."\n#\n"; - print $outputUPower '# 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"; + my $outUPower = do {local *OUT_UPOWER}; + open $outUPower, ">$outputUPower" || die "error $outputUPower : $!"; + print $outUPower '##############################################################################################################'."\n"; + print $outUPower '# Uninterruptible Power Supplies with USB HID interfaces'."\n#\n"; + print $outUPower '# This file was automatically generated by NUT:'."\n"; + print $outUPower '# https://github.com/networkupstools/nut/'."\n#\n"; + print $outUPower '# To keep up to date, monitor upstream NUT'."\n"; + print $outUPower '# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.hwdb'."\n"; + print $outUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n"; # Device scanner header - open my $outputDevScanner, ">$outputDevScanner" || die "error $outputDevScanner : $!"; - print $outputDevScanner '/* nutscan-usb'.$GPL_header."\n */\n\n"; - print $outputDevScanner "#ifndef DEVSCAN_USB_H\n#define DEVSCAN_USB_H\n\n"; - print $outputDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t etc. */\n\n"; - print $outputDevScanner "#include \t/* for PATH_MAX in usb.h etc. */\n\n"; - print $outputDevScanner "#include \t/* for MAXPATHLEN etc. */\n\n"; - print $outputDevScanner "/* libusb header file */\n"; - print $outputDevScanner "#if (!WITH_LIBUSB_1_0) && (!WITH_LIBUSB_0_1)\n"; - print $outputDevScanner "#error \"configure script error: Neither WITH_LIBUSB_1_0 nor WITH_LIBUSB_0_1 is set\"\n"; - print $outputDevScanner "#endif\n\n"; - print $outputDevScanner "#if (WITH_LIBUSB_1_0) && (WITH_LIBUSB_0_1)\n"; - print $outputDevScanner "#error \"configure script error: Both WITH_LIBUSB_1_0 and WITH_LIBUSB_0_1 are set\"\n"; - print $outputDevScanner "#endif\n\n"; - print $outputDevScanner "#if WITH_LIBUSB_1_0\n"; - print $outputDevScanner " #include \n"; - print $outputDevScanner "#endif\n"; - print $outputDevScanner "#if WITH_LIBUSB_0_1\n"; - print $outputDevScanner " #include \n"; - print $outputDevScanner " /* simple remap to avoid bloating structures */\n"; - print $outputDevScanner " typedef usb_dev_handle libusb_device_handle;\n"; - print $outputDevScanner "#endif\n"; + my $outDevScanner = do {local *OUT_DEV_SCANNER}; + open $outDevScanner, ">$outputDevScanner" || die "error $outputDevScanner : $!"; + print $outDevScanner "/* nutscan-usb.h - header with USB identifiers known to NUT drivers\n"; + print $outDevScanner " * This file was automatically generated during NUT build by 'tools/nut-usbinfo.pl'\n *"; + print $outDevScanner $GPL_header."\n */\n\n"; + print $outDevScanner "#ifndef DEVSCAN_USB_H\n#define DEVSCAN_USB_H\n\n"; + print $outDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t etc. */\n\n"; + print $outDevScanner "#include \t/* for PATH_MAX in usb.h etc. */\n\n"; + print $outDevScanner "#include \t/* for MAXPATHLEN etc. */\n\n"; + print $outDevScanner "/* libusb header file */\n"; + print $outDevScanner "#if (!WITH_LIBUSB_1_0) && (!WITH_LIBUSB_0_1)\n"; + print $outDevScanner "#error \"configure script error: Neither WITH_LIBUSB_1_0 nor WITH_LIBUSB_0_1 is set\"\n"; + print $outDevScanner "#endif\n\n"; + print $outDevScanner "#if (WITH_LIBUSB_1_0) && (WITH_LIBUSB_0_1)\n"; + print $outDevScanner "#error \"configure script error: Both WITH_LIBUSB_1_0 and WITH_LIBUSB_0_1 are set\"\n"; + print $outDevScanner "#endif\n\n"; + print $outDevScanner "#if WITH_LIBUSB_1_0\n"; + print $outDevScanner "# include \n"; + print $outDevScanner "#endif\n"; + print $outDevScanner "#if WITH_LIBUSB_0_1\n"; + print $outDevScanner "# ifdef HAVE_USB_H\n"; + print $outDevScanner "# include \n"; + print $outDevScanner "# else\n"; + print $outDevScanner "# ifdef HAVE_LUSB0_USB_H\n"; + print $outDevScanner "# include \n"; + print $outDevScanner "# else\n"; + print $outDevScanner "# error \"configure script error: Neither HAVE_USB_H nor HAVE_LUSB0_USB_H is set for the WITH_LIBUSB_0_1 build\"\n"; + print $outDevScanner "# endif\n"; + print $outDevScanner "# endif\n"; + print $outDevScanner " /* simple remap to avoid bloating structures */\n"; + print $outDevScanner " typedef usb_dev_handle libusb_device_handle;\n"; + print $outDevScanner "#endif\n"; # vid, pid, driver - print $outputDevScanner "typedef struct {\n\tuint16_t\tvendorID;\n\tuint16_t\tproductID;\n\tchar*\tdriver_name;\n} usb_device_id_t;\n\n"; - print $outputDevScanner "/* USB IDs device table */\nstatic usb_device_id_t usb_device_table[] = {\n\n"; + print $outDevScanner "typedef struct {\n\tuint16_t\tvendorID;\n\tuint16_t\tproductID;\n\tchar*\tdriver_name;\n\tchar*\talt_driver_names;\n} usb_device_id_t;\n\n"; + print $outDevScanner "/* USB IDs device table */\nstatic usb_device_id_t usb_device_table[] = {\n\n"; + my $entryNumber = 0; # generate the file in alphabetical order (first for VendorID, then for ProductID) foreach my $vendorId (sort { lc $a cmp lc $b } keys %vendorName) { @@ -181,9 +196,13 @@ sub gen_usb_files print $out_devd "\n# ".$vendorName{$vendorId}."\n"; } + # FreeBSD quirks vendor header + if ($vendorName{$vendorId}) { + print $out_freebsd_quirks "\n# ".$vendorName{$vendorId}."\n"; + } # UPower vendor header flag - $upowerMfrHeaderDone = 0; + my $upowerVendorHasDevices = 0; foreach my $productId (sort { lc $a cmp lc $b } keys %{$vendor{$vendorId}}) { @@ -193,54 +212,69 @@ sub gen_usb_files print $outHotplug " 0x00 0x00 0x00 0x00 0x00 0x00000000\n"; # udev device entry - print $outUdev "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"driver"}."\n"; + print $outUdev "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"drivers"}."\n"; print $outUdev "ATTR{idVendor}==\"".removeHexPrefix($vendorId); print $outUdev "\", ATTR{idProduct}==\"".removeHexPrefix($productId)."\","; print $outUdev ' MODE="664", GROUP="@RUN_AS_GROUP@"'."\n"; # devd device entry - print $out_devd "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"driver"}."\n"; + print $out_devd "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"drivers"}."\n"; print $out_devd "notify 100 {\n\tmatch \"system\"\t\t\"USB\";\n"; print $out_devd "\tmatch \"subsystem\"\t\"DEVICE\";\n"; print $out_devd "\tmatch \"type\"\t\t\"ATTACH\";\n"; print $out_devd "\tmatch \"vendor\"\t\t\"$vendorId\";\n"; - # print $out_devd "\tmatch \"product\"\t\t\"$productId\";\n"; print $out_devd "\taction \"chgrp \@RUN_AS_GROUP\@ /dev/\$cdev; chmod g+rw /dev/\$cdev\";\n"; print $out_devd "};\n"; + # FreeBSD quirks device entry + # e.g. hw.usb.quirk.1="0x051d 0x0003 0x0000 0xffff UQ_HID_IGNORE" + print $out_freebsd_quirks "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"drivers"}."\n"; + print $out_freebsd_quirks "hw.usb.quirk." . $entryNumber . "=\"" . $vendorId . " " . $productId . " 0x0000 0xffff UQ_HID_IGNORE\"\n"; + # UPower device entry (only for USB/HID devices!) - if ($vendor{$vendorId}{$productId}{"driver"} eq "usbhid-ups") + if ($vendor{$vendorId}{$productId}{"driver"} eq "usbhid-ups" || + $vendor{$vendorId}{$productId}{"driver"} eq "apc_modbus") { - if ($upowerMfrHeaderDone == 0) - { - # UPower vendor header + if (!$upowerVendorHasDevices) { if ($vendorName{$vendorId}) { - $tmpOutputUPower = $tmpOutputUPower."\n# ".$vendorName{$vendorId}."\n"; + print $outUPower "\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 $outUPower "usb:v".uc(removeHexPrefix($vendorId))."p".uc(removeHexPrefix($productId))."*\n"; } # Device scanner entry - print $outputDevScanner "\t{ ".$vendorId.', '.$productId.", \"".$vendor{$vendorId}{$productId}{"driver"}."\" },\n"; + print $outDevScanner "\t{ ".$vendorId.', '.$productId.", \"".$vendor{$vendorId}{$productId}{"driver"}."\", "; + if (index($vendor{$vendorId}{$productId}{"drivers"}, " ") != -1) { + my $otherDrivers = $vendor{$vendorId}{$productId}{"drivers"}; + $otherDrivers =~ s/$vendor{$vendorId}{$productId}{"driver"}//; + $otherDrivers =~ s/ / /; + $otherDrivers =~ s/^ //; + $otherDrivers =~ s/ $//; + print $outDevScanner "\"".$otherDrivers."\""; + } else { + print $outDevScanner "NULL"; + } + print $outDevScanner " },\n"; + + $entryNumber++; + } + + if ($upowerVendorHasDevices) { + print $outUPower " UPOWER_BATTERY_TYPE=ups\n"; + if ($vendorName{$vendorId}) { + print $outUPower " 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"; + print $outDevScanner "\n\t/* Terminating entry */\n\t{ 0, 0, NULL, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; } sub find_usbdevs @@ -251,8 +285,9 @@ sub find_usbdevs my $nameFile=$_; my $lastComment=""; - open my $file,$nameFile or die "error open file $nameFile"; - while(my $line=<$file>) + my $file = do {local *FILE}; + open ($file, $nameFile) or die "error open file $nameFile"; + while(my $line = <$file>) { # catch comment (should permit comment on the precedent or on the current line of USB_DEVICE declaration) if($line =~/\s*\/\*(.+)\*\/\s*$/) @@ -270,10 +305,11 @@ sub find_usbdevs # Format: # /* vendor name */ # #define VENDORID 0x???? + my $fh = do {local *FH}; if(!($VendorID=~/\dx(\d|\w)+/)) { - open my $fh,$nameFile or die "error open file $nameFile"; - while(my $data=<$fh>) + open $fh, $nameFile or die "error open file $nameFile"; + while(my $data = <$fh>) { # catch Vendor Name if($data =~/\s*\/\*(.+)\*\/\s*$/) @@ -287,11 +323,13 @@ sub find_usbdevs last; } } + + close $fh; } # same thing for the productID if(!($ProductID=~/\dx(\d|\w)+/)) { - my $data = do { open my $fh, $nameFile or die "error open file $nameFile"; join '', <$fh> }; + my $data = do { open $fh, $nameFile or die "error open file $nameFile"; join '', <$fh> }; if ($data =~ /(#define|#DEFINE)\s+$ProductID\s+(\dx(\d|\w)+)/) { $ProductID=$2; @@ -302,6 +340,9 @@ sub find_usbdevs } } + $VendorID=lc($VendorID); + $ProductID=lc($ProductID); + # store data (to be optimized) # and don't overwrite actual vendor names with empty values if( (!$vendorName{$VendorID}) or (($vendorName{$VendorID} eq "") and ($VendorName ne "")) ) @@ -311,23 +352,84 @@ sub find_usbdevs $vendor{$VendorID}{$ProductID}{"comment"}=$lastComment; # process the driver name my $driver=$nameFile; - if($nameFile=~/(.+)-hid\.c/) { + my $preferDriver=1; + if($nameFile=~/(.+)-hid\.c$/) { $driver="usbhid-ups"; } # generic matching rule *.c => * elsif ($nameFile =~ /(.+)\.c$/) { $driver=$1; } + elsif ($nameFile =~ /(.+)\.(orig|bak|tmp)/) { + return; + } + elsif ($nameFile =~ /(.+)_(BACKUP|LOCAL|REMOTE|BASE)_\d*/) { + return; + } else { - die "Unknown driver type: $nameFile"; + warn "Unknown driver type: $nameFile"; + next; } - if ($vendor{$VendorID}{$ProductID}{"driver"} && $ENV{"DEBUG"}) { - print STDERR "nut-usbinfo.pl: VendorID=$VendorID ProductID=$ProductID " . - "was already related to driver '" . - $vendor{$VendorID}{$ProductID}{"driver"} . - "' and changing to '$driver'\n"; + if ($vendor{$VendorID}{$ProductID}{"driver"}) { + if ($driver ne $vendor{$VendorID}{$ProductID}{"driver"}) { + # FIXME: Prefer apc_modbus to usbhid-ups in builds + # with libmodbus versions which support USB + if ($vendor{$VendorID}{$ProductID}{"driver"} eq "usbhid-ups" + || $vendor{$VendorID}{$ProductID}{"driver"} eq "nutdrv_qx" + || (index($vendor{$VendorID}{$ProductID}{"driver"}, "blazer_") == 0 && $driver ne "nutdrv_qx") + ) { + # This newly seen driver is not as cool + # as the one we already saw earlier. + $preferDriver = 0; + } + } + if ($ENV{"DEBUG"}) { + if ($preferDriver) { + print STDERR "nut-usbinfo.pl: VendorID=$VendorID ProductID=$ProductID " . + "was already related to driver '" . + $vendor{$VendorID}{$ProductID}{"driver"} . + "' and changing to '$driver' as latest hit\n"; + } else { + print STDERR "nut-usbinfo.pl: VendorID=$VendorID ProductID=$ProductID " . + "was already related to driver '" . + $vendor{$VendorID}{$ProductID}{"driver"} . + "' and now also to '$driver'; keeping original as more preferred\n"; + } + } + + # \Q \E magic is only since perl 5.16 so preferring index instead: + if ($ENV{"DEBUG"}) { + print STDERR "nut-usbinfo.pl: checking " . + "list='" . $vendor{$VendorID}{$ProductID}{"drivers"} . "'" . + " l1=" . (index($vendor{$VendorID}{$ProductID}{"drivers"}, " " . $driver . " ")) . + " l2=" . (index($vendor{$VendorID}{$ProductID}{"drivers"}, $driver . " ")) . + " l3=" . (index($vendor{$VendorID}{$ProductID}{"drivers"}, " " . $driver)) . + " l4=" . (length($vendor{$VendorID}{$ProductID}{"drivers"}) - length($driver) - 1) . + "\n"; + } + + if (index($vendor{$VendorID}{$ProductID}{"drivers"}, " " . $driver . " ") > -1 + || index($vendor{$VendorID}{$ProductID}{"drivers"}, $driver . " ") == 0 + || (index($vendor{$VendorID}{$ProductID}{"drivers"}, " " . $driver) == length($vendor{$VendorID}{$ProductID}{"drivers"}) - length($driver) - 1 + && index($vendor{$VendorID}{$ProductID}{"drivers"}, " " . $driver) > -1) + ) { + if ($ENV{"DEBUG"}) { + print STDERR "nut-usbinfo.pl: driver '$driver' was already listed for VendorID=$VendorID ProductID=$ProductID\n"; + } + } else { + $vendor{$VendorID}{$ProductID}{"drivers"} .= " " . $driver; + if ($ENV{"DEBUG"}) { + print STDERR "nut-usbinfo.pl: added '$driver' to list for VendorID=$VendorID ProductID=$ProductID, now: " . $vendor{$VendorID}{$ProductID}{"drivers"} . "\n"; + } + } + } else { + # First hit + $vendor{$VendorID}{$ProductID}{"drivers"} = $driver; + } + + if ($preferDriver) { + $vendor{$VendorID}{$ProductID}{"driver"} = $driver; } - $vendor{$VendorID}{$ProductID}{"driver"}=$driver; } } } diff --git a/tools/nutconf/Makefile.am b/tools/nutconf/Makefile.am index ab322a331e..53663e4a53 100644 --- a/tools/nutconf/Makefile.am +++ b/tools/nutconf/Makefile.am @@ -1,3 +1,5 @@ +# Network UPS Tools: NUT configuration tool + all: $(bin_PROGRAMS) bin_PROGRAMS = @@ -6,7 +8,7 @@ bin_PROGRAMS = # so we do not build it unless explicitly asked to. if WITH_NUTCONF bin_PROGRAMS += nutconf -nutconf_SOURCES = nutconf.cpp +nutconf_SOURCES = nutconf-cli.cpp nutconf_CXXFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include nutconf_LDADD = $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libnutconf.la @@ -15,16 +17,15 @@ nutconf_LDADD = $(top_builddir)/common/libcommon.la \ if WITH_LIBLTDL nutconf_CXXFLAGS += -DWITH_NUTSCANNER -I$(top_srcdir)/tools/nut-scanner nutconf_LDADD += $(top_builddir)/tools/nut-scanner/libnutscan.la -endif +endif WITH_LIBLTDL -# End of WITH_NUTCONF -endif +endif WITH_NUTCONF # Make sure out-of-dir dependencies exist (especially when dev-building parts): $(top_builddir)/common/libcommon.la \ $(top_builddir)/common/libnutconf.la \ $(top_builddir)/tools/nut-scanner/libnutscan.la: dummy - @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) dummy: diff --git a/tools/nutconf/nutconf.cpp b/tools/nutconf/nutconf-cli.cpp similarity index 84% rename from tools/nutconf/nutconf.cpp rename to tools/nutconf/nutconf-cli.cpp index d979da44b8..f20a3eb724 100644 --- a/tools/nutconf/nutconf.cpp +++ b/tools/nutconf/nutconf-cli.cpp @@ -16,18 +16,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/*! \file nutscan-display.c - \brief format and display scanned devices +/*! \file nutconf.cpp + \brief NUT configuration tool \author Frederic Bohe \author Vaclav Krpec */ #include "config.h" -#include "nutconf.h" +#include "nutconf.hpp" #include "nutstream.hpp" extern "C" { -#include "nut_platform.h" #if (defined WITH_NUTSCANNER) #include "nut-scan.h" #include "nutscan-init.h" @@ -62,104 +61,104 @@ class Usage { const char * Usage::s_text[] = { -" -h -help", -" --help Display this help and exit", -" --autoconfigure Perform autoconfiguration", -" --is-configured Checks whether NUT is configured", -" --local Sets configuration directory", -" --system Sets configuration directory to " CONFPATH " (default)", -" --get-mode Gets NUT mode (see below)", -" --set-mode Sets NUT mode (see below)", -" --set-monitor Configures one monitor (see below)", -" All existing entries are removed; however, it may be", -" specified multiple times to set multiple entries", -" --add-monitor Same as --set-monitor, but keeps existing entries", -" The two options are mutually exclusive", -" --set-listen [] Configures one listen address for the NUT daemon", -" All existing entries are removed; however, it may be", -" specified multiple times to set multiple entries", -" --add-listen [] Same as --set-listen, but keeps existing entries", -" The two options are mutually exclusive", -" --set-device Configures one UPS device (see below)", -" All existing devices are removed; however, it may be", -" specified multiple times to set multiple devices", -" --add-device Same as --set-device, but keeps existing devices", -" The two options are mutually exclusive", -" --set-notifyflags + Configures notify flags for notification type", -" See below for the types and supported flags", -" Existing flags are replaced", -" --add-notifyflags + Same as --set-notifyflags, but keeps existing flags", -" --set-notifymsg Configures notification message for the type", -" --set-notifycmd Configures notification command", -" --set-shutdowncmd Configures shutdown command", -" --set-minsupplies Configures minimum of required power supplies", -" --set-powerdownflag Configures powerdown flag file", -" --set-user Configures one user (see below)", -" All existing users are removed; however, it may be", -" specified multiple times to set multiple users", -" --add-user Same as --set-user, but keeps existing users", -" The two options are mutually exclusive", -" -v", -" --verbose Increase verbosity of output one level", -" May be specified multiple times", + " -h -help", + " --help Display this help and exit", + " --autoconfigure Perform automatic configuration", + " --is-configured Checks whether NUT is configured", + " --local Sets configuration directory", + " --system Sets configuration directory to " CONFPATH " (default)", + " --get-mode Gets NUT mode (see below)", + " --set-mode Sets NUT mode (see below)", + " --set-monitor Configures one monitor (see below)", + " All existing entries are removed; however, it may be", + " specified multiple times to set multiple entries", + " --add-monitor Same as --set-monitor, but keeps existing entries", + " The two options are mutually exclusive", + " --set-listen [] Configures one listen address for the NUT daemon", + " All existing entries are removed; however, it may be", + " specified multiple times to set multiple entries", + " --add-listen [] Same as --set-listen, but keeps existing entries", + " The two options are mutually exclusive", + " --set-device Configures one UPS device (see below)", + " All existing devices are removed; however, it may be", + " specified multiple times to set multiple devices", + " --add-device Same as --set-device, but keeps existing devices", + " The two options are mutually exclusive", + " --set-notifyflags + Configures notify flags for notification type", + " See below for the types and supported flags", + " Existing flags are replaced", + " --add-notifyflags + Same as --set-notifyflags, but keeps existing flags", + " --set-notifymsg Configures notification message for the type", + " --set-notifycmd Configures notification command", + " --set-shutdowncmd Configures shutdown command", + " --set-minsupplies Configures minimum of required power supplies", + " --set-powerdownflag Configures powerdown flag file", + " --set-user Configures one user (see below)", + " All existing users are removed; however, it may be", + " specified multiple times to set multiple users", + " --add-user Same as --set-user, but keeps existing users", + " The two options are mutually exclusive", + " -v", + " --verbose Increase verbosity of output one level", + " May be specified multiple times", #if (defined WITH_NUTSCANNER) #if (defined WITH_SNMP) -" --scan-snmp Scan SNMP devices (see below)", -" May be specified multiple times", + " --scan-snmp Scan SNMP devices (see below)", + " May be specified multiple times", #endif // defined WITH_SNMP #if (defined WITH_USB) -" --scan-usb Scan USB devices", + " --scan-usb Scan USB devices", #endif // defined WITH_USB #if (defined WITH_NEON) -" --scan-xml-http [] Scan XML/HTTP devices (optional timeout in us)", + " --scan-xml-http [] Scan XML/HTTP devices (optional timeout in us)", #endif // defined WITH_NEON -" --scan-nut Scan NUT devices (see below for the specs)", -" May be specified multiple times", + " --scan-nut Scan NUT devices (see below for the specs)", + " May be specified multiple times", #if (defined WITH_AVAHI) -" --scan-avahi [] Scan Avahi devices (optional timeout in us)", + " --scan-avahi [] Scan Avahi devices (optional timeout in us)", #endif // defined WITH_AVAHI #if (defined WITH_IPMI) -" --scan-ipmi Scan IPMI devices (see below)", -" May be specified multiple times", + " --scan-ipmi Scan IPMI devices (see below)", + " May be specified multiple times", #endif // defined WITH_IPMI -" --scan-serial * Scan for serial devices on specified ports", + " --scan-serial * Scan for serial devices on specified ports", #endif // defined WITH_NUTSCANNER -"", -"NUT modes: standalone, netserver, netclient, controlled, manual, none", -"Monitor is specified by the following sequence:", -" [:] (\"master\"|\"slave\")", -"UPS device is specified by the following sequence:", -" [=]*", -"Notification types:", -" ONLINE, ONBATT, LOWBATT, FSD, COMMOK, COMMBAD, SHUTDOWN, REPLBATT, NOCOMM, NOPARENT", -"Notification flags:", -" SYSLOG, WALL, EXEC, IGNORE", -"User specification:", -"The very 1st argument is the username (but see the upsmon exception, below).", -"Next arguments use scheme of key/value pairs in form =", -"Known keys are:", -" password, actions (from {SET,FSD}), instcmds (accepted multiple times)", -"Specially, for the upsmon user, the 1st argument takes form of", -" upsmon={master|slave}", + "", + "NUT modes: standalone, netserver, netclient, controlled, manual, none", + "Monitor is specified by the following sequence:", + " [:] (\"master\"|\"slave\")", + "UPS device is specified by the following sequence:", + " [=]*", + "Notification types:", + " ONLINE, ONBATT, LOWBATT, FSD, COMMOK, COMMBAD, SHUTDOWN, REPLBATT, NOCOMM, NOPARENT", + "Notification flags:", + " SYSLOG, WALL, EXEC, IGNORE", + "User specification:", + "The very 1st argument is the username (but see the upsmon exception, below).", + "Next arguments use scheme of key/value pairs in form =", + "Known keys are:", + " password, actions (from {SET,FSD}), instcmds (accepted multiple times)", + "Specially, for the upsmon user, the 1st argument takes form of", + " upsmon={master|slave}", #if (defined WITH_NUTSCANNER) #if (defined WITH_SNMP) -"SNMP device scan specification:", -" [=]*", -"Known attributes are:", -" timeout (in us), community, sec-level, sec-name, auth-password, priv-password,", -" auth-protocol, priv-protocol, peer-name", + "SNMP device scan specification:", + " [=]*", + "Known attributes are:", + " timeout (in us), community, sec-level, sec-name, auth-password, priv-password,", + " auth-protocol, priv-protocol, peer-name", #endif // defined WITH_SNMP -"NUT device scan specification:", -" []", + "NUT device scan specification:", + " []", #if (defined WITH_IPMI) -"IMPI device scan specification:", -" [=]*", -"Known attributes are:", -" username, password, auth-type, cipher-suite-id, K-g-BMC-key, priv-level,", -" workaround-flags, version", + "IMPI device scan specification:", + " [=]*", + "Known attributes are:", + " username, password, auth-type, cipher-suite-id, K-g-BMC-key, priv-level,", + " workaround-flags, version", #endif // defined WITH_IPMI #endif // defined WITH_NUTSCANNER -"", + "", }; @@ -198,7 +197,7 @@ class Options { doubleDash, /**< Double-dash prefixed option */ } type_t; - /** Arguments of the last option processed (\c NULL means bin. args) */ + /** Arguments of the last option processed (\c nullptr means bin. args) */ Arguments * m_last; /** Binary arguments */ @@ -224,7 +223,7 @@ class Options { * \param arg Argument */ inline void addArg(const std::string & arg) { - Arguments * args = NULL != m_last ? m_last : &m_args; + Arguments * args = nullptr != m_last ? m_last : &m_args; args->push_back(arg); } @@ -247,7 +246,7 @@ class Options { * \param[out] args Option arguments * \param[in] order Option order (1st by default) * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ bool get(const Map & map, const std::string & opt, Arguments & args, size_t order = 0) const; @@ -317,7 +316,7 @@ class Options { * * \param opt Option * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ inline bool existsSingle(const std::string & opt) const { @@ -329,7 +328,7 @@ class Options { * * \param opt Option * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ inline bool existsDouble(const std::string & opt) const { @@ -341,7 +340,7 @@ class Options { * * \param opt Option * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ inline bool exists(const std::string & opt) const { @@ -355,7 +354,7 @@ class Options { * \param[out] args Option arguments * \param[in] order Option order (1st by default) * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ inline bool getSingle(const std::string & opt, Arguments & args, size_t order = 0) const { @@ -369,7 +368,7 @@ class Options { * \param[out] args Option arguments * \param[in] order Option order (1st by default) * - * \retval true iff the option was specified on the command line + * \retval true IFF the option was specified on the command line * \retval false otherwise */ inline bool getDouble(const std::string & opt, Arguments & args, size_t order = 0) const { @@ -432,6 +431,41 @@ void Options::add(Options::type_t type, const std::string & opt) { map = &m_double; 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 + default: + /* Must not occur thanks to enum. + * But otherwise we can see + * error: 'map' may be used uninitialized + * from some overly zealous compilers. + */ + if (1) { // scoping + std::stringstream e; + + e << "Options::add() got unsupported enum value"; + + throw std::logic_error(e.str()); + } +#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 } Map::iterator entry = map->insert(Map::value_type(opt, Arguments())); @@ -515,12 +549,13 @@ void Options::dump(std::ostream & stream) const { } -Options::Options(char * const argv[], int argc): m_last(NULL) { +Options::Options(char * const argv[], int argc): m_last(nullptr) { for (int i = 1; i < argc; ++i) { const std::string arg(argv[i]); // Empty string is the current option argument, too - // '-' alone is also an option argument // (like stdout placeholder etc) + // '-' alone is also an option argument + // (like stdout placeholder etc) if (arg.empty() || '-' != arg[0] || 1 == arg.size()) addArg(arg); @@ -531,7 +566,7 @@ Options::Options(char * const argv[], int argc): m_last(NULL) { // "--" alone is valid as it means that what follows // belongs to the binary ("empty" option arguments) else if (2 == arg.size()) - m_last = NULL; + m_last = nullptr; // Double-dashed option else if ('-' != arg[2]) @@ -636,17 +671,17 @@ class NutScanner { private: - /** NUT scanner initialisation/finalisation */ + /** NUT scanner initialization/finalization */ struct InitFinal { - /** Initialisation */ + /** Initialization */ InitFinal() { nutscan_init(); } - /** Finalisation */ + /** Finalization */ ~InitFinal() { nutscan_free(); } }; // end of struct InitFinal - /** Initialiser / finaliser */ + /** Initializer / finalizer */ static InitFinal s_init_final; /** @@ -656,7 +691,7 @@ class NutScanner { * * \param dev_list nut-scan provided device list * - * \return Dvice info list + * \return Device info list */ static devices_t dev2list(nutscan_device_t * dev_list); @@ -678,7 +713,7 @@ class NutScanner { static devices_t devicesSNMP( const std::string & start_ip, const std::string & stop_ip, - long us_timeout, + useconds_t us_timeout, const SNMPAttributes & attrs); /** @@ -687,7 +722,12 @@ class NutScanner { * \return Device list */ inline static devices_t devicesUSB() { - nutscan_device_t * dev = nutscan_scan_usb(); + // FIXME: Since NUT v2.8.2 nutscan_scan_usb accepts + // a `nutscan_usb_t * scanopts` to tweak what values + // it reports -- make use of it in this class. + // A nullptr value causes safe defaults to be used, + // as decided by the library. + nutscan_device_t * dev = ::nutscan_scan_usb(nullptr); return dev2list(dev); } @@ -699,17 +739,17 @@ class NutScanner { * * \return Device list */ - inline static devices_t devicesXMLHTTP(long us_timeout) { + inline static devices_t devicesXMLHTTP(useconds_t us_timeout) { nutscan_xml_t xml_sec; nutscan_device_t * dev; - memset(&xml_sec, 0, sizeof(xml_sec)); + ::memset(&xml_sec, 0, sizeof(xml_sec)); /* Set the default values for XML HTTP (run_xml()) */ xml_sec.port_http = 80; xml_sec.port_udp = 4679; xml_sec.usec_timeout = us_timeout; - xml_sec.peername = NULL; - dev = nutscan_scan_xml_http_range(NULL, NULL, us_timeout, &xml_sec); + xml_sec.peername = nullptr; + dev = ::nutscan_scan_xml_http_range(nullptr, nullptr, us_timeout, &xml_sec); return dev2list(dev); } @@ -728,9 +768,9 @@ class NutScanner { const std::string & start_ip, const std::string & stop_ip, const std::string & port, - long us_timeout) + useconds_t us_timeout) { - nutscan_device_t * dev = nutscan_scan_nut( + nutscan_device_t * dev = ::nutscan_scan_nut( start_ip.c_str(), stop_ip.c_str(), port.c_str(), us_timeout); return dev2list(dev); @@ -743,8 +783,8 @@ class NutScanner { * * \return Device list */ - inline static devices_t devicesAvahi(long us_timeout) { - nutscan_device_t * dev = nutscan_scan_avahi(us_timeout); + inline static devices_t devicesAvahi(useconds_t us_timeout) { + nutscan_device_t * dev = ::nutscan_scan_avahi(us_timeout); return dev2list(dev); } @@ -778,12 +818,12 @@ NutScanner::Device::options_t NutScanner::Device::createOptions(nutscan_options_ options_t options; // Create options - for (; NULL != opt; opt = opt->next) { - assert(NULL != opt->option); + for (; nullptr != opt; opt = opt->next) { + assert(nullptr != opt->option); options.insert( options_t::value_type(opt->option, - NULL != opt->value ? opt->value : "")); + nullptr != opt->value ? opt->value : "")); } return options; @@ -792,8 +832,8 @@ NutScanner::Device::options_t NutScanner::Device::createOptions(nutscan_options_ NutScanner::Device::Device(nutscan_device_t * dev): type(nutscan_device_type_string(dev->type)), - driver(NULL != dev->driver ? dev->driver : ""), - port(NULL != dev->port ? dev->port : ""), + driver(nullptr != dev->driver ? dev->driver : ""), + port(nullptr != dev->port ? dev->port : ""), options(createOptions(dev->opt)) {} @@ -803,7 +843,7 @@ NutScanner::devices_t NutScanner::dev2list(nutscan_device_t * dev_list) { nutscan_device_t * dev = dev_list; - for (; dev != NULL; dev = dev->next) { + for (; dev != nullptr; dev = dev->next) { // Skip devices of type NONE // TBD: This happens with the serial scan on an invalid device // Should be fixed in libnutscan I think @@ -813,7 +853,7 @@ NutScanner::devices_t NutScanner::dev2list(nutscan_device_t * dev_list) { list.push_back(Device(dev)); } - nutscan_free_device(dev_list); + ::nutscan_free_device(dev_list); return list; } @@ -822,7 +862,7 @@ NutScanner::devices_t NutScanner::dev2list(nutscan_device_t * dev_list) { NutScanner::devices_t NutScanner::devicesSNMP( const std::string & start_ip, const std::string & stop_ip, - long us_timeout, + useconds_t us_timeout, const SNMPAttributes & attrs) { nutscan_snmp_t snmp_attrs; @@ -856,7 +896,7 @@ NutScanner::devices_t NutScanner::devicesSNMP( if (!attrs.peer_name.empty()) snmp_attrs.peername = const_cast(attrs.peer_name.c_str()); - nutscan_device_t * dev = nutscan_scan_snmp( + nutscan_device_t * dev = ::nutscan_scan_snmp( start_ip.c_str(), stop_ip.c_str(), us_timeout, &snmp_attrs); return dev2list(dev); @@ -872,7 +912,7 @@ NutScanner::devices_t NutScanner::devicesIPMI( ::memset(&ipmi_attrs, 0, sizeof(ipmi_attrs)); - // TBD: const casting is necessery + // TBD: const casting is necessary // Shouldn't the nutscan_ipmi_t C-string items be constant? if (!attrs.username.empty()) @@ -891,7 +931,7 @@ NutScanner::devices_t NutScanner::devicesIPMI( ipmi_attrs.workaround_flags = attrs.wa_flags; ipmi_attrs.ipmi_version = attrs.version; - nutscan_device_t * dev = nutscan_scan_ipmi( + nutscan_device_t * dev = ::nutscan_scan_ipmi( start_ip.c_str(), stop_ip.c_str(), &ipmi_attrs); return dev2list(dev); @@ -914,7 +954,7 @@ NutScanner::devices_t NutScanner::devicesEatonSerial(const std::list class autodelete_ptr { @@ -1736,16 +1776,16 @@ class autodelete_ptr { /** Cleanup */ inline void cleanup() { - if (NULL != m_impl) + if (nullptr != m_impl) delete m_impl; - m_impl = NULL; + m_impl = nullptr; } public: /** Constructor (unset) */ - autodelete_ptr(): m_impl(NULL) {} + autodelete_ptr(): m_impl(nullptr) {} /** Constructor */ autodelete_ptr(T * ptr): m_impl(ptr) {} @@ -1769,7 +1809,7 @@ class autodelete_ptr { inline T * give() { T * ptr = m_impl; - m_impl = NULL; + m_impl = nullptr; return ptr; } @@ -1782,10 +1822,14 @@ class autodelete_ptr { private: /** Copying is forbidden */ - autodelete_ptr(const autodelete_ptr & orig) {} + autodelete_ptr(const autodelete_ptr & orig) { + NUT_UNUSED_VARIABLE(orig); + } /** Assignment is forbidden */ - autodelete_ptr & operator = (const autodelete_ptr & orig) {} + autodelete_ptr & operator = (const autodelete_ptr & orig) { + NUT_UNUSED_VARIABLE(orig); + } }; // end of template class autodelete_ptr @@ -1823,13 +1867,13 @@ void NutConfOptions::addUser(const Options::Arguments & args) { } while (0); // end of pragmatic do ... while (0) loop // Ordinary user - if (NULL == user) { + if (nullptr == user) { user = new UserSpec; user->name = name; } - assert(NULL != user); + assert(nullptr != user); // Set user attributes bool errors = false; @@ -1880,9 +1924,9 @@ void NutConfOptions::getMonitor( std::string & pwr_val, std::string & user, std::string & passwd, - std::string & mode, + std::string & mode_arg, size_t which) const -#if (defined __cplusplus) && (__cplusplus < 201700) +#if (defined __cplusplus) && (__cplusplus < 201100) throw(std::range_error) #endif { @@ -1898,7 +1942,7 @@ void NutConfOptions::getMonitor( pwr_val = monitors[base_idx + 2]; user = monitors[base_idx + 3]; passwd = monitors[base_idx + 4]; - mode = monitors[base_idx + 5]; + mode_arg = monitors[base_idx + 5]; } @@ -1916,7 +1960,7 @@ void NutConfOptions::getMonitor( * \retval true if the configuration file was sourced * \retval false if the file doesn't exist */ -bool source(nut::Serialisable * config, const std::string & file_name) { +static bool source(nut::Serialisable * config, const std::string & file_name) { nut::NutFile file(file_name); if (!file.exists()) @@ -1945,7 +1989,7 @@ bool source(nut::Serialisable * config, const std::string & file_name) { * \param config Configuration object * \param file_name File name */ -void store(nut::Serialisable * config, const std::string & file_name) { +static void store(nut::Serialisable * config, const std::string & file_name) { nut::NutFile file(file_name, nut::NutFile::WRITE_ONLY); bool written_ok = config->writeTo(file); @@ -1966,10 +2010,10 @@ void store(nut::Serialisable * config, const std::string & file_name) { * * \param etc Configuration directory * - * \retval true iff nut.conf exists and MODE != none + * \retval true IFF nut.conf exists and MODE != none * \retval false otherwise */ -bool isConfigured(const std::string & etc) { +static bool isConfigured(const std::string & etc) { nut::NutFile nut_conf_file(etc + "/nut.conf"); if (!nut_conf_file.exists()) @@ -1997,7 +2041,7 @@ bool isConfigured(const std::string & etc) { * * \return Monitor configuration */ -nut::UpsmonConfiguration::Monitor monitor( +static nut::UpsmonConfiguration::Monitor monitor( size_t i, const NutConfOptions & options) { @@ -2056,9 +2100,9 @@ nut::UpsmonConfiguration::Monitor monitor( * * \return NUT mode (as string) */ -std::string getMode(const std::string & etc) { +static std::string getMode(const std::string & etc) { std::string nut_conf_file(etc + "/nut.conf"); - + std::stringstream e; nut::NutConfiguration nut_conf; // Source previous configuration @@ -2074,13 +2118,34 @@ std::string getMode(const std::string & etc) { case nut::NutConfiguration::MODE_NETCLIENT: return "netclient"; case nut::NutConfiguration::MODE_CONTROLLED: return "controlled"; case nut::NutConfiguration::MODE_MANUAL: return "manual"; - } - std::stringstream e; +#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 + default: + break; + } e << "INTERNAL ERROR: Unknown NUT mode: " << mode; - throw std::logic_error(e.str()); +#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 } @@ -2090,7 +2155,7 @@ std::string getMode(const std::string & etc) { * \param mode Mode * \param etc Configuration directory */ -void setMode(const std::string & mode, const std::string & etc) { +static void setMode(const std::string & mode, const std::string & etc) { std::string nut_conf_file(etc + "/nut.conf"); nut::NutConfiguration nut_conf; @@ -2113,7 +2178,7 @@ void setMode(const std::string & mode, const std::string & etc) { * \param etc Configuration directory * \param keep_ex Keep existing entries (discard by default) */ -void setMonitors( +static void setMonitors( const std::list & monitors, const std::string & etc, bool keep_ex = false) { @@ -2150,7 +2215,7 @@ void setMonitors( * * \return Listen address configuration */ -nut::UpsdConfiguration::Listen listenAddr( +static nut::UpsdConfiguration::Listen listenAddr( size_t i, const NutConfOptions & options) { @@ -2188,7 +2253,7 @@ nut::UpsdConfiguration::Listen listenAddr( * \param etc Configuration directory * \param keep_ex Keep existing entries (discard by default) */ -void setListenAddrs( +static void setListenAddrs( const std::list & listen_addrs, const std::string & etc, bool keep_ex = false) { @@ -2222,7 +2287,7 @@ void setListenAddrs( * \param etc Configuration directory * \param keep_ex Keep existing entries (discard by default) */ -void setDevices( +static void setDevices( const std::vector & devices, const std::string & etc, bool keep_ex = false) { @@ -2272,7 +2337,7 @@ void setDevices( * \param flags Notify flags specifications * \param etc Configuration directory */ -void setNotifyFlags( +static void setNotifyFlags( const NutConfOptions::NotifyFlagsSpecs & flags, const std::string & etc) { @@ -2298,7 +2363,7 @@ void setNotifyFlags( ::exit(1); } - nut::Settable & sum = + nut::Settable & sum = upsmon_conf.notifyFlags[type]; // Clear current flags (unless we want to keep them) @@ -2312,7 +2377,7 @@ void setNotifyFlags( nut::UpsmonConfiguration::NotifyFlag flag = nut::UpsmonConfiguration::NotifyFlagFromString(*spec); - sum |= (unsigned short)flag; + sum |= static_cast(flag); } } @@ -2327,7 +2392,7 @@ void setNotifyFlags( * \param msgs Notify messages specifications * \param etc Configuration directory */ -void setNotifyMsgs( +static void setNotifyMsgs( const NutConfOptions::NotifyMsgSpecs & msgs, const std::string & etc) { @@ -2365,10 +2430,10 @@ void setNotifyMsgs( /** * \brief Set notify command in upsmon.conf * - * \param cmd otify command + * \param cmd Notify command * \param etc Configuration directory */ -void setNotifyCmd(const std::string & cmd, const std::string & etc) +static void setNotifyCmd(const std::string & cmd, const std::string & etc) { std::string upsmon_conf_file(etc + "/upsmon.conf"); @@ -2390,7 +2455,7 @@ void setNotifyCmd(const std::string & cmd, const std::string & etc) * \param cmd Shutdown command * \param etc Configuration directory */ -void setShutdownCmd(const std::string & cmd, const std::string & etc) +static void setShutdownCmd(const std::string & cmd, const std::string & etc) { std::string upsmon_conf_file(etc + "/upsmon.conf"); @@ -2412,7 +2477,7 @@ void setShutdownCmd(const std::string & cmd, const std::string & etc) * \param min_supplies Minimum of power supplies * \param etc Configuration directory */ -void setMinSupplies(const std::string & min_supplies, const std::string & etc) { +static void setMinSupplies(const std::string & min_supplies, const std::string & etc) { std::string upsmon_conf_file(etc + "/upsmon.conf"); nut::UpsmonConfiguration upsmon_conf; @@ -2445,7 +2510,7 @@ void setMinSupplies(const std::string & min_supplies, const std::string & etc) { * \param powerdown_flag Powerdown flag file * \param etc Configuration directory */ -void setPowerdownFlag(const std::string & powerdown_flag, const std::string & etc) { +static void setPowerdownFlag(const std::string & powerdown_flag, const std::string & etc) { std::string upsmon_conf_file(etc + "/upsmon.conf"); nut::UpsmonConfiguration upsmon_conf; @@ -2467,7 +2532,7 @@ void setPowerdownFlag(const std::string & powerdown_flag, const std::string & et * \param etc Configuration directory * \param keep_ex Keep existing entries (discard by default) */ -void setUsers( +static void setUsers( const NutConfOptions::UserSpecs & users, const std::string & etc, bool keep_ex = false) { @@ -2557,7 +2622,7 @@ void setUsers( * \param devices Device list * \param verbose Verbosity level */ -void printDevicesInfo(const NutScanner::devices_t & devices, unsigned int verbose = 0) { +static void printDevicesInfo(const NutScanner::devices_t & devices, unsigned int verbose = 0) { NutScanner::devices_t::const_iterator dev_iter = devices.begin(); nut::GenericConfiguration devices_conf; @@ -2634,7 +2699,7 @@ void printDevicesInfo(const NutScanner::devices_t & devices, unsigned int verbos * * \param options Options */ -void scanSNMPdevices(const NutConfOptions & options) { +static void scanSNMPdevices(const NutConfOptions & options) { for (size_t i = 0; ; ++i) { NutConfOptions::Arguments args; @@ -2651,7 +2716,7 @@ void scanSNMPdevices(const NutConfOptions & options) { const std::string & stop_ip = *arg++; // TBD: where should we get the default? - long us_timeout = 1000000; + useconds_t us_timeout = 1000000; NutScanner::SNMPAttributes attrs; @@ -2736,7 +2801,7 @@ void scanSNMPdevices(const NutConfOptions & options) { * * \param options Options */ -void scanUSBdevices(const NutConfOptions & options) { +static void scanUSBdevices(const NutConfOptions & options) { NutScanner::devices_t devices = NutScanner::devicesUSB(); printDevicesInfo(devices, options.verbose); @@ -2748,7 +2813,7 @@ void scanUSBdevices(const NutConfOptions & options) { * * \param options Options */ -void scanNUTdevices(const NutConfOptions & options) { +static void scanNUTdevices(const NutConfOptions & options) { for (size_t i = 0; ; ++i) { NutConfOptions::Arguments args; @@ -2766,7 +2831,7 @@ void scanNUTdevices(const NutConfOptions & options) { const std::string & port = *arg++; // TBD: where should we get the default? - long us_timeout = 1000000; + useconds_t us_timeout = 1000000; if (arg != args.end()) { std::stringstream ss(*arg); @@ -2787,7 +2852,7 @@ void scanNUTdevices(const NutConfOptions & options) { * * \param options Options */ -void scanXMLHTTPdevices(const NutConfOptions & options) { +static void scanXMLHTTPdevices(const NutConfOptions & options) { NutConfOptions::Arguments args; bool ok = options.getDouble("scan-xml-http", args); @@ -2796,7 +2861,7 @@ void scanXMLHTTPdevices(const NutConfOptions & options) { assert(ok); // TBD: where should we get the default? - long us_timeout = 1000000; + useconds_t us_timeout = 1000000; if (!args.empty()) { std::stringstream ss(args.front()); @@ -2815,7 +2880,7 @@ void scanXMLHTTPdevices(const NutConfOptions & options) { * * \param options Options */ -void scanAvahiDevices(const NutConfOptions & options) { +static void scanAvahiDevices(const NutConfOptions & options) { NutConfOptions::Arguments args; bool ok = options.getDouble("scan-avahi", args); @@ -2824,7 +2889,7 @@ void scanAvahiDevices(const NutConfOptions & options) { assert(ok); // TBD: where should we get the default? - long us_timeout = 1000000; + useconds_t us_timeout = 1000000; if (!args.empty()) { std::stringstream ss(args.front()); @@ -2843,7 +2908,7 @@ void scanAvahiDevices(const NutConfOptions & options) { * * \param options Options */ -void scanIPMIdevices(const NutConfOptions & options) { +static void scanIPMIdevices(const NutConfOptions & options) { for (size_t i = 0; ; ++i) { NutConfOptions::Arguments args; @@ -2992,7 +3057,7 @@ void scanIPMIdevices(const NutConfOptions & options) { * * \param options Options */ -void scanSerialDevices(const NutConfOptions & options) { +static void scanSerialDevices(const NutConfOptions & options) { NutConfOptions::Arguments args; bool ok = options.getDouble("scan-serial", args); @@ -3016,7 +3081,7 @@ void scanSerialDevices(const NutConfOptions & options) { * * \return 0 always (exits on error) */ -int mainx(int argc, char * const argv[]) { +static int mainx(int argc, char * const argv[]) { // Get options NutConfOptions options(argv, argc); @@ -3195,25 +3260,9 @@ int main(int argc, char * const argv[]) { catch (...) { std::cerr << "INTERNAL ERROR: exception of unknown origin caught" << std::endl - << "Please issue a bugreport to nut-upsdev@lists.alioth.debian.org" + << "Please issue a bug report to nut-upsdev@lists.alioth.debian.org" << std::endl; } ::exit(128); } - - -/* Formal do_upsconf_args implementation to satisfy linker on AIX */ -#if (defined NUT_PLATFORM_AIX) -static void do_upsconf_args_impl(char *upsname, char *var, char *val) { - std::cerr << "INTERNAL ERROR: formal do_upsconf_args called" << std::endl; - - ::exit(128); -} - -extern "C" { -void do_upsconf_args(char *upsname, char *var, char *val) { - do_upsconf_args_impl(upsname, var, val); -} -} -#endif /* end of #if (defined NUT_PLATFORM_AIX) */