diff --git a/.obs/workflows.yml b/.obs/workflows.yml
index 280b70ad01..013722d0f3 100644
--- a/.obs/workflows.yml
+++ b/.obs/workflows.yml
@@ -15,8 +15,7 @@ rebuild_master:
project: home:networkupstools:nut-stable
package: "%{SCM_REPOSITORY_NAME}"
filters:
- event:
- - push
+ event: push
branches:
only:
- master
@@ -28,5 +27,4 @@ release_tag:
source_package: "%{SCM_REPOSITORY_NAME}"
target_project: home:networkupstools:nut-releases
filters:
- event:
- - tag_push
+ event: tag_push
diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix
index ed56d50bcf..d8ab890abb 100644
--- a/Jenkinsfile-dynamatrix
+++ b/Jenkinsfile-dynamatrix
@@ -1240,17 +1240,19 @@ set | sort -n """
],
dynamatrixAxesCommonEnv: [],
dynamatrixAxesCommonEnvCartesian: [
- ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors'],
+ [ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors'] ],
[ ['BUILD_WARNFATAL=yes','BUILD_WARNOPT=hard'], ['BUILD_WARNFATAL=no','BUILD_WARNOPT=minimal'] ]
],
allowedFailure: [
dynacfgPipeline.axisCombos_STRICT_C,
+ // good by now? is in DMF... // 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!
+ 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,
+ // good by now? is in DMF... // dynacfgPipeline.axisCombos_WINDOWS_CROSS,
dynacfgPipeline.axisCombos_WINDOWS
]
], body)
diff --git a/Makefile.am b/Makefile.am
index c02d135aa4..fef1bdb21e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -222,6 +222,7 @@ all-libs-local/include:
### (consume only one of these at a time!)
### Delivers: libcommonversion.la (only version methods)
### Delivers: libparseconf.la libnutconf.la libnutwincompat.la
+### May deliver: libnutprivate-$(SEMVER)-common-all.la and libnutprivate-$(SEMVER)-common-client.la for dynamic shared-object linking, libcommonversion-private.la (dependency for the above)
### Requires-ext: include/nut_version.h
### Requires-int: libparseconf.la libcommonclient.la
all-libs-local/common: all-libs-local/include
@@ -234,18 +235,21 @@ all-libs-local/common: all-libs-local/include
### Requires-ext: common/libcommon.la common/libcommonclient.la
### Requires-ext: common/libcommonversion.la
### Requires-ext: common/libparseconf.la
+### For dynamic builds, alternately LIB-Requires-ext and Requires-ext: libnutprivate-$(SEMVER)-common*.la
### Requires-int: libupsclient.la
all-libs-local/clients: all-libs-local/common
+@NUT_VERSION_H_GENERATED=true; export NUT_VERSION_H_GENERATED; \
$(SUBDIR_TGT_RULE)
-### Delivers: libdummy.la libdummy_serial.la libdummy_upsdrvquery.la
+### Delivers: libdummy_main.la libdummy_serial.la libdummy_upsdrvquery.la
### Delivers: libdummy_mockdrv.la libserial-nutscan.la
### LIB-Requires-ext: common/libcommon.la common/libparseconf.la
### Requires-ext: common/libcommon.la common/libparseconf.la
### Requires-ext: clients/libupsclient.la (dummy-ups only)
-### Requires-int: libdummy.la libdummy_upsdrvquery.la
+### Requires-int: libdummy_main.la libdummy_upsdrvquery.la
### Requires-int: libdummy_serial.la
+### For dynamic builds, alternately LIB-Requires-ext and Requires-ext: libnutprivate-$(SEMVER)-common*.la
+### and may deliver: libnutprivate-$(SEMVER)-drivers-common.la and libnutprivate-$(SEMVER)-drivers-serial.la
all-libs-local/drivers: all-libs-local/common
+@NUT_VERSION_H_GENERATED=true; export NUT_VERSION_H_GENERATED; \
$(SUBDIR_TGT_RULE)
@@ -270,6 +274,7 @@ all-libs-local/tools:
### LIB-Requires-ext: drivers/libserial-nutscan.la
### LIB-Requires-ext: common/libnutwincompat.la common/libcommonstr.la
### LIB-Requires-ext: common/libcommonversion.la
+### For dynamic builds, alternately LIB-Requires-ext and Requires-ext: libnutprivate-$(SEMVER)-common*.la
### HDR-Requires-ext: clients/libupsclient-version.h
### HDR-Requires-ext: nut-scanner/nutscan-snmp.h nut-scanner/nutscan-usb.h
### (generated by nut-scanner-deps/tools aliased as all-libs-local/tools)
@@ -358,7 +363,7 @@ all/clients: all/common all-libs-local/clients
### Requires-ext: common/libcommon.la common/libparseconf.la
### Requires-ext: common/libcommonversion.la
### Requires-ext: clients/libupsclient.la (dummy-ups only)
-### Requires-int: libdummy.la libdummy_upsdrvquery.la
+### Requires-int: libdummy_main.la libdummy_upsdrvquery.la
### Requires-int: libdummy_serial.la
# TODO in the future: propagate the knowledge of whether we are building
@@ -528,6 +533,7 @@ DISTCHECK_CONFIGURE_FLAGS = ${DISTCHECK_FLAGS} \
--with-systemdsystempresetdir='$${prefix}/usr/lib/systemd/system-preset' \
--with-systemdshutdowndir='$${prefix}/lib/systemd/system-shutdown' \
--with-systemdtmpfilesdir='$${prefix}/usr/lib/tmpfiles.d' \
+ --with-systemdsysusersdir='$${prefix}/usr/lib/sysusers.d' \
--with-augeas-lenses-dir='$${prefix}/usr/share/augeas/lenses' \
--with-hotplug-dir='$${prefix}/etc/hotplug' \
--with-udev-dir='$${prefix}/etc/udev' \
@@ -1211,7 +1217,7 @@ distcheck-completeness: dist
&& cd "$$am__cwd" \
&& echo " $@ diff $(distdir)-orig-$$$$ $(distdir)-derived-$$$$" \
&& diff $(distdir)-orig-$$$$ $(distdir)-derived-$$$$ ; \
- } || RES=$?? ;
+ } || RES=$$? ; \
rm -rf $(distdir)-orig-$$$$ $(distdir)-derived-$$$$ $(distdir) ; \
exit $$RES
@echo "SUCCESS: $(distdir) archives are self-reproducing"
@@ -1368,6 +1374,12 @@ else !WITH_SYSTEMD_TMPFILES
WITH_SYSTEMD_TMPFILES = false
endif !WITH_SYSTEMD_TMPFILES
+if WITH_SYSTEMD_SYSUSERS
+WITH_SYSTEMD_SYSUSERS = true
+else !WITH_SYSTEMD_SYSUSERS
+WITH_SYSTEMD_SYSUSERS = false
+endif !WITH_SYSTEMD_SYSUSERS
+
if WITH_SYSTEMD_PRESET
WITH_SYSTEMD_PRESET = true
else !WITH_SYSTEMD_PRESET
@@ -1519,6 +1531,10 @@ install-as-root:
echo "$@: Apply systemd-tmpfiles presets" >&2 ; \
$(SYSTEMD_TMPFILES_PROGRAM) --create || exit ; \
fi ; \
+ if $(WITH_SYSTEMD_SYSUSERS) ; then \
+ echo "$@: Apply systemd-sysusers presets" >&2 ; \
+ $(SYSTEMD_SYSUSERS_PROGRAM) || exit ; \
+ fi ; \
echo "$@: Learn systemd definition changes" >&2 ; \
$(SYSTEMD_SYSTEMCTL_PROGRAM) daemon-reload || exit ; \
APPLIED_SYSTEMD_PRESET=false ; \
diff --git a/NEWS.adoc b/NEWS.adoc
index 2f2df4cfaa..c30766204a 100644
--- a/NEWS.adoc
+++ b/NEWS.adoc
@@ -40,12 +40,6 @@ https://github.com/networkupstools/nut/milestone/12
to patterns defined in link:docs/nut-names.txt[]
- common code:
- * Generally improved shell script portability (including `Makefile.am`
- embedded recipes) when mixing back-quotes and double-quotes. Namely,
- the classic Solaris `ksh` implementation (also used in current versions
- of Solaris and illumos based distributions) was strict about following
- (an older revision of) the POSIX standard here, and some use-cases
- misbehaved with our older script spelling. [issue #3196]
* Introduced `setproctag()` and `getproctag()` (see examples in `upsmon`)
to help track the log messages from massively-forking NUT daemons. [#3084]
* Extended with plural `checkprocnames()` and `compareprocnames()`,
@@ -64,6 +58,7 @@ https://github.com/networkupstools/nut/milestone/12
other too. [issue #3136]
* Improved high-verbosity debug tracing of NUT `libhid` and `usbhid-ups`
to help make sense of data processing issues (HID paths, strings). [#3201]
+ * Further removal of unbounded `strcpy()` and `sprintf()` usage. [#3253]
* For state tree methods, introduced `difftime_st_tree_timespec()` to
abstract platform-dependent definitions of `st_tree_timespec_t`. [PR #3156]
* Introduced global variables for last changed timestamp and value of
@@ -80,12 +75,32 @@ https://github.com/networkupstools/nut/milestone/12
systemd watchdog situation once, will not spam more about it" even if
those "logged" messages were at an invisible verbosity level. [issue #3157,
PR #3151]
- * Revised detection of (relative) paths to program and configuration files
- near the currently running program in NUT for Windows builds. [issues #3207,
- #3063, #3065, #3219]
+ * Systemd units for some daemons were revised to honour the `MODE` setting
+ from `nut.conf`, if available, to gracefully not-start when explicitly
+ not-requested (e.g. do not start `upsd` in `MODE=netclient`), even though
+ the unit is nominally enabled; this should make packaging and providing
+ service pre-sets more simple. [issue #837, PR #3233]
* Fixed thread-safety of IP address printout in `libupsclient` method
- `upscli_tryconnect()` (practical bug seen in `nut-scanner` parallel scans).
- [issue #3234]
+ `upscli_tryconnect()` (practical bug seen in `nut-scanner` parallel scans).
+ [issue #3234]
+
+ - NUT for Windows specific updates:
+ * Revised detection of (relative) paths to program and configuration files
+ near the currently running program in NUT for Windows builds, including
+ limited support for UNC paths (ignoring a localhost `\\?\` prefix).
+ [issues #3207, #3063, #3065, #3219]
+ * Augmented WIN32 `nut.exe` launcher with `status` and `restart` actions,
+ and general ability to debug-log the activities during service management
+ operations. (Re-)registration of the "Network UPS Tools" service should
+ now populate a nice description of it. The `-U` (uninstall) action should
+ now also try to stop the service first. [PR #3235]
+ * Revised WIN32 `WSAStartup()` and registration of `atexit(WSACleanup)` to
+ only be done once per program (and cleanups to be always registered); this
+ impacts the C `libupsclient` and C++ `libnutclient` libraries (and so most
+ NUT clients and tools which use the former, and `nutconf` which uses the
+ latter), the `apcupsd` driver, `upsd` data server, and `nut-scanner` tool.
+ [PR #3237]
+ * Documented setup of Microsoft IIS to host the NUT CGI programs. [#3207]
- `asem`, `bestfortress`, `bestuferrups`, `bicker_ser`, `everups`, `metasys`,
`masterguard`, `mge-utalk`, `oneac`, `phoenixcontact_modbus`, `pijuice`,
@@ -142,6 +157,7 @@ https://github.com/networkupstools/nut/milestone/12
Tested on A1000 and A2000 units. [#3181]
* Hides QX_FLAG_NONUT variables from syslog unless the debug level
is raised. [issue #3190, PR #3198]
+ * Added support for battery.charge in hunnox subdriver. [#3254]
- `powerp-bin` and `powerp-txt` driver updates:
* Their `upsdrv_initinfo()` methods did not explicitly reference the
@@ -176,6 +192,9 @@ https://github.com/networkupstools/nut/milestone/12
- `usbhid-ups` driver updates:
* Declared support for APC with USB ID `051d:0005` (details may evolve
as the devices become better known). [issue #3047, PR #3081]
+ * Extended iDowell subdriver with data points for GoldenMate UPS using
+ LiFePO4 batteries (tested with GoldenMate LIPO 1500VA/1000W).
+ [issue #3015, PR #3265]
* Improved support for Eaton 5S1500LCD and 5S1600LCD (US version). [#2380]
* The driver used to report `CHRG` status if the device was not "fully
charged", however the latter status was only queried and reported by
@@ -193,6 +212,23 @@ https://github.com/networkupstools/nut/milestone/12
ignore any useful reports, and that we successfully use reasonably many
of the existing mappings. Suggest how user can help improve the driver
if too few data points were seen. [#3082, #3095]
+ * `cps-hid` updated with Replace Battery information. [PR #3268]
+ * `openups-hid` had `nobattery` definition inverted, causing alarms -- now
+ fixed to use the same mapping helper as other subdrivers. [issue #3246]
+ * `powercom-hid` subdriver improved with a `powercom_hack_voltage_lkp` to
+ query `battery.voltage`. [issue #2766]
+ * Improved handling of transient `LIBUSB_ERROR_IO` failures during polling.
+ Some devices (CyberPower, etc.) have firmware bugs causing random I/O
+ errors on certain HID reports. The driver now skips failing reports and
+ continues polling rather than triggering expensive reconnection attempts.
+ True disconnections are still detected via other error codes or when all
+ polls fail. [issue #3116]
+
+ - `nut-scanner` tool updates:
+ * Fixed `nut-scanner` search for "simulation devices" to not use only the
+ built-in NUT configuration path on all platforms, but to also consider
+ `NUT_CONFPATH` and other fallback locations, like other code does.
+ [PR #3249]
- `upsd` data server updates:
* Sometimes "Data for UPS [X] is stale" and "UPS [X] data is no longer
@@ -200,6 +236,8 @@ https://github.com/networkupstools/nut/milestone/12
systems. Now we allow one more second on top of `MAXAGE` setting to
declare the device dead, just in case fractional/whole second rounding
comes into play and breaks things. [issue #661]
+ * Fixed `LISTEN *` handling for `upsd.exe` in NUT for Windows builds.
+ [PR #3237]
- `upsdrvctl` tool updates:
* Make use of `setproctag()` and `getproctag()` to report parent/child
@@ -207,7 +245,7 @@ https://github.com/networkupstools/nut/milestone/12
- `upslog` tool updates:
* Updated `help()` and failure messages to suggest `-m '*,-'` for logging
- of all known local devices to stdout. [#3083]
+ of all known local devices to stdout. [#3083, #2756]
- `upsmon` client updates:
* The `SHUTDOWNEXIT` option was not handled properly, and the requested
@@ -236,10 +274,14 @@ several `FSD` notifications into one executed action. [PR #3097]
- `upsset` should now recognize `RANGE NUMBER` and `NUMBER` types. [#3164]
- `upsstats` tool updates:
- * Now has JSON output mode via `?json` (or `&json`) CGI query
- parameter. [issue #2524, PR #3171]
+ * Now has JSON output mode via `?json` (or `&json`) CGI query parameter.
+ New `@TREELINK_JSON@` keyword added and HTML templates updated to expose
+ these JSON documents when browsing. [issue #2524, PRs #3171, #3249]
* Handle `device.model` in addition to `ups.model` in upsstats HTML
templates. [#3180]
+ * Introduced a `@NUT_UPSSTATS_TEMPLATE@` command which the HTML template
+ files now MUST start with (safety check that we are reading a template).
+ [issue #3252, PR #3249]
- `upssched` tool updates:
* Previously in PR #2896 (NUT releases v2.8.3 and v2.8.4) the `UPSNAME` and
@@ -268,7 +310,12 @@ several `FSD` notifications into one executed action. [PR #3097]
* Revised info/error/warning/debug message emission so they go to `stderr`
and have a consistent look. Revised some typos along the way. [issue #3194]
- - `configure` script options:
+ - `configure` script updates:
+ * Added an option to `--enable-shared-private-libs` to deliver NUT common
+ libraries as shared objects used by different NUT binaries, rather than
+ linking just the used bits into each binary. This has some potential
+ both for mayhem (so disabled by default) and for significant reduction
+ of installation footprint. [issue #2800]
* For ages, most recipes for building NUT had customized the `sysconfdir` to
be `/etc/nut`, which is not exactly the *system* configuration directory.
This is finally deprecated, with new `--with-confdir` configuration option
@@ -282,6 +329,18 @@ several `FSD` notifications into one executed action. [PR #3097]
most cases. A `--with-confdir-examples` option was also introduced, to
help distributions that place `*.conf.sample` files into docs or other
locations. [#3131]
+ * The `configure` script would now probe (if it can) the operating systems
+ for more user and group account names, such as `upsmon`, `nutmon`, `ups`,
+ `nut` (last hit wins, separately for user and groups accounts) settling
+ on one of those if detected instead of `nobody` (and optionally `nogroup`).
+ It would also warn if `nobody` or `nogroup` end up being used for a build.
+ [#3173]
+ * Default `PIDPATH` is now more strictly `/var/run`, unless building on a
+ system conforming to FHS-3.0 standard where that location is absent or
+ is a symlink, while `/run` exists and is a true directory. [#3099]
+ * Made default NUT CGI URI (used in HTML templates, which may be edited
+ later on a NUT deployment to customize for specific web server setup)
+ configurable `--with-cgi-uri`. [#3207]
* Introduced `--with-python{,2,3}-modules-dir` to specify PyNUT(Client)
module installation location (for module-named dir to be created under
it), if not bundling with NUT-Monitor UI app. By default the respective
@@ -296,6 +355,12 @@ several `FSD` notifications into one executed action. [PR #3097]
Default settings were changed from plain `auto` (allowing for multiple
`python*` interpreters and `site-package` locations to be used), to
prefer a `python3` if available, else `python2`, else `python`. [#1792]
+ * The `configure` script should now try harder to report specifically
+ the "purelib" location as `PYTHON*_SITE_PACKAGES`. [#1209]
+ * Added a `--with-systemdsysusersdir=PATH` option to have systemd pre-create
+ the user and group accounts for NUT, before packaging or manually executed
+ commands do (this is particularly important for immutable-image operating
+ environments). [PR #3262]
* Streamlined handling of `--with-*` and `--enable-*` options using NUT 'm4'
macros; fixed reporting help for options whose defaults are evaluated.
[issue #3049, PR #3140]
@@ -308,25 +373,80 @@ several `FSD` notifications into one executed action. [PR #3097]
updated many help messages. The `configure` script logs should now clarify
where CFLAGS/LIBS/LDFLAGS values come from ('pkg-config', 'default', user
provided 'confargs' etc.) [#3140]
- * Added configure script options for 'libregex' tuning, just so it is on par
- with our other optional dependencies. [#3140]
+ * Added `configure` script options for 'libregex' tuning, just so that one
+ is on par with our other optional dependencies. [#3140]
* Changed `--enable-extapi-enphase` to not default to `auto` and then get
installed on every system capable of using it; this is a niche capability
for users of specific devices via a cloud portal. [follow-up to PR #2813
for issue #2807]
- - Fixed CI recipes for PyPI publication of PyNUT(Client) module to also
- include the source distribution (was posted for NUT v2.8.1 and v2.8.2
- tagged releases, but absent for v2.8.3 and v2.8.4). [#3056]
-
- - The `configure` script would now probe (if it can) the operating systems for
- more user and group account names, such as `upsmon`, `nutmon`, `ups`, `nut`
- (last hit wins, separately for user and groups accounts) settling on one of
- those if detected instead of `nobody` (and optionally `nogroup`). It would
- also warn if `nobody` or `nogroup` end up being used for a build. [#3173]
+ - Recipes, CI and helper script updates not classified above:
+ * Fixed CI recipes for PyPI publication of PyNUT(Client) module to also
+ include the source distribution (was posted for NUT v2.8.1 and v2.8.2
+ tagged releases, but absent for v2.8.3 and v2.8.4). [#3056]
+ * Updated `make spellcheck` to help avoid asciidoc admonition blocks with
+ visually invalid sentences (after rendering as a box in HTML or PDF).
+ [#3077]
+ * Added a `make dist-docs` goal to generate, collect and tarball all
+ document types (`man`, `html-single`, `html-chunked`, `pdf`) that we
+ may have enabled for the current build configuration and available
+ tooling. [#1400]
+ * Further developer workflow speedup with `make spellcheck-interactive-quick`
+ which should now not re-check source texts that were okay with the previous
+ dictionary contents, in case some new terms have to be added. [PRs #3186,
+ #2871]
+ * Introduced `make distcheck-completeness` goal to verify that our
+ distribution archives can exactly reproduce themselves. [#2829]
+ * Added a GitHub Actions CI job to generate, upload and recycle `make dist`
+ and `make dist-docs` tarballs so they are easier to obtain for people and
+ other CI systems (which might not want to follow a Git repository). Links
+ to these artifacts can be seen in workflow page (or its logs), and added
+ as a line in GitHub Checks list associated with a commit. Note that the
+ GitHub Action storage keeps artifacts for at most 90 days. [#1400, #2829]
+ * Revised CI and deliverable scripts, and Makefile recipes, to not use
+ the verbatim `grep -E` (loudly preferred by newer systems, but may be
+ absent on older ones) after all, nor use `egrep` (loudly disliked by
+ newer systems). Instead, use what `configure` script detected for the
+ generated files (or ones made from templates), and use a similar
+ detection in standalone scripts. Also revised the use of `grep -q`,
+ `id -u`, `diff -u`, etc. which are not ubiquitous, and of `test -e`
+ which is not only absent in some older shells, but can cause them to
+ abort processing the script immediately. Also the `if ! condition`
+ syntax is not supported everywhere (or the `!` operator generally).
+ [#3099, #1660]
+ * Generally improved shell script portability (including `Makefile.am`
+ embedded recipes) when mixing back-quotes and double-quotes. Namely,
+ the classic Solaris `ksh` implementation (also used in current versions
+ of Solaris and illumos based distributions) was strict about following
+ (an older revision of) the POSIX standard here, and some use-cases
+ misbehaved with our older script spelling. [issue #3196]
+ * Revised `drivers/Makefile.am` to not always build `libdummy_serial.la`,
+ but only if it is needed by serial-only or modbus drivers. Introduced
+ a `libdummy_usb.la` to only build USB layer sources once and share the
+ results between several USB-capable drivers. [#2800]
+ * Revised `tools/gitlog2version.sh` helper script (logic added into
+ a new `tools/semver-compare.sh`) with a mode to expand NUT SEMVER
+ components into wide zero-padded numbers, so it is easier to
+ alphanumerically compare different releases regardless of version
+ component lengths in digits (and differences in their amounts). You can
+ request an inverse operation with `NUT_VERSION_STRIP_LEADING_ZEROES=true`.
+ Added tests to cover different shell interpreter platforms (piggy-back
+ on the `tests/nut-driver-enumerator-test.sh` script), and made sure
+ that outputs of legacy-mode processing (with `NUT_VERSION_DEFAULT`
+ string provided by caller or saved in a tarball) are consistent with
+ git-mode. The new `tools/semver-compare.sh` helper can be used directly
+ to expand and strip version strings, sort and compare multiple versions
+ as one simple operation. [issue #3055, PRs #3213, #3217]
+ * Dropped the `compile` script from Git sources. It originates from automake
+ and is added to work area (if missing) during `autogen.sh` rituals anyway.
+ It is still distributed as part of `make dist` tarball. [#1209]
- - Updated `make spellcheck` to help avoid asciidoc admonition blocks with
- visually invalid sentences (after rendering as a box in HTML or PDF). [#3077]
+ - Upstreamed reference packaging recipes (DEB, RPM) from the 42ITy project
+ which can be used with OBS (Open Build Service by SUSE), both to support
+ end-user testing of NUT development, and to have a yet another NUT CI farm
+ player with a distinct opinion on code quality. Many of the changes listed
+ above happened thanks to this effort, and due to concerns raised by OBS
+ build agents with various Linux distributions. [#1209, #1316, #3144]
- Updated `docs/*.txt`: add asciidoc comments with links to nut-website
rendered contents of most interesting pages. [#3095]
@@ -334,74 +454,15 @@ several `FSD` notifications into one executed action. [PR #3097]
- HTML renditions of NUT man pages and books (or chunked chapters thereof)
now include the AnchorJS script to provide permalinks to sections. [#3185]
- - Added a `make dist-docs` goal to generate, collect and tarball all document
- types (man, html-single, html-chunked, pdf) that we may have enabled for the
- current build configuration and available tooling. [#1400]
-
- - Further developer workflow speedup with `make spellcheck-interactive-quick`
- which should now not re-check source texts that were okay with the previous
- dictionary contents, in case some new terms have to be added. [PRs #3186, #2871]
-
- - Added a GitHub Actions CI job to generate, upload and recycle `make dist`
- and `make dist-docs` tarballs so they are easier to obtain for people and
- other CI systems (which might not want to follow a Git repository). Links
- to these artifacts can be seen in workflow page (or its logs), and added
- as a line in GitHub Checks list associated with a commit. Note that the
- GitHub Action storage keeps artifacts for at most 90 days. [#1400, #2829]
-
- Source directory `data/html` was renamed to `data/htmlcgi` in order to
better reflect its contents and purpose, compared to documentation-oriented
- `html*` directories (and recipe variable names). This may impact some
- packaging recipes which do not rely on `make install` alone. [#3049]
-
- - Dropped the `compile` script from Git sources. It originates from automake
- and is added to work area (if missing) during `autogen.sh` rituals anyway.
- It is still distributed as part of `make dist` tarball. [#1209]
-
- - Default `PIDPATH` is now more strictly `/var/run`, unless building on a
- system conforming to FHS-3.0 standard where that location is absent or
- is a symlink, while `/run` exists and is a true directory. [#3099]
-
- - Augmented `nut.exe` launcher with `status` and `restart` actions, and
- general ability to debug-log the activities during service management
- operations. (Re-)registration of the "Network UPS Tools" service should
- now populate a nice description of it. The `-U` (uninstall) action should
- now also try to stop the service first. [PR #3235]
-
- - Fixed `LISTEN *` handling for `upsd.exe` in NUT for Windows builds. [PR #3237]
-
- - Revised WIN32 `WSAStartup()` and registration of `atexit(WSACleanup)` to
- only be done once per program (and cleanups to be always registered); this
- impacts the C `libupsclient` and C++ `libnutclient` libraries (and so most
- NUT clients and tools which use the former, and `nutconf` which uses the
- latter), the `apcupsd` driver, `upsd` data server, and `nut-scanner` tool.
- [PR #3237]
-
- - Revised CI and deliverable scripts, and Makefile recipes, to not use
- the verbatim `grep -E` (loudly preferred by newer systems, but may be
- absent on older ones) after all, nor use `egrep` (loudly disliked by
- newer systems). Instead, use what `configure` script detected for the
- generated files (or ones made from templates), and use a similar
- detection in standalone scripts. Also revised the use of `grep -q`,
- `id -u`, `diff -u`, etc. which are not ubiquitous, and of `test -e`
- which is not only absent in some older shells, but can cause them to
- abort processing the script immediately. Also the `if ! condition`
- syntax is not supported everywhere (or the `!` operator generally).
- [#3099, #1660]
-
- - Revised `tools/gitlog2version.sh` helper script (logic added into
- a new `tools/semver-compare.sh`) with a mode to expand NUT SEMVER
- components into wide zero-padded numbers, so it is easier to
- alphanumerically compare different releases regardless of version
- component lengths in digits (and differences in their amounts). You can
- request an inverse operation with `NUT_VERSION_STRIP_LEADING_ZEROES=true`.
- Added tests to cover different shell interpreter platforms (piggy-back
- on the `tests/nut-driver-enumerator-test.sh` script), and made sure
- that outputs of legacy-mode processing (with `NUT_VERSION_DEFAULT`
- string provided by caller or saved in a tarball) are consistent with
- git-mode. The new `tools/semver-compare.sh` helper can be used directly
- to expand and strip version strings, sort and compare multiple versions
- as one simple operation. [issue #3055, PRs #3213, #3217]
+ `html*` directories (and recipe variable names). The static HTML pages now
+ also suggest a NUT icon (installed from `docs/images`). This may impact some
+ packaging recipes which do not rely on `make install` alone. [#3049, #3249]
+
+ - Fixed man page naming for `nutdrv_siemens-sitop(.8)` (dash vs. underscore)
+ to match the driver program name. Follow-up from slightly botched renaming
+ in original contribution. [PR #545]
- The NUT Integration Testing suite (NIT) script, if started as `root`,
can now consult its run-time situation vs. `BUILTIN_RUN_AS_USER` and
@@ -409,23 +470,6 @@ several `FSD` notifications into one executed action. [PR #3097]
do not exist (e.g. running in a packaging build root), a different
value like "nobody" or "nogroup" would be defaulted for tests. [#1209]
- - Fixed man page naming for `nutdrv_siemens-sitop(.8)` (dash vs. underscore)
- to match the driver program name. Follow-up from slightly botched renaming
- in original contribution. [PR #545]
-
- - The `configure` script should now try harder to report specifically
- the "purelib" location as `PYTHON*_SITE_PACKAGES`. [#1209]
-
- - Introduce `make distcheck-completeness` goal to verify that our distribution
- archives can exactly reproduce themselves. [#2829]
-
- - Upstreamed reference packaging recipes (DEB, RPM) from the 42ITy project
- which can be used with OBS (Open Build Service by SUSE), both to support
- end-user testing of NUT development, and to have a yet another NUT CI farm
- player with a distinct opinion on code quality. Many of the changes listed
- above happened thanks to this effort, and due to concerns raised by OBS
- build agents with various Linux distributions. [#1209, #1316, #3144]
-
- Revised use of NUT macros for USB Vendor IDs listed in different drivers,
so the names would be better exposed in generated `udev.rules` and similar
files. [PR #3139]
diff --git a/README.adoc b/README.adoc
index c19a588222..93b80a10b5 100644
--- a/README.adoc
+++ b/README.adoc
@@ -894,8 +894,12 @@ endif::env-github[]
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
+| 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.
+
+ They are essentially 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
@@ -908,6 +912,14 @@ endif::env-github[]
of operating systems, compilers, script interpreters, tools and third-party
dependencies.
+| image:images/ci/obs-logo.png[alt="openSUSE Build Service logo",width="140",height="62",link="https://build.opensuse.org/"]
+| The several variants of
+ link:https://build.opensuse.org/project/show/home:networkupstools[OBS NUT
+ packaging project] hosted on the link:https://build.opensuse.org/[openSUSE
+ Build Service (OBS)] allow us to propose reference packaging recipes for a
+ number of Linux distributions, as well as to test NUT PR and stable branch
+ iterations on those across several CPU architectures not available elsewhere.
+
| 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
@@ -918,13 +930,16 @@ endif::env-github[]
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/fosshost_org_Host_Light_38px.png[alt="Fosshost logo",width="112",height="38"]
+| Fosshost used to provide virtual machines where the multi-platform NUT CI farm
+ ran, following up from a take on multi-platform builds with Travis CI while it
+ was free for FOSS projects. In turn, DigitalOcean helped us move on from there
+ after the Fosshost project went defunct.
| 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.
+| link:https://www.gandi.net/[Gandi.Net] ultimately took up the costs of
+ NUT DNS hosting, which they have provided commercially for years before
+ that.
| 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
diff --git a/UPGRADING.adoc b/UPGRADING.adoc
index 9ca1e7b572..fdfcdabd60 100644
--- a/UPGRADING.adoc
+++ b/UPGRADING.adoc
@@ -26,6 +26,15 @@ Changes from 2.8.4 to 2.8.5
- PLANNED: Keep track of any further API clean-up?
+- Added a `configure` script option to `--enable-shared-private-libs` which
+ allows to deliver NUT common libraries as shared objects used by different
+ NUT binaries, rather than linking just the used bits into each binary.
+ This has some potential both for mayhem (so disabled by default) and
+ for significant reduction of installation footprint. If enabled with
+ a NUT packaging build, note there would be several more NUT shared
+ library files to deliver with the packages (formally versioned and
+ named by NUT release semantic version triplet). [issue #2800]
+
- For ages, most recipes for building NUT had customized the `sysconfdir` to
be `/etc/nut`, which is not exactly the *system* configuration directory.
This is finally deprecated, with new `--with-confdir` configuration option
@@ -50,6 +59,12 @@ Changes from 2.8.4 to 2.8.5
installed (and possibly customized) with the `*.html.sample` files delivered
by the new build. [PR #3180]
+- Introduced a `@NUT_UPSSTATS_TEMPLATE@` command which the NUT CGI template
+ files now MUST start with (safety check that we are reading a template).
+ While the delivered `upsstats*.html.sample` files would include the change,
+ ultimate `upsstats*.html` templates deployed for end-users MUST be updated.
+ [issue #3252, PR #3249]
+
- Dropped the `compile` script from Git sources. It originates from automake
and is added to work area (if missing) during `autogen.sh` rituals anyway
(as `make` says, `'automake --add-missing' can install 'compile'` when you
diff --git a/appveyor.yml b/appveyor.yml
index fe1f0c0966..e98d80d959 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -106,7 +106,16 @@ build_script:
set MSYSTEM=MINGW64
REM Note: currently we save job time and do not install asciidoc/a2x
REM # --with-docs="man=auto html-single=auto html-chunked=no pdf=no"
- C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" CI_SKIP_CHECK=true CANBUILD_WITH_LIBMODBUS_USB=yes ./ci_build.sh --with-docs=no'
+ REM The resulting configuration defaults to --prefix=/mingw{32,64}
+ REM as appropriate for the platform, which ends up as a subdirectory
+ REM under DESTDIR when we `make install` later, and in our 7z archive.
+ REM The builds are relocatable however, so end-users can extract to
+ REM e.g. "C:\Program Files\NUT" and forfeit "mingwXX" part, probably,
+ REM but this can complicate automatic relative directory resolution
+ REM to find "nearby" program or configuration files (see common.c
+ REM for current implementation). Hard-coded fallback strings may
+ REM end up getting used in those cases.
+ C:\msys64\usr\bin\bash -lc 'date -u; PATH="/mingw64/bin:$PATH" CI_SKIP_CHECK=true CANBUILD_WITH_LIBMODBUS_USB=yes WITH_LIBNUTPRIVATE=true ./ci_build.sh --with-docs=no'
after_build:
@@ -138,7 +147,9 @@ after_test:
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; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s -j 8 install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%" ; rm -rf ./.inst/NUT-for-Windows-x86_64-SNAPSHOT || true ; 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 ; ) ; date -u'
+ C:\msys64\usr\bin\bash -lc 'date -u; set -e ; if ! rm -rf ".inst" ; then echo "WARNING: Failed to clean away .inst" ; fi ; PATH="/mingw64/lib/ccache/bin:/mingw64/bin:$PATH" make -s -j 8 install-win-bundle DESTDIR="`pwd`/.inst/NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%"'
+ C:\msys64\usr\bin\bash -lc 'date -u; set -e ; rm -rf ./.inst/NUT-for-Windows-x86_64-SNAPSHOT || true ; 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 ; )'
+ C:\msys64\usr\bin\bash -lc 'date -u'
cd .inst
7z a ../NUT-for-Windows-x86_64-SNAPSHOT-%APPVEYOR_BUILD_VERSION%.7z NUT*
- cmd: |
diff --git a/autogen.sh b/autogen.sh
index 9a3fcd8aa6..89b2739135 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -157,6 +157,12 @@ if [ ! -f scripts/systemd/nut-common-tmpfiles.conf.in ]; then
) > scripts/systemd/nut-common-tmpfiles.conf.in
fi
+if [ ! -f scripts/systemd/nut-common-sysusers.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-sysusers.conf.in
+fi
+
# now we can safely call autoreconf
if ( command -v dos2unix ) 2>/dev/null >/dev/null ; then
if ( dos2unix < configure.ac | cmp - configure.ac ) 2>/dev/null >/dev/null ; then
diff --git a/ci_build.sh b/ci_build.sh
index 2bde8b09ae..1d77f666fe 100755
--- a/ci_build.sh
+++ b/ci_build.sh
@@ -109,6 +109,8 @@ if [ "$BUILD_TYPE" = fightwarn ]; then
# Similarly for testing builds with and without "unmapped" values
# (normally hidden by #ifdef blocks) in certain evolving drivers
#[ -n "$NUT_UNMAPPED_VARIANTS" ] || NUT_UNMAPPED_VARIANTS=auto
+
+ #[ -n "$NUT_LIBNUTPRIVATE_VARIANTS" ] || NUT_LIBNUTPRIVATE_VARIANTS=auto
fi
# configure default is "no"; an "auto" value is "yes unless CFLAGS say something"
@@ -1610,6 +1612,13 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
CONFIG_OPTS+=("--without-unmapped-data-points") ;;
*) ;; # Keep built-in default
esac
+ case x"${WITH_LIBNUTPRIVATE-}" in
+ [Tt][Rr][Uu][Ee]|[Yy][Ee][Ss])
+ CONFIG_OPTS+=("--enable-shared-pivate-libs") ;;
+ [Ff][Aa][Ll][Ss][Ee]|[Nn][Oo])
+ CONFIG_OPTS+=("--disable-shared-pivate-libs") ;;
+ *) ;; # Keep built-in default
+ esac
;;
esac
@@ -2148,15 +2157,35 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
done
fi
+ if [ -z "$NUT_LIBNUTPRIVATE_VARIANTS" ] || [ "$NUT_LIBNUTPRIVATE_VARIANTS" = auto ] || [ "$NUT_LIBNUTPRIVATE_VARIANTS" = default ] ; then
+ # Handle similarly to NUT_UNMAPPED_VARIANTS
+ NUT_LIBNUTPRIVATE_VARIANTS=()
+ case x"${WITH_LIBNUTPRIVATE-}" in
+ [Tt][Rr][Uu][Ee]|[Yy][Ee][Ss])
+ NUT_LIBNUTPRIVATE_VARIANTS+=("yes") ;;
+ [Ff][Aa][Ll][Ss][Ee]|[Nn][Oo])
+ NUT_LIBNUTPRIVATE_VARIANTS+=("no") ;;
+ *)
+ NUT_LIBNUTPRIVATE_VARIANTS+=("yes" "no") ;;
+ esac
+ else
+ TMP="$NUT_LIBNUTPRIVATE_VARIANTS"
+ NUT_LIBNUTPRIVATE_VARIANTS=()
+ for VAL in $TMP ; do
+ NUT_LIBNUTPRIVATE_VARIANTS+=("$VAL")
+ done
+ fi
+
# TODO: Similar loops for other variations like TESTING,
# MGE SHUT vs. other serial protocols...
BUILDSTODO_SSL="${#NUT_SSL_VARIANTS[@]}"
BUILDSTODO_USB="${#NUT_USB_VARIANTS[@]}"
BUILDSTODO_UNMAPPED="${#NUT_UNMAPPED_VARIANTS[@]}"
+ BUILDSTODO_LIBNUTPRIVATE="${#NUT_LIBNUTPRIVATE_VARIANTS[@]}"
- echo "=== Found ${BUILDSTODO_SSL} SSL (${NUT_SSL_VARIANTS[*]}) and ${BUILDSTODO_USB} USB (${NUT_USB_VARIANTS[*]}) and ${BUILDSTODO_UNMAPPED} UNMAPPED (${NUT_UNMAPPED_VARIANTS[*]}) variations..."
- if [ x"${BUILDSTODO_SSL}${BUILDSTODO_USB}${BUILDSTODO_UNMAPPED}" = x"000" ] ; then
+ echo "=== Found ${BUILDSTODO_SSL} SSL (${NUT_SSL_VARIANTS[*]}) and ${BUILDSTODO_USB} USB (${NUT_USB_VARIANTS[*]}) and ${BUILDSTODO_UNMAPPED} UNMAPPED (${NUT_UNMAPPED_VARIANTS[*]}) and ${BUILDSTODO_LIBNUTPRIVATE} LIBNUTPRIVATE (${NUT_LIBNUTPRIVATE_VARIANTS[*]}) variations..."
+ if [ x"${BUILDSTODO_SSL}${BUILDSTODO_USB}${BUILDSTODO_UNMAPPED}${BUILDSTODO_LIBNUTPRIVATE}" = x"0000" ] ; then
echo "=== ERROR: BUILD_TYPE='${BUILD_TYPE}' got no builds to run!" >&2
exit 1
fi
@@ -2179,10 +2208,14 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
if [ "$BUILDSTODO_UNMAPPED" = 1 ]; then
BUILDSTODO_ALWAYS="NUT_UNMAPPED_VARIANT=${NUT_UNMAPPED_VARIANTS[*]};${BUILDSTODO_ALWAYS}"
fi
+ if [ "$BUILDSTODO_LIBNUTPRIVATE" = 1 ]; then
+ BUILDSTODO_ALWAYS="NUT_LIBNUTPRIVATE_VARIANT=${NUT_LIBNUTPRIVATE_VARIANTS[*]};${BUILDSTODO_ALWAYS}"
+ fi
if [ "$BUILDSTODO_SSL" -le 1 ] \
&& [ "$BUILDSTODO_USB" -le 1 ] \
&& [ "$BUILDSTODO_UNMAPPED" -le 1 ] \
+ && [ "$BUILDSTODO_LIBNUTPRIVATE" -le 1 ] \
; then
echo "=== NOTE: Considering at most one variant in each category, will do them all at once"
BUILDSTODO_LIST+=("${BUILDSTODO_ALWAYS}")
@@ -2216,6 +2249,12 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
BUILDSTODO_LIST+=("NUT_UNMAPPED_VARIANT=${VAL};${BUILDSTODO_ALWAYS}")
done
fi
+ if [ "$BUILDSTODO_LIBNUTPRIVATE" -gt 1 ]; then
+ for VAL in "${NUT_LIBNUTPRIVATE_VARIANTS[@]}" ; do
+ if [ "$VAL" = no ] ; then continue ; fi # Default setting for other builds
+ BUILDSTODO_LIST+=("NUT_LIBNUTPRIVATE_VARIANT=${VAL};${BUILDSTODO_ALWAYS}")
+ done
+ fi
else
# Try to mix into the smallest amount of builds (randomize
# the mix so we can cover many scenarios as different CI
@@ -2232,6 +2271,9 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
[ "$BUILDSTODO_MAX" -ge "$BUILDSTODO_UNMAPPED" ] \
|| { BUILDSTODO_MAX="$BUILDSTODO_UNMAPPED"; BUILDSTODO_MAX_TYPE="BUILDSTODO_UNMAPPED" ; }
+ [ "$BUILDSTODO_MAX" -ge "$BUILDSTODO_LIBNUTPRIVATE" ] \
+ || { BUILDSTODO_MAX="$BUILDSTODO_LIBNUTPRIVATE"; BUILDSTODO_MAX_TYPE="BUILDSTODO_LIBNUTPRIVATE" ; }
+
# FIXME: Can this be eval'ed?
# First populate the longer set of variants:
case "${BUILDSTODO_MAX_TYPE}" in
@@ -2250,6 +2292,11 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
BUILDSTODO_LIST+=("NUT_UNMAPPED_VARIANT=${VAL};${BUILDSTODO_ALWAYS}")
done
;;
+ BUILDSTODO_LIBNUTPRIVATE)
+ for VAL in "${NUT_LIBNUTPRIVATE_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST+=("NUT_LIBNUTPRIVATE_VARIANT=${VAL};${BUILDSTODO_ALWAYS}")
+ done
+ ;;
esac
case "${BUILDSTODO_MAX_TYPE}" in
@@ -2267,6 +2314,13 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
BUILDSTODO_LIST[$i]="NUT_UNMAPPED_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
done
+
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_LIBNUTPRIVATE" -le 1 ] || \
+ for VAL in "${NUT_LIBNUTPRIVATE_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_LIBNUTPRIVATE_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
;;
BUILDSTODO_USB)
i=$(($RANDOM % $BUILDSTODO_MAX))
@@ -2282,6 +2336,13 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
BUILDSTODO_LIST[$i]="NUT_UNMAPPED_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
done
+
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_LIBNUTPRIVATE" -le 1 ] || \
+ for VAL in "${NUT_LIBNUTPRIVATE_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_LIBNUTPRIVATE_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
;;
BUILDSTODO_UNMAPPED)
i=$(($RANDOM % $BUILDSTODO_MAX))
@@ -2297,6 +2358,35 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
BUILDSTODO_LIST[$i]="NUT_USB_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
done
+
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_LIBNUTPRIVATE" -le 1 ] || \
+ for VAL in "${NUT_LIBNUTPRIVATE_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_LIBNUTPRIVATE_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
+ ;;
+ BUILDSTODO_LIBNUTPRIVATE)
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_SSL" -le 1 ] || \
+ for VAL in "${NUT_SSL_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_SSL_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
+
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_USB" -le 1 ] || \
+ for VAL in "${NUT_USB_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_USB_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
+
+ i=$(($RANDOM % $BUILDSTODO_MAX))
+ [ "$BUILDSTODO_UNMAPPED" -le 1 ] || \
+ for VAL in "${NUT_UNMAPPED_VARIANTS[@]}" ; do
+ BUILDSTODO_LIST[$i]="NUT_UNMAPPED_VARIANT=${VAL};${BUILDSTODO_LIST[$i]}"
+ i=$(( $(($i + 1)) % $BUILDSTODO_MAX))
+ done
;;
esac
fi
@@ -2317,6 +2407,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
NUT_SSL_VARIANT=""
NUT_USB_VARIANT=""
NUT_UNMAPPED_VARIANT=""
+ NUT_LIBNUTPRIVATE_VARIANT=""
eval $TESTCOMBO
echo "=== Starting 'TESTCOMBO=${TESTCOMBO}', ${BUILDSTODO} build variants remaining..."
@@ -2406,6 +2497,26 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-al
;;
esac
+ case "${NUT_LIBNUTPRIVATE_VARIANT}" in
+ "") ;;
+ yes|no) # Try this variant
+ echo "=== Building with 'NUT_LIBNUTPRIVATE_VARIANT=${NUT_LIBNUTPRIVATE_VARIANT}' ..."
+ if [ "${NUT_SSL_VARIANTS[*]}" != "auto" ] && [ x"${NUT_SSL_VARIANT}" = x ] ; then
+ CONFIG_OPTS+=("--without-all")
+ CONFIG_OPTS+=("--without-ssl")
+ fi
+ CONFIG_OPTS+=("--with-serial=auto")
+ if [ "${NUT_USB_VARIANTS[*]}" != "no" ] && [ x"${NUT_USB_VARIANT}" = x ] ; then
+ CONFIG_OPTS+=("--with-usb=auto")
+ fi
+ CONFIG_OPTS+=("--enable-shared-private-libs=${NUT_LIBNUTPRIVATE_VARIANT}")
+ ;;
+ *) # Potentially something new? Unknown values can fail in the configure script.
+ echo "=== Building with 'NUT_LIBNUTPRIVATE_VARIANT=${NUT_LIBNUTPRIVATE_VARIANT}' (WARNING: may be not supported)..."
+ CONFIG_OPTS+=("--enable-shared-private-libs=${NUT_LIBNUTPRIVATE_VARIANT}")
+ ;;
+ esac
+
# Snippet from autogen.sh: restore files required by autoconf
# for non-"foreign" projects that a deep clean in other loops
# could have destroyed:
@@ -2739,6 +2850,10 @@ bindings)
CONFIG_OPTS+=("--with-unmapped-data-points=yes-if-not-DMF")
fi; fi
+ if [ x"${WITH_LIBNUTPRIVATE-}" = xtrue ] ; then
+ CONFIG_OPTS+=("--enable-shared-private-libs")
+ fi
+
if [ -n "${BUILD_DEBUGINFO-}" ]; then
CONFIG_OPTS+=("--with-debuginfo=${BUILD_DEBUGINFO}")
else
@@ -2877,6 +2992,13 @@ cross-windows-mingw*)
;;
esac
+ if [ x"${WITH_LIBNUTPRIVATE-}" = x ] ; then
+ # For Windows we want compact builds
+ # (they reach into gigabytes anyway):
+ WITH_LIBNUTPRIVATE=true
+ fi # else we have some value from caller
+ export WITH_LIBNUTPRIVATE
+
SOURCEMODE="out-of-tree" \
MAKEFLAGS="$PARMAKE_FLAGS" \
KEEP_NUT_REPORT_FEATURE="true" \
diff --git a/clients/Makefile.am b/clients/Makefile.am
index 36fc2be1bf..f9e4450fb8 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -15,9 +15,10 @@ CLEANFILES =
# nutclient.cpp for some legacy reason (maybe initial detached development?)
# optionally includes "common.h" with the NUT build setup - and this option
# was never triggered in fact, not until pushed through command line like this:
-AM_CXXFLAGS = -DHAVE_NUTCOMMON=1 -I$(top_srcdir)/include
+AM_CXXFLAGS = -DHAVE_NUTCOMMON=1 -I$(top_builddir)/include -I$(top_srcdir)/include
# 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/libcommonclient.la \
$(top_builddir)/common/libcommonversion.la \
@@ -30,19 +31,46 @@ $(top_builddir)/common/libparseconf.la: dummy
# (sub-makes are independent as far as trying to write into same files):
$(top_builddir)/common/libcommon.la: $(top_builddir)/common/libparseconf.la
$(top_builddir)/common/libcommonclient.la: $(top_builddir)/common/libparseconf.la
+$(top_builddir)/common/libcommonversion.la: $(top_builddir)/include/nut_version.h
-LDADD_FULL = \
- $(top_builddir)/common/libcommon.la \
+LDADD_FULL =
+LDADD_CLIENT =
+
+if ENABLE_SHARED_PRIVATE_LIBS
+$(top_builddir)/common/libcommonversion-private.la \
+$(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-all.la \
+$(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-client.la: dummy
+ +@cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F)
+
+$(top_builddir)/common/libcommonversion-private.la: $(top_builddir)/include/nut_version.h
+$(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-all.la: $(top_builddir)/common/libcommon.la $(top_builddir)/common/libcommonversion-private.la
+$(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-client.la: $(top_builddir)/common/libcommonclient.la $(top_builddir)/common/libcommonversion-private.la
+
+LDADD_FULL += \
+ $(top_builddir)/common/libcommonversion.la \
+ $(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-all.la
+
+LDADD_CLIENT += \
$(top_builddir)/common/libcommonversion.la \
+ $(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-client.la
+else !ENABLE_SHARED_PRIVATE_LIBS
+LDADD_FULL += \
+ $(top_builddir)/common/libcommon.la \
+ $(top_builddir)/common/libcommonversion.la
+
+LDADD_CLIENT += \
+ $(top_builddir)/common/libcommonclient.la \
+ $(top_builddir)/common/libcommonversion.la
+endif !ENABLE_SHARED_PRIVATE_LIBS
+
+LDADD_FULL += \
libupsclient.la \
$(NETLIBS)
if WITH_SSL
LDADD_FULL += $(LIBSSL_LIBS) $(LIBSSL_LDFLAGS_RPATH)
endif WITH_SSL
-LDADD_CLIENT = \
- $(top_builddir)/common/libcommonclient.la \
- $(top_builddir)/common/libcommonversion.la \
+LDADD_CLIENT += \
libupsclient.la \
$(NETLIBS)
if WITH_SSL
@@ -56,7 +84,7 @@ 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.
-AM_CFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
if WITH_SSL
AM_CFLAGS += $(LIBSSL_CFLAGS)
endif WITH_SSL
@@ -93,31 +121,48 @@ endif WITH_CGI
upsc_SOURCES = upsc.c upsclient.h
upsc_LDADD = $(LDADD_CLIENT) $(top_builddir)/common/libcommonstrjson.la
+
upscmd_SOURCES = upscmd.c upsclient.h
+
upsrw_SOURCES = upsrw.c upsclient.h
+
upslog_SOURCES = upslog.c upsclient.h upslog.h
upslog_LDADD = $(LDADD_FULL)
+
upsmon_SOURCES = upsmon.c upsmon.h upsclient.h
upsmon_LDADD = $(LDADD_FULL)
+
if HAVE_WINDOWS_SOCKETS
message_SOURCES = message.c
endif HAVE_WINDOWS_SOCKETS
upssched_SOURCES = upssched.c upssched.h
-upssched_LDADD = \
+upssched_LDADD =
+if ENABLE_SHARED_PRIVATE_LIBS
+upssched_LDADD += \
+ $(top_builddir)/common/libcommonversion.la \
+ $(top_builddir)/common/libnutprivate-@NUT_SOURCE_GITREV_SEMVER_UNDERSCORES@-common-client.la
+else !ENABLE_SHARED_PRIVATE_LIBS
+upssched_LDADD += \
$(top_builddir)/common/libcommonclient.la \
$(top_builddir)/common/libcommonversion.la \
- $(top_builddir)/common/libparseconf.la \
+ $(top_builddir)/common/libparseconf.la
+endif !ENABLE_SHARED_PRIVATE_LIBS
+
+upssched_LDADD += \
$(NETLIBS)
upsimage_cgi_SOURCES = upsimage.c upsclient.h upsimagearg.h cgilib.c cgilib.h
upsimage_cgi_LDADD = $(LDADD) $(LIBGD_LDFLAGS)
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
upsstats_cgi_LDADD = $(LDADD_CLIENT) $(top_builddir)/common/libcommonstrjson.la
+################################## Plain C client library (libupsclient) :
+
# not LDADD... why?
libupsclient_la_SOURCES = upsclient.c upsclient.h
# NOTE: The library does not require libcommonversion.la
@@ -185,6 +230,8 @@ libupsclient-version.h: libupsclient.la
rm -f "$@.tmp.$$$$" ; \
exit $$RES
+################################## C++ client library (libnutclient) :
+
if HAVE_CXX11
# libnutclient version information and build
libnutclient_la_SOURCES = nutclient.h nutclient.cpp
diff --git a/clients/cgilib.c b/clients/cgilib.c
index 6591a11686..687eed8c5b 100644
--- a/clients/cgilib.c
+++ b/clients/cgilib.c
@@ -116,42 +116,120 @@ void extractcgiargs(void)
void extractpostargs(void)
{
- char buf[SMALLBUF], *ptr, *cleanval;
- int ch;
+ char buf[SMALLBUF], *ptr, *cleanval, *server_software = NULL;
+ int ch, content_length = -1, bytes_seen = 0;
+ size_t buflen;
+
+ /* First, see if there's anything waiting...
+ * the server may not close STDIN properly
+ * or somehow delay opening/populating it. */
+#ifndef WIN32
+ int selret;
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000; /* wait for up to 250ms for a POST query to come */
+
+ selret = select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
+ if (selret <= 0) {
+#else
+ HANDLE hSTDIN = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD selret = WaitForSingleObject(hSTDIN, 250);
+ if (selret != WAIT_OBJECT_0) { /* or == WAIT_TIMEOUT ? */
+#endif /* WIN32 */
+ upsdebug_with_errno(1, "%s: no stdin is waiting (%" PRIiMAX ") ", __func__, (intmax_t)selret);
+ return;
+ }
- ch = fgetc(stdin);
buf[0] = '\0';
- while (ch != EOF) {
- if (ch == '&') {
+ /* Does the web server tell us how much it sent
+ * (and might keep the channel open... indefinitely)? */
+ ptr = getenv("CONTENT_LENGTH");
+ if (ptr) {
+ content_length = atoi(ptr);
+ }
+
+ ptr = getenv("SERVER_SOFTWARE");
+ if (ptr) {
+ server_software = ptr;
+ } else {
+ server_software = "";
+ }
+
+ if (content_length > 0 && strstr(server_software, "IIS")) {
+ /* Our POSTs end with a newline, and that one never arrives
+ * (reads hang), possibly buffered output from IIS?
+ * Our own setmode() in e.g. upsset.c does not help.
+ * So upsset.c ends each FORM with do_hidden_sentinel()
+ * to sacrifice a few bytes we would not use.
+ */
+ upsdebugx(3, "%s: truncating expected content length on IIS ", __func__);
+ content_length--;
+ }
+ upsdebugx(3, "%s: starting to read %d POSTed bytes on server '%s' ", __func__, content_length, server_software);
+
+ ch = fgetc(stdin);
+ upsdebugx(6, "%s: got char: '%c' (%d, 0x%02X) ", __func__, ch, ch, (unsigned int)ch);
+
+ if (ch == EOF) {
+ bytes_seen++;
+ upsdebugx(3, "%s: got immediate EOF in stdin ", __func__);
+ } else while(1) {
+ bytes_seen++;
+ if (ch == '&' || ch == EOF || (content_length >= 0 && bytes_seen >= content_length)) {
+ buflen = strlen(buf);
+ upsdebugx(1, "%s: collected a chunk of %" PRIuSIZE " bytes on stdin: %s ",
+ __func__, buflen, buf);
ptr = strchr(buf, '=');
- if (!ptr)
+ if (!ptr) {
+ upsdebugx(3, "%s: parsearg('%s', '') ", __func__, buf);
parsearg(buf, "");
- else {
+ } else {
*ptr++ = '\0';
cleanval = unescape(ptr);
+ upsdebugx(3, "%s: parsearg('%s', '%s') ", __func__, buf, cleanval);
parsearg(buf, cleanval);
free(cleanval);
}
buf[0] = '\0';
+
+ if (ch == EOF || (content_length >= 0 && bytes_seen >= content_length))
+ break; /* end the loop */
}
else
snprintfcat(buf, sizeof(buf), "%c", ch);
+#ifndef WIN32
+ /* Must re-init every time when looping (array is changed by select method) */
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000; /* wait for up to 250ms for a POST response */
+
+ selret = select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
+ if (selret <= 0) {
+#else
+ selret = WaitForSingleObject(hSTDIN, 250);
+ if (selret != WAIT_OBJECT_0) { /* or == WAIT_TIMEOUT ? */
+#endif
+ /* We do not always get EOF, so assume the input stream stopped */
+ upsdebug_with_errno(1, "%s: timed out waiting for an stdin byte (%" PRIiMAX ") ", __func__, (intmax_t)selret);
+ break;
+ }
+
+ fflush(stderr);
ch = fgetc(stdin);
- }
+ upsdebugx(6, "%s: got char: '%c' (%d, 0x%02X) ", __func__, ch, ch, (unsigned int)ch);
+ if (ch == EOF)
+ upsdebugx(3, "%s: got proper stdin EOF ", __func__);
+ upsdebugx(6, "%s: processed %d bytes with %d expected incoming content length on server '%s' ", __func__, bytes_seen, content_length, server_software);
+ } /* end of infinite loop */
- if (strlen(buf) != 0) {
- ptr = strchr(buf, '=');
- if (!ptr)
- parsearg(buf, "");
- else {
- *ptr++ = '\0';
- cleanval = unescape(ptr);
- parsearg(buf, cleanval);
- free(cleanval);
- }
- }
+ upsdebugx(3, "%s: processed %d bytes with %d incoming content length ", __func__, bytes_seen, content_length);
}
/* called for fatal errors in parseconf like malloc failures */
diff --git a/clients/upsc.c b/clients/upsc.c
index 764811fa87..d8801f7496 100644
--- a/clients/upsc.c
+++ b/clients/upsc.c
@@ -397,6 +397,10 @@ int main(int argc, char **argv)
}
upsdebugx(1, "Starting NUT client: %s", prog);
+#if (defined NUT_PLATFORM_AIX) && (defined ENABLE_SHARED_PRIVATE_LIBS) && ENABLE_SHARED_PRIVATE_LIBS
+ callback_upsconf_args = do_upsconf_args;
+#endif
+
while ((i = getopt(argc, argv, "+hlLcVW:j")) != -1) {
switch (i)
@@ -492,6 +496,6 @@ int main(int argc, char **argv)
/* Formal do_upsconf_args implementation to satisfy linker on AIX */
#if (defined NUT_PLATFORM_AIX)
void do_upsconf_args(char *upsname, char *var, char *val) {
- fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called");
+ fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called");
}
#endif /* end of #if (defined NUT_PLATFORM_AIX) */
diff --git a/clients/upscmd.c b/clients/upscmd.c
index 2c5fe6ec03..47efe29161 100644
--- a/clients/upscmd.c
+++ b/clients/upscmd.c
@@ -308,6 +308,10 @@ int main(int argc, char **argv)
}
upsdebugx(1, "Starting NUT client: %s", prog);
+#if (defined NUT_PLATFORM_AIX) && (defined ENABLE_SHARED_PRIVATE_LIBS) && ENABLE_SHARED_PRIVATE_LIBS
+ callback_upsconf_args = do_upsconf_args;
+#endif
+
while ((i = getopt(argc, argv, "+lhu:p:t:wVW:")) != -1) {
switch (i)
diff --git a/clients/upsimage.c b/clients/upsimage.c
index a74b8f4d0c..909841704f 100644
--- a/clients/upsimage.c
+++ b/clients/upsimage.c
@@ -614,12 +614,38 @@ static int get_var(const char *var, char *buf, size_t buflen)
int main(int argc, char **argv)
{
- char str[SMALLBUF];
+ char str[SMALLBUF], *s;
int i, min, nom, max;
double var = 0;
+
+#ifdef WIN32
+ /* Required ritual before calling any socket functions */
+ static WSADATA WSAdata;
+ static int WSA_Started = 0;
+ if (!WSA_Started) {
+ WSAStartup(2, &WSAdata);
+ atexit((void(*)(void))WSACleanup);
+ WSA_Started = 1;
+ }
+
+ /* Avoid binary output conversions, e.g.
+ * mangling what looks like CRLF on WIN32 */
+ setmode(STDOUT_FILENO, O_BINARY);
+#endif
+
NUT_UNUSED_VARIABLE(argc);
NUT_UNUSED_VARIABLE(argv);
+ /* NOTE: Caller must `export NUT_DEBUG_LEVEL` to see debugs for upsc
+ * and NUT methods called from it. This line aims to just initialize
+ * the subsystem, and set initial timestamp. Debugging the client is
+ * primarily of use to developers, so is not exposed via `-D` args.
+ */
+ s = getenv("NUT_DEBUG_LEVEL");
+ if (s && str_to_int(s, &i, 10) && i > 0) {
+ nut_debug_level = i;
+ }
+
extractcgiargs();
upscli_init_default_connect_timeout(NULL, NULL, UPSCLI_DEFAULT_CONNECT_TIMEOUT);
diff --git a/clients/upslog.c b/clients/upslog.c
index 66e99b8b0c..16b5791cd4 100644
--- a/clients/upslog.c
+++ b/clients/upslog.c
@@ -218,6 +218,8 @@ static void help(const char *prog)
printf(" and it would not imply foregrounding\n");
printf(" - Unlike one '-s ups -l file' spec, you can specify many tuples\n");
printf(" - Example: -m '*,-' to view updates of all known local devices\n");
+ printf(" - Example: -m '*@1.2.3.4,-' to view updates of all known remote\n");
+ printf(" devices served by NUT data server with IP address 1.2.3.4\n");
printf(" -u - Switch to if started as root\n");
printf("\nCommon arguments:\n");
printf(" -V - display the version of this software\n");
@@ -518,6 +520,10 @@ int main(int argc, char **argv)
logformat = DEFAULT_LOGFORMAT;
user = RUN_AS_USER;
+#if (defined NUT_PLATFORM_AIX) && (defined ENABLE_SHARED_PRIVATE_LIBS) && ENABLE_SHARED_PRIVATE_LIBS
+ callback_upsconf_args = do_upsconf_args;
+#endif
+
print_banner_once(prog, 0);
while ((i = getopt(argc, argv, "+hDs:l:i:d:Nf:u:Vp:FBm:W:")) != -1) {
@@ -1025,6 +1031,6 @@ int main(int argc, char **argv)
/* Formal do_upsconf_args implementation to satisfy linker on AIX */
#if (defined NUT_PLATFORM_AIX)
void do_upsconf_args(char *upsname, char *var, char *val) {
- fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called");
+ fatalx(EXIT_FAILURE, "INTERNAL ERROR: formal do_upsconf_args called");
}
#endif /* end of #if (defined NUT_PLATFORM_AIX) */
diff --git a/clients/upsmon.c b/clients/upsmon.c
index 983c6e57e2..c349b193b5 100644
--- a/clients/upsmon.c
+++ b/clients/upsmon.c
@@ -233,19 +233,21 @@ static void wall(const char *text)
pclose(wf);
#else /* WIN32 */
# define MESSAGE_CMD "message.exe"
- char * command;
+ 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 ) {
+ size_t commandsz = strlen(MESSAGE_CMD) + 1 + 2 + strlen(text) + 1;
+
+ command = malloc (commandsz);
+ 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 ) {
+ snprintf(command, commandsz, "%s \"%s\"", MESSAGE_CMD, text);
+ if (system(command) != 0) {
upslog_with_errno(LOG_NOTICE, "Can't invoke wall");
}
free(command);
diff --git a/clients/upsrw.c b/clients/upsrw.c
index dc76c9336b..79c6ffa87e 100644
--- a/clients/upsrw.c
+++ b/clients/upsrw.c
@@ -666,6 +666,10 @@ int main(int argc, char **argv)
}
upsdebugx(1, "Starting NUT client: %s", prog);
+#if (defined NUT_PLATFORM_AIX) && (defined ENABLE_SHARED_PRIVATE_LIBS) && ENABLE_SHARED_PRIVATE_LIBS
+ callback_upsconf_args = do_upsconf_args;
+#endif
+
while ((i = getopt(argc, argv, "+hls:p:t:u:wVW:")) != -1) {
switch (i)
{
diff --git a/clients/upsset.c b/clients/upsset.c
index ebfb2a5e91..03e10b504f 100644
--- a/clients/upsset.c
+++ b/clients/upsset.c
@@ -170,6 +170,14 @@ static void do_hidden(const char *next)
next);
}
+static void do_hidden_sentinel(void)
+{
+ /* MS IIS tends to not close CGI STDIN and not serve the last byte(s)
+ * but just hangs at fgets(), so we truncate the inputs in cgilib,
+ * and add a dummy entry here that we can afford to lose in the end */
+ printf("\n");
+}
+
/* generate SELECT chooser from hosts.conf entries */
static void upslist_arg(size_t numargs, char **arg)
{
@@ -202,7 +210,7 @@ static void do_pickups(const char *currfunc)
snprintf(hostfn, sizeof(hostfn), "%s/hosts.conf", confpath());
- printf("\n");
}
@@ -315,7 +325,7 @@ static void loginscreen(void)
static void loginscreen(void)
{
do_header("Login");
- printf("\n");
printf("\n");
@@ -445,7 +456,7 @@ static void showcmds(void)
"This UPS doesn't support any instant commands.");
do_header("Instant commands");
- printf("\n");
@@ -873,7 +885,7 @@ static void showsettings(void)
}
do_header("Current settings");
- printf("\n");
@@ -1043,6 +1056,7 @@ static void check_conf(void)
PCONF_CTX_t ctx;
snprintf(fn, sizeof(fn), "%s/upsset.conf", confpath());
+ upsdebugx(1, "%s: considering configuration file %s", __func__, fn);
pconf_init(&ctx, upsset_conf_err);
@@ -1092,30 +1106,50 @@ static void check_conf(void)
int main(int argc, char **argv)
{
+ char *s;
+ int i;
+
+#ifdef WIN32
+ /* Required ritual before calling any socket functions */
+ static WSADATA WSAdata;
+ static int WSA_Started = 0;
+ if (!WSA_Started) {
+ WSAStartup(2, &WSAdata);
+ atexit((void(*)(void))WSACleanup);
+ WSA_Started = 1;
+ }
+
+ /* Avoid binary output conversions, e.g.
+ * mangling what looks like CRLF on WIN32 */
+ setmode(STDOUT_FILENO, O_BINARY);
+ /* Also do not break what we receive from HTTP POST queries */
+ setmode(STDIN_FILENO, O_BINARY);
+#endif
+
NUT_UNUSED_VARIABLE(argc);
NUT_UNUSED_VARIABLE(argv);
username = password = function = monups = NULL;
printf("Content-type: text/html\n\n");
+ /* NOTE: Caller must `export NUT_DEBUG_LEVEL` to see debugs for upsc
+ * and NUT methods called from it. This line aims to just initialize
+ * the subsystem, and set initial timestamp. Debugging the client is
+ * primarily of use to developers, so is not exposed via `-D` args.
+ */
+ s = getenv("NUT_DEBUG_LEVEL");
+ if (s && str_to_int(s, &i, 10) && i > 0) {
+ nut_debug_level = i;
+ }
+
/* see if the magic string is present in the config file */
check_conf();
upscli_init_default_connect_timeout(NULL, NULL, UPSCLI_DEFAULT_CONNECT_TIMEOUT);
- /* see if there's anything waiting .. the server my not close STDIN properly */
- if (1) {
- fd_set fds;
- struct timeval tv;
+ extractpostargs();
- FD_ZERO(&fds);
- FD_SET(STDIN_FILENO, &fds);
- tv.tv_sec = 0;
- tv.tv_usec = 250000; /* wait for up to 250ms for a POST response */
-
- if ((select(STDIN_FILENO+1, &fds, 0, 0, &tv)) > 0)
- extractpostargs();
- }
+ /* Nothing POSTed (or parsed correctly)? */
if ((!username) || (!password) || (!function))
loginscreen();
diff --git a/clients/upsstats.c b/clients/upsstats.c
index 06a341a0f8..4ec650c451 100644
--- a/clients/upsstats.c
+++ b/clients/upsstats.c
@@ -39,12 +39,30 @@ static char *monhost = NULL;
static int use_celsius = 1, refreshdelay = -1, treemode = 0;
static int output_json = 0;
+/* call tracing for debug */
+static int call_depth = 0;
+#define upsdebug_call_starting0() upsdebugx(2, "[depth=%02d+] starting %s...", call_depth++, __func__)
+#define upsdebug_call_starting1(msgfmt) upsdebugx(2, "[depth=%02d+] starting %s " msgfmt "...", call_depth++, __func__)
+#define upsdebug_call_starting2(msgfmt, arg1) upsdebugx(2, "[depth=%02d+] starting %s " msgfmt "...", call_depth++, __func__, arg1)
+#define upsdebug_call_starting3(msgfmt, arg1, arg2) upsdebugx(2, "[depth=%02d+] starting %s " msgfmt "...", call_depth++, __func__, arg1, arg2)
+#define upsdebug_call_starting4(msgfmt, arg1, arg2, arg3) upsdebugx(2, "[depth=%02d+] starting %s " msgfmt "...", call_depth++, __func__, arg1, arg2, arg3)
+
+#define upsdebug_call_starting_for_str1(arg1) upsdebug_call_starting2("for '%s'", NUT_STRARG(arg1))
+#define upsdebug_call_starting_for_str2(arg1, arg2) upsdebug_call_starting3("for '%s' '%s'", NUT_STRARG(arg1), NUT_STRARG(arg2))
+#define upsdebug_call_starting_for_str3(arg1, arg2, arg3) upsdebug_call_starting4("for '%s' '%s' '%s'", NUT_STRARG(arg1), NUT_STRARG(arg2), NUT_STRARG(arg3))
+
+#define upsdebug_call_finished0() upsdebugx(2, "[depth=%02d-] finished %s", --call_depth, __func__)
+#define upsdebug_call_finished1(msgfmt) upsdebugx(2, "[depth=%02d-] finished %s " msgfmt, --call_depth, __func__)
+#define upsdebug_call_finished2(msgfmt, arg1) upsdebugx(2, "[depth=%02d-] finished %s " msgfmt, --call_depth, __func__, arg1)
+#define upsdebug_call_finished3(msgfmt, arg1, arg2) upsdebugx(2, "[depth=%02d-] finished %s " msgfmt, --call_depth, __func__, arg1, arg2)
+#define upsdebug_call_finished4(msgfmt, arg1, arg2, arg3) upsdebugx(2, "[depth=%02d-] finished %s " msgfmt, --call_depth, __func__, arg1, arg2, arg3)
+
/* from cgilib's checkhost() */
static char *monhostdesc = NULL;
static uint16_t port;
static char *upsname, *hostname;
-static char *upsimgpath="upsimage.cgi", *upsstatpath="upsstats.cgi";
+static char *upsimgpath="upsimage.cgi" EXEEXT, *upsstatpath="upsstats.cgi" EXEEXT;
static UPSCONN_t ups;
static FILE *tf;
@@ -56,14 +74,17 @@ static int skip_clause = 0, skip_block = 0;
void parsearg(char *var, char *value)
{
+ upsdebug_call_starting_for_str2(var, value);
+
/* avoid bogus junk from evil people */
- if ((strlen(var) > MAX_CGI_STRLEN) || (strlen(value) > MAX_CGI_STRLEN))
+ if ((strlen(var) > MAX_CGI_STRLEN) || (strlen(value) > MAX_CGI_STRLEN)) {
+ upsdebug_call_finished1(": strings too long for CGI");
return;
+ }
if (!strcmp(var, "host")) {
free(monhost);
monhost = xstrdup(value);
- return;
}
if (!strcmp(var, "refresh"))
@@ -77,23 +98,32 @@ void parsearg(char *var, char *value)
if (!strcmp(var, "json")) {
output_json = 1;
}
+
+ upsdebug_call_finished0();
}
static void report_error(void)
{
+ upsdebug_call_starting0();
+
if (upscli_upserror(&ups) == UPSCLI_ERR_VARNOTSUPP)
printf("Not supported\n");
else
printf("[error: %s]\n", upscli_strerror(&ups));
+
+ upsdebug_call_finished0();
}
/* make sure we're actually connected to upsd */
static int check_ups_fd(int do_report)
{
+ upsdebug_call_starting0();
+
if (upscli_fd(&ups) == -1) {
if (do_report)
report_error();
+ upsdebug_call_finished1(": upscli_fd() failed");
return 0;
}
@@ -103,10 +133,12 @@ static int check_ups_fd(int do_report)
if (do_report)
printf("No UPS specified for monitoring\n");
+ upsdebug_call_finished1(": currups is null");
return 0;
}
/* must be OK */
+ upsdebug_call_finished0();
return 1;
}
@@ -117,14 +149,19 @@ static int get_var(const char *var, char *buf, size_t buflen, int verbose)
const char *query[4];
char **answer;
+ upsdebug_call_starting_for_str2(upsname, var);
+
/* pass verbose to check_ups_fd */
- if (!check_ups_fd(verbose))
+ if (!check_ups_fd(verbose)) {
+ upsdebug_call_finished0();
return 0;
+ }
if (!upsname) {
if (verbose)
printf("[No UPS name specified]\n");
+ upsdebug_call_finished1(": no UPS name");
return 0;
}
@@ -139,6 +176,8 @@ static int get_var(const char *var, char *buf, size_t buflen, int verbose)
if (ret < 0) {
if (verbose)
report_error();
+
+ upsdebug_call_finished1(": upscli_get() failed");
return 0;
}
@@ -146,10 +185,12 @@ static int get_var(const char *var, char *buf, size_t buflen, int verbose)
if (verbose)
printf("[Invalid response]\n");
+ upsdebug_call_finished1(": invalid response");
return 0;
}
snprintf(buf, buflen, "%s", answer[3]);
+ upsdebug_call_finished0();
return 1;
}
@@ -157,10 +198,15 @@ static void parse_var(const char *var)
{
char answer[SMALLBUF];
- if (!get_var(var, answer, sizeof(answer), 1))
+ upsdebug_call_starting_for_str1(var);
+
+ if (!get_var(var, answer, sizeof(answer), 1)) {
+ upsdebug_call_finished1(": get_var() failed");
return;
+ }
printf("%s", answer);
+ upsdebug_call_finished0();
}
static void do_status(void)
@@ -168,12 +214,14 @@ static void do_status(void)
int i;
char status[SMALLBUF], *ptr, *last = NULL;
+ upsdebug_call_starting0();
+
if (!get_var("ups.status", status, sizeof(status), 1)) {
+ upsdebug_call_finished1(": get_var() failed");
return;
}
for (ptr = strtok_r(status, " \n", &last); ptr != NULL; ptr = strtok_r(NULL, " \n", &last)) {
-
/* expand from table in status.h */
for (i = 0; stattab[i].name != NULL; i++) {
@@ -182,6 +230,8 @@ static void do_status(void)
}
}
}
+
+ upsdebug_call_finished0();
}
static void do_runtime(void)
@@ -189,8 +239,12 @@ static void do_runtime(void)
int total, hours, minutes, seconds;
char runtime[SMALLBUF];
- if (!get_var("battery.runtime", runtime, sizeof(runtime), 1))
+ upsdebug_call_starting0();
+
+ if (!get_var("battery.runtime", runtime, sizeof(runtime), 1)) {
+ upsdebug_call_finished1(": get_var() failed");
return;
+ }
total = (int) strtol(runtime, (char **) NULL, 10);
@@ -199,7 +253,7 @@ static void do_runtime(void)
seconds = total % 60;
printf("%02d:%02d:%02d", hours, minutes, seconds);
-
+ upsdebug_call_finished0();
}
static int do_date(const char *buf)
@@ -208,12 +262,16 @@ static int do_date(const char *buf)
time_t tod;
struct tm tmbuf;
+ upsdebug_call_starting0();
+
time(&tod);
if (strftime(datebuf, sizeof(datebuf), buf, localtime_r(&tod, &tmbuf))) {
printf("%s", datebuf);
+ upsdebug_call_finished0();
return 1;
}
+ upsdebug_call_finished1(": failed");
return 0;
}
@@ -221,8 +279,12 @@ static int get_img_val(const char *var, const char *desc, const char *imgargs)
{
char answer[SMALLBUF];
- if (!get_var(var, answer, sizeof(answer), 1))
+ upsdebug_call_starting_for_str3(var, desc, imgargs);
+
+ if (!get_var(var, answer, sizeof(answer), 1)) {
+ upsdebug_call_finished1(": get_var() failed");
return 1;
+ }
printf("sys, var);
@@ -232,6 +294,7 @@ static int get_img_val(const char *var, const char *desc, const char *imgargs)
printf("\" ALT=\"%s: %s\">", desc, answer);
+ upsdebug_call_finished0();
return 1;
}
@@ -289,6 +352,9 @@ static void split_imgarg(char *in, char *out, size_t outlen)
static int do_img(char *buf)
{
char *type, *ptr, imgargs[SMALLBUF];
+ int ret = 0;
+
+ upsdebug_call_starting2("for type '%s'", buf);
memset(imgargs, '\0', sizeof(imgargs));
@@ -309,15 +375,21 @@ static int do_img(char *buf)
|| !strcmp(type, "input.L3-N.voltage")
|| !strcmp(type, "input.L1-L2.voltage")
|| !strcmp(type, "input.L2-L3.voltage")
- || !strcmp(type, "input.L3-L1.voltage")) {
- return get_img_val(type, "Input voltage", imgargs);
+ || !strcmp(type, "input.L3-L1.voltage")
+ ) {
+ ret = get_img_val(type, "Input voltage", imgargs);
+ goto finish;
}
- if (!strcmp(type, "battery.voltage"))
- return get_img_val(type, "Battery voltage", imgargs);
+ if (!strcmp(type, "battery.voltage")) {
+ ret = get_img_val(type, "Battery voltage", imgargs);
+ goto finish;
+ }
- if (!strcmp(type, "battery.charge"))
- return get_img_val(type, "Battery charge", imgargs);
+ if (!strcmp(type, "battery.charge")) {
+ ret = get_img_val(type, "Battery charge", imgargs);
+ goto finish;
+ }
if (!strcmp(type, "output.voltage")
|| !strcmp(type, "output.L1-N.voltage")
@@ -325,8 +397,10 @@ static int do_img(char *buf)
|| !strcmp(type, "output.L3-N.voltage")
|| !strcmp(type, "output.L1-L2.voltage")
|| !strcmp(type, "output.L2-L3.voltage")
- || !strcmp(type, "output.L3-L1.voltage")) {
- return get_img_val(type, "Output voltage", imgargs);
+ || !strcmp(type, "output.L3-L1.voltage")
+ ) {
+ ret = get_img_val(type, "Output voltage", imgargs);
+ goto finish;
}
if (!strcmp(type, "ups.load")
@@ -335,26 +409,40 @@ static int do_img(char *buf)
|| !strcmp(type, "output.L3.power.percent")
|| !strcmp(type, "output.L1.realpower.percent")
|| !strcmp(type, "output.L2.realpower.percent")
- || !strcmp(type, "output.L3.realpower.percent")) {
- return get_img_val(type, "UPS load", imgargs);
+ || !strcmp(type, "output.L3.realpower.percent")
+ ) {
+ ret = get_img_val(type, "UPS load", imgargs);
+ goto finish;
}
- if (!strcmp(type, "input.frequency"))
- return get_img_val(type, "Input frequency", imgargs);
+ if (!strcmp(type, "input.frequency")) {
+ ret = get_img_val(type, "Input frequency", imgargs);
+ goto finish;
+ }
- if (!strcmp(type, "output.frequency"))
- return get_img_val(type, "Output frequency", imgargs);
+ if (!strcmp(type, "output.frequency")) {
+ ret = get_img_val(type, "Output frequency", imgargs);
+ goto finish;
+ }
- if (!strcmp(type, "ups.temperature"))
- return get_img_val(type, "UPS temperature", imgargs);
+ if (!strcmp(type, "ups.temperature")) {
+ ret = get_img_val(type, "UPS temperature", imgargs);
+ goto finish;
+ }
- if (!strcmp(type, "ambient.temperature"))
- return get_img_val(type, "Ambient temperature", imgargs);
+ if (!strcmp(type, "ambient.temperature")) {
+ ret = get_img_val(type, "Ambient temperature", imgargs);
+ goto finish;
+ }
- if (!strcmp(type, "ambient.humidity"))
- return get_img_val(type, "Ambient humidity", imgargs);
+ if (!strcmp(type, "ambient.humidity")) {
+ ret = get_img_val(type, "Ambient humidity", imgargs);
+ goto finish;
+ }
- return 0;
+finish:
+ upsdebug_call_finished0();
+ return ret;
}
static void ups_connect(void)
@@ -363,23 +451,28 @@ static void ups_connect(void)
char *newups, *newhost;
uint16_t newport = 0;
+ upsdebug_call_starting0();
+
/* try to minimize reconnects */
if (lastups) {
/* don't reconnect if these are both the same UPS */
if (currups && !strcmp(lastups->sys, currups->sys)) {
lastups = currups;
+ upsdebug_call_finished1(": skip: lastups same as currups");
return;
}
/* see if it's just on the same host */
newups = newhost = NULL;
- if (currups && upscli_splitname(currups->sys, &newups, &newhost,
- &newport) != 0) {
+ if (currups
+ && upscli_splitname(currups->sys, &newups, &newhost, &newport) != 0
+ ) {
printf("Unusable UPS definition [%s]\n", currups->sys);
fprintf(stderr, "Unusable UPS definition [%s]\n",
currups->sys);
+ upsdebug_call_finished1(": Unusable UPS definition");
exit(EXIT_FAILURE);
}
@@ -389,12 +482,16 @@ static void ups_connect(void)
free(newhost);
lastups = currups;
+
+ upsdebug_call_finished2(": pick next device on already connected data server [%s]", NUT_STRARG(currups->sys));
return;
}
/* not the same upsd, so disconnect */
free(newups);
free(newhost);
+
+ upsdebugx(2, "%s: not same data server as used by lastups: will connect to another", __func__);
}
upscli_disconnect(&ups);
@@ -407,6 +504,7 @@ static void ups_connect(void)
if (currups && upscli_splitname(currups->sys, &upsname, &hostname, &port) != 0) {
printf("Unusable UPS definition [%s]\n", currups->sys);
fprintf(stderr, "Unusable UPS definition [%s]\n", currups->sys);
+ upsdebug_call_finished1(": Unusable UPS definition");
exit(EXIT_FAILURE);
}
@@ -414,11 +512,15 @@ static void ups_connect(void)
fprintf(stderr, "UPS [%s]: can't connect to server: %s\n", currups->sys, upscli_strerror(&ups));
lastups = currups;
+ upsdebug_call_finished2(": pick first device on newly connected data server [%s]", NUT_STRARG(currups->sys));
}
static void do_hostlink(void)
{
+ upsdebug_call_starting0();
+
if (!currups) {
+ upsdebug_call_finished1(": no-op: no currups!");
return;
}
@@ -429,15 +531,39 @@ static void do_hostlink(void)
}
printf("\">%s", currups->desc);
+ upsdebug_call_finished0();
}
-static void do_treelink(void)
+static void do_treelink_json(const char *text)
{
+ upsdebug_call_starting0();
+
if (!currups) {
+ upsdebug_call_finished1(": no-op: no currups!");
return;
}
- printf("All data", upsstatpath, currups->sys);
+ printf("%s",
+ upsstatpath, currups->sys,
+ ((text && *text) ? text : "JSON"));
+
+ upsdebug_call_finished0();
+}
+
+static void do_treelink(const char *text)
+{
+ upsdebug_call_starting0();
+
+ if (!currups) {
+ upsdebug_call_finished1(": no-op: no currups!");
+ return;
+ }
+
+ printf("%s",
+ upsstatpath, currups->sys,
+ ((text && *text) ? text : "All data"));
+
+ upsdebug_call_finished0();
}
/* see if the UPS supports this variable - skip to the next ENDIF if not */
@@ -446,25 +572,33 @@ static void do_ifsupp(const char *var, const char *val)
{
char dummy[SMALLBUF];
+ upsdebug_call_starting3("for '%s' ( =? '%s')", NUT_STRARG(var), NUT_STRARG(val));
+
/* if not connected, act like it's not supported and skip the rest */
if (!check_ups_fd(0)) {
skip_clause = 1;
+ upsdebug_call_finished1(": check_ups_fd() failed");
return;
}
if (!get_var(var, dummy, sizeof(dummy), 0)) {
skip_clause = 1;
+ upsdebug_call_finished1(": get_var() failed");
return;
}
- if(!val) {
+ if (!val) {
+ upsdebug_call_finished1(": ok, not checking val");
return;
}
- if(strcmp(dummy, val)) {
+ if (strcmp(dummy, val)) {
skip_clause = 1;
+ upsdebug_call_finished1(": get_var() returned unexpected val");
return;
}
+
+ upsdebug_call_finished0();
}
static int breakargs(char *s, char **aargs)
@@ -497,15 +631,19 @@ static void do_ifeq(const char *s)
char *aa[MAX_PARSE_ARGS];
int nargs;
- strcpy(var, s);
+ upsdebug_call_starting_for_str1(s);
+
+ strncpy(var, s, sizeof(var) - 1);
nargs = breakargs(var, aa);
if(nargs != 2) {
printf("upsstats: IFEQ: Argument error!\n");
+ upsdebug_call_finished1(": arg error");
return;
}
do_ifsupp(aa[0], aa[1]);
+ upsdebug_call_finished0();
}
/* IFBETWEEN var1 var2 var3. Skip if var3 not between var1
@@ -519,60 +657,79 @@ static void do_ifbetween(const char *s)
long v1, v2, v3;
char *isvalid=NULL;
- strcpy(var, s);
+ upsdebug_call_starting_for_str1(s);
+
+ strncpy(var, s, sizeof(var) - 1);
nargs = breakargs(var, aa);
- if(nargs != 3) {
+ if (nargs != 3) {
printf("upsstats: IFBETWEEN: Argument error!\n");
+ upsdebug_call_finished1(": Argument error");
return;
}
if (!check_ups_fd(0)) {
+ upsdebug_call_finished1(": check_ups_fd() failed");
return;
}
if (!get_var(aa[0], tmp, sizeof(tmp), 0)) {
+ upsdebug_call_finished0();
return;
}
v1 = strtol(tmp, &isvalid, 10);
- if(tmp == isvalid) {
+ if (tmp == isvalid) {
+ upsdebug_call_finished0();
return;
}
if (!get_var(aa[1], tmp, sizeof(tmp), 0)) {
+ upsdebug_call_finished0();
return;
}
v2 = strtol(tmp, &isvalid, 10);
- if(tmp == isvalid) {
+ if (tmp == isvalid) {
+ upsdebug_call_finished0();
return;
}
if (!get_var(aa[2], tmp, sizeof(tmp), 0)) {
+ upsdebug_call_finished0();
return;
}
v3 = strtol(tmp, &isvalid, 10);
- if(tmp == isvalid) {
+ if (tmp == isvalid) {
+ upsdebug_call_finished0();
return;
}
- if(v1 > v3 || v2 < v3) {
+ if (v1 > v3 || v2 < v3) {
skip_clause = 1;
+ upsdebug_call_finished0();
return;
}
+
+ upsdebug_call_finished0();
}
static void do_upsstatpath(const char *s) {
+ upsdebug_call_starting_for_str1(s);
if(strlen(s)) {
upsstatpath = strdup(s);
}
+
+ upsdebug_call_finished0();
}
static void do_upsimgpath(const char *s) {
+ upsdebug_call_starting_for_str1(s);
if(strlen(s)) {
upsimgpath = strdup(s);
}
+
+ upsdebug_call_finished0();
}
static void do_temp(const char *var)
@@ -580,26 +737,36 @@ static void do_temp(const char *var)
char tempc[SMALLBUF];
double tempf;
- if (!get_var(var, tempc, sizeof(tempc), 1))
+ upsdebug_call_starting_for_str1(var);
+
+ if (!get_var(var, tempc, sizeof(tempc), 1)) {
+ upsdebug_call_finished1(": get_var() failed");
return;
+ }
if (use_celsius) {
printf("%s", tempc);
+ upsdebug_call_finished0();
return;
}
tempf = (strtod(tempc, (char **) NULL) * 1.8) + 32;
printf("%.1f", tempf);
+ upsdebug_call_finished0();
}
static void do_degrees(void)
{
+ upsdebug_call_starting0();
+
printf("°");
if (use_celsius)
printf("C");
else
printf("F");
+
+ upsdebug_call_finished0();
}
/* plug in the right color string (like #FF0000) for the UPS status */
@@ -608,16 +775,19 @@ static void do_statuscolor(void)
int severity, i;
char stat[SMALLBUF], *ptr, *last = NULL;
- if (!check_ups_fd(0)) {
+ upsdebug_call_starting0();
+ if (!check_ups_fd(0)) {
/* can't print the warning here - give a red error condition */
printf("#FF0000");
+ upsdebug_call_finished1(": check_ups_fd() failed");
return;
}
if (!get_var("ups.status", stat, sizeof(stat), 0)) {
/* status not available - give yellow as a warning */
printf("#FFFF00");
+ upsdebug_call_finished1(": get_var() failed");
return;
}
@@ -639,19 +809,25 @@ static void do_statuscolor(void)
default: printf("#FF0000"); break; /* red : error */
}
+
+ upsdebug_call_finished0();
}
static int do_command(char *cmd)
{
+ upsdebug_call_starting_for_str1(cmd);
+
/* ending an if block? */
if (!strcmp(cmd, "ENDIF")) {
skip_clause = 0;
skip_block = 0;
+ upsdebug_call_finished1(": ENDIF");
return 1;
}
/* Skipping a block means skip until ENDIF, so... */
if (skip_block) {
+ upsdebug_call_finished1(": skip until ENDIF");
return 1;
}
@@ -662,64 +838,77 @@ static int do_command(char *cmd)
} else {
skip_block = 1;
}
+ upsdebug_call_finished1(": ELSE (state toggle)");
return 1;
}
/* don't do any commands if skipping a section */
if (skip_clause == 1) {
+ upsdebug_call_finished1(": SKIP in effect");
return 1;
}
if (!strncmp(cmd, "VAR ", 4)) {
parse_var(&cmd[4]);
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "HOST")) {
printf("%s", currups->sys);
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "HOSTDESC")) {
printf("%s", currups->desc);
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "RUNTIME")) {
do_runtime();
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "STATUS")) {
do_status();
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "STATUSCOLOR")) {
do_statuscolor();
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "TEMPF")) {
use_celsius = 0;
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "TEMPC")) {
use_celsius = 1;
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "DATE ", 5)) {
+ upsdebug_call_finished0();
return do_date(&cmd[5]);
}
if (!strncmp(cmd, "IMG ", 4)) {
+ upsdebug_call_finished0();
return do_img(&cmd[4]);
}
if (!strcmp(cmd, "VERSION")) {
printf("%s", UPS_VERSION);
+ upsdebug_call_finished0();
return 1;
}
@@ -727,6 +916,7 @@ static int do_command(char *cmd)
if (refreshdelay > 0) {
printf("", refreshdelay);
}
+ upsdebug_call_finished0();
return 1;
}
@@ -734,82 +924,125 @@ static int do_command(char *cmd)
forofs = ftell(tf);
currups = ulhead;
+ upsdebugx(2, "%s: FOREACHUPS: begin with UPS [%s] [%s]", __func__, NUT_STRARG(currups->sys), NUT_STRARG(currups->desc));
+ upsdebugx(2, "%s: current skip_clause=%d skip_block=%d", __func__, skip_clause, skip_block);
ups_connect();
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "ENDFOR")) {
-
/* if not in a for, ignore this */
if (forofs == 0) {
+ upsdebug_call_finished1(": not in FOR");
return 1;
}
+ upsdebugx(2, "%s: ENDFOR: done with UPS [%s] [%s]", __func__, NUT_STRARG(currups->sys), NUT_STRARG(currups->desc));
+ upsdebugx(2, "%s: current skip_clause=%d skip_block=%d", __func__, skip_clause, skip_block);
currups = currups->next;
if (currups) {
+ upsdebugx(2, "%s: ENDFOR: proceed with next UPS [%s]", __func__, NUT_STRARG(currups->desc));
fseek(tf, forofs, SEEK_SET);
ups_connect();
}
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "HOSTLINK")) {
do_hostlink();
+ upsdebug_call_finished0();
+ return 1;
+ }
+
+ if (!strncmp(cmd, "TREELINK_JSON ", 14)) {
+ do_treelink_json(&cmd[14]);
+ upsdebug_call_finished0();
+ return 1;
+ }
+
+ if (!strcmp(cmd, "TREELINK_JSON")) {
+ do_treelink_json(NULL);
+ upsdebug_call_finished0();
+ return 1;
+ }
+
+ if (!strncmp(cmd, "TREELINK ", 9)) {
+ do_treelink(&cmd[9]);
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "TREELINK")) {
- do_treelink();
+ do_treelink(NULL);
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "IFSUPP ", 7)) {
do_ifsupp(&cmd[7], NULL);
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "UPSTEMP")) {
do_temp("ups.temperature");
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "BATTTEMP")) {
do_temp("battery.temperature");
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "AMBTEMP")) {
do_temp("ambient.temperature");
+ upsdebug_call_finished0();
return 1;
}
if (!strcmp(cmd, "DEGREES")) {
do_degrees();
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "IFEQ ", 5)) {
do_ifeq(&cmd[5]);
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "IFBETWEEN ", 10)) {
do_ifbetween(&cmd[10]);
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "UPSSTATSPATH ", 13)) {
do_upsstatpath(&cmd[13]);
+ upsdebug_call_finished0();
return 1;
}
if (!strncmp(cmd, "UPSIMAGEPATH ", 13)) {
do_upsimgpath(&cmd[13]);
+ upsdebug_call_finished0();
+ return 1;
+ }
+
+ if (!strncmp(cmd, "NUT_UPSSTATS_TEMPLATE ", 22) || !strcmp(cmd, "NUT_UPSSTATS_TEMPLATE")) {
+ upsdebugx(2, "%s: saw magic token, ignoring", __func__);
+ upsdebug_call_finished0();
return 1;
}
+ upsdebug_call_finished2(": unknown cmd: '%s'", cmd);
return 0;
}
@@ -819,8 +1052,9 @@ static void parse_line(const char *buf)
size_t i, len;
char do_cmd = 0;
- for (i = 0; buf[i]; i += len) {
+ upsdebug_call_starting_for_str1(buf);
+ for (i = 0; buf[i]; i += len) {
len = strcspn(&buf[i], "@");
if (len == 0) {
@@ -849,21 +1083,47 @@ static void parse_line(const char *buf)
/* pass it trough */
printf("%.*s", (int)len, &buf[i]);
}
+
+ upsdebug_call_finished0();
}
static void display_template(const char *tfn)
{
char fn[NUT_PATH_MAX + 1], buf[LARGEBUF];
+ upsdebug_call_starting_for_str1(tfn);
+
snprintf(fn, sizeof(fn), "%s/%s", confpath(), tfn);
- tf = fopen(fn, "r");
+ tf = fopen(fn, "rb");
if (!tf) {
fprintf(stderr, "upsstats: Can't open %s: %s\n", fn, strerror(errno));
printf("Error: can't open template file (%s)\n", tfn);
+ upsdebug_call_finished1(": no template");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!fgets(buf, sizeof(buf), tf)) {
+ fprintf(stderr, "upsstats: template file %s seems to be empty (fgets failed): %s\n", fn, strerror(errno));
+
+ printf("Error: template file %s seems to be empty\n", tfn);
+
+ upsdebug_call_finished1(": empty template");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Test first line for a bit of expected magic */
+ if (!strncmp(buf, "@NUT_UPSSTATS_TEMPLATE", 22)) {
+ parse_line(buf);
+ } else {
+ fprintf(stderr, "upsstats: template file %s does not start with NUT_UPSSTATS_TEMPLATE command\n", fn);
+
+ printf("Error: template file %s does not start with NUT_UPSSTATS_TEMPLATE command\n", tfn);
+
+ upsdebug_call_finished1(": not a valid template");
exit(EXIT_FAILURE);
}
@@ -872,6 +1132,7 @@ static void display_template(const char *tfn)
}
fclose(tf);
+ upsdebug_call_finished0();
}
static void display_tree(int verbose)
@@ -880,9 +1141,12 @@ static void display_tree(int verbose)
const char *query[4];
char **answer;
+ upsdebug_call_starting0();
+
if (!upsname) {
if (verbose)
printf("[No UPS name specified]\n");
+ upsdebug_call_finished1(": No UPS name specified");
return;
}
@@ -893,6 +1157,7 @@ static void display_tree(int verbose)
if (upscli_list_start(&ups, numq, query) < 0) {
if (verbose)
report_error();
+ upsdebug_call_finished1(": upscli_list_start() failed");
return;
}
@@ -922,6 +1187,7 @@ static void display_tree(int verbose)
if (verbose)
printf("[Invalid response]\n");
+ upsdebug_call_finished1(": invalid response");
return;
}
@@ -939,6 +1205,7 @@ static void display_tree(int verbose)
/* FIXME (AQ): add a save button (?), and a checkbt for showing var.desc */
printf("