2. 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).

Note

This change log section represents git commits in range v2.8.5..HEAD (commits 0e051f9f6..2caa3c875).

2.1. 2026-09-07 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: document NUT-Monitor fixes [PRs #3516, #3525]
  • docs/nut.dict: update to cover preceding PR merge train
  • clients/upsimage.c, clients/upsset.c, clients/upsstats.c: clients/{upsset,upsstats,upsimage}.c: return the debug ability

2.2. 2026-09-06 nmbro <nmbro@users.noreply.github.com>

  • NEWS.adoc, docs/nut.dict: NEWS.adoc: reword to avoid a dictionary addition Prefer rewording over growing docs/nut.dict for a one-off word. Drop "decompiling" (revert the dict entry from the previous commit) and reword around it; also avoid "PowerChute’s", which aspell rejected as a possessive of the dictionary word "PowerChute" even though the plain word is already listed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2
  • docs/nut.dict: add "decompiling" CI’s clean aspell (no personal word list) flagged it in the new NEWS.adoc beeper.mute bullet: decompiling PowerChute’s …. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2
  • NEWS.adoc: document outlet-group-count and beeper fixes Cover the three commits added to the PR #3591 series after it opened: outlet.group.count no longer requiring an unswitched main group, ups.beeper.status decoded as a bitmask, and beeper.mute replaced by beeper.enable/beeper.disable. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2
  • docs/man/apcmicrolink.txt, drivers/apcmicrolink.c: fix(apcmicrolink): replace beeper.mute with beeper.enable/disable beeper.mute wrote APC_USER_IF_CMD_MUTE_ALL_ACTIVE_AUDIBLE_ALARMS to 2:4.B.3B, a "user interface command" register ported from apc_modbus by analogy when this driver was first written. It had no confirmed effect on real Microlink hardware - getgray reported no observable change on an SMX1500RM2U (issue #3587), and the previous commit’s digging into ups.beeper.status turned up more evidence the apc_modbus-style command register isn’t what real client software uses here. Decompiling PowerChute’s own CompositeAudibleAlarm class settles it: its alarm on/off control (ChangeAudibleAlarmRunnable) writes a plain value - 1 for enabled, 2 for disabled - straight to the AlarmDelay usage (2:4.B.3A), the exact same usage ups.beeper.status reads. That is a persistent setting, not the transient "silence the currently sounding alarm, then re-arm automatically" action NUT documents beeper.mute as (data/cmdvartab: "Temporarily mute the UPS beeper"). Rather than keep a command named "mute" that actually disables the beeper until told otherwise, drop it and expose what the device genuinely supports: beeper.enable (writes 1) and beeper.disable (writes 2), both to 2:4.B.3A. test.panel.start is untouched - getgray already confirmed that command works as-is, still via the 2:4.B.3B "user interface command" register. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2
  • drivers/apcmicrolink-maps.c: fix(apcmicrolink): read ups.beeper.status as a bitmask, not an enum ups.beeper.status matched the exact values 193/194 (0xC1/0xC2), taken from round-tripping PowerChute writes on one SCL500RM1UC. Decompiling PowerChute’s own CompositeAudibleAlarm class shows this is actually a bitmask: bit 0 (0x01) means enabled, bit 1 (0x02) means disabled. The 0xC1/0xC2 values only worked because that particular unit always sets two extra bits (0xC0) alongside the enabled/disabled bit. A second device (APC Smart-UPS X1500, SMX1500RM2U, reported in https://github.com/networkupstools/nut/issues/3587) reports 0x09

    • bit 0 (enabled) plus bit 3, PowerChute’s unrelated "advanced menu" UI flag - which never matched either exact value, so ups.beeper.status published the raw number instead of a label. Switch the map to MLINK_DESC_BITFIELD_MAP (already used elsewhere in this driver for the same kind of field) with the real bit values. Verified against real captures from both devices: still "enabled" on the SCL500RM1UC, now "enabled" instead of "9" on the SMX1500RM2U. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2
  • docs/man/apcmicrolink.txt, drivers/apcmicrolink.c: fix(apcmicrolink): count switched outlet groups without a main group microlink_outlet_group_count() returned 0 whenever the unswitched "main" group (descriptor path 2:4.3E.B6) was absent, even if switched groups (2:4.3D[i].B6) were present. This silently skipped outlet.group.count, outlet.group.N.switchable, and all outlet instant commands on any device without an unswitched group, while the per-group name/status/delay data kept publishing fine through the separate descriptor-export path - a real device (APC Smart-UPS X1500, SMX1500RM2U) confirmed this: three switched groups with no group 0. Split the group-presence check per index (microlink_outlet_group_exists()) instead of gating everything on group 0, and use it everywhere group membership is tested: command registration, outlet.group.N validation, and the "all groups" target mask for load./shutdown. commands. Devices that do have a group 0 are unaffected (verified against a real SCL500RM1UC capture, byte-for-byte identical output before and after). Use of coding helper tools and AI disclosed: Claude Code was used for development assistance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LB4rN1EMA4s2TumED2ZWB2

2.3. 2026-09-06 Laurent Bigonville <bigon@bigon.be>

  • scripts/python/app/NUT-Monitor-py3qt6.in: nut-monitor: py3qt6: Fix unscoped QStandardPaths enums for PyQt6 PyQt6 requires fully-scoped enum access, unlike PyQt5 which allowed the shorthand QStandardPaths.AppDataLocation form. The unscoped references were missed during the Qt6 port and would raise AttributeError, breaking resource lookup (ui/pixmaps/icons) whenever the app runs from an installed location rather than the source tree. Fixes: #3624
  • scripts/python/app/NUT-Monitor-py3qt6.in: nut-monitor: py3qt6: Fix DesktopFileName to match the Qt6 desktop file Was copy-pasted from the Qt5 variant, causing the Qt6 build to misidentify itself to the window manager/taskbar as nut-monitor-py3qt5 instead of nut-monitor-py3qt6.

2.4. 2026-09-06 Jim Klimov <jimklimov+nut@gmail.com>

2.5. 2026-09-05 user01010111 <lapses.50.booster@icloud.com>

  • drivers/snmp-ups.c, tests/Makefile.am, tests/snmp-ups-setvar-test.c: tests: strengthen snmp-ups setvar regression Assert the exact OID, Net-SNMP type, value and call count passed by su_setOID() for successful and failed setters. Link the focused test against only snmp-ups.c and snmp-ups-helpers.c by supplying nut_snmp_set() at its existing public boundary. Production builds retain the real setter and complete MIB registry. AI assistance: OpenAI Codex GPT-6 (controller) and gpt-5.6-sol at high reasoning (implementation, validation and review). The human contributor remains responsible for reviewing and validating the change.

2.6. 2026-09-04 Jonathan Chavez <jonathangiancarlo1@gmail.com>

  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-*.in: fix AttributeError in gui_updater.attempt_reconnect() [#3509, #3530] The reconnection code added in #3530 reads the "ups_authentication_check" widget through self.__widgets, but __attempt_reconnect() lives in the gui_updater class, which has no __widgets attribute (name-mangled to _gui_updater__widgets). The widgets belong to the interface class and are reached, as the surrounding lines already do, through self.__parent_class._interface__widgets. The branch is taken whenever PyNUT.AuthConf.getAuthConf() returns an object, which is the common case, so every reconnection attempt raised: AttributeError: gui_updater object has no attribute _gui_updaterwidgets In the Qt variants an exception escaping a slot is fatal: PyQt turns it into qFatal() and the process aborts (SIGABRT) instead of reconnecting. Reproduced on Arch Linux, PyQt6 6.11 / Qt 6.11.2, by restarting upsd while NUT-Monitor-py3qt6 (master, 59fad7a97) was connected. Same one-line fix applied to the py3qt5, py3qt6 and py2gtk2 variants. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

2.7. 2026-09-04 Jiří Setnička <setnicka@seznam.cz>

  • NEWS.adoc, drivers/mge-hid.c: mge-hid: add Eaton 5P Gen2 models to mge_model_names The Gen2 units report iModel strings like "650iR G2", which match no entry in mge_model_names[], so mge_type stays MGE_DEFAULT. Both mge_battery_voltage_fun() and mge_battery_voltage_nominal_fun() return NULL for unknown types, so battery.voltage and battery.voltage.nominal are dropped although their UPS.PowerSummary.Voltage and .ConfigVoltage source paths are parsed fine. Names are assumed per the VA numbers and form factors of the Gen2 range, as was done before for the 5PX and 5SC series. Bump the subdriver version so driver.version.data reflects the extended mapping table.

2.8. 2026-09-01 user01010111 <lapses.50.booster@icloud.com>

  • common/snprintf.c: keep magnitudes unsigned Carry unsigned format arguments in unsigned LLONG from dopr() through fmtint(), rather than converting them through a possibly narrower signed long. Track negativity separately for signed decimal values and keep the magnitude negation in unsigned arithmetic so LLONG_MIN remains defined. Convert pointers through size_t before widening, and extend the built-in comparisons with narrow and wide unsigned extrema. AI assistance: OpenAI Codex gpt-5.6-sol with xhigh reasoning was used for repository analysis, implementation, review, drafting and validation. Anthropic Claude Fable 5 (claude-fable-5) with xhigh reasoning performed an independent adversarial review and produced a prototype used as design evidence. The human contributor reviewed the change and remains responsible for it.
  • common/snprintf.c: explain unsigned negation Document why fmtint negates the unsigned magnitude when formatting negative values, including LLONG_MIN. AI assistance: OpenAI Codex gpt-5.6-sol was used for repository analysis, implementation, review, drafting and validation. The human contributor reviewed the change and remains responsible for it.
  • NEWS.adoc, drivers/Makefile.am, tests/Makefile.am, {drivers ⇒ tests}/snmp-ups-setvar-test.c: tests: relocate snmp-ups setvar regression Move the focused SNMP setvar regression from drivers/ to tests/ and wire it through the existing linked-driver-source pattern. Restore drivers/Makefile.am to the normal driver-only build and add the requested NEWS entry for issue #3360. AI assistance: OpenAI Codex gpt-5.6-sol at high reasoning. The human contributor remains responsible for reviewing and validating the change.
  • NEWS.adoc, tests/Makefile.am, tests/become-user-root-warning-test.sh: tests: improve root-warning test portability Honour EXEEXT, pass the exact build-tree upslog path, and use the current UID-0 account name. Treat an empty TMPDIR as unset and add the requested NEWS entry. Refs #3471. AI assistance: OpenAI Codex gpt-5.6-sol at high reasoning was used for investigation, implementation and validation. The human contributor remains responsible for the change.
  • drivers/Makefile.am, drivers/snmp-ups-setvar-test.c, drivers/snmp-ups.c: drivers/snmp-ups: prefer exact outlet mappings Look up exact mappings before interpreting outlet names as numbered templates. Return the existing unknown status when a numbered template is absent, update server-side writable values through dstate, and track ownership for instantiated entries. Add a focused hardware-free regression for exact mappings, missing templates, valid numbered templates, unavailable mappings and cleanup. AI assistance: OpenAI Codex gpt-5.6-sol at high reasoning. The human contributor remains responsible for reviewing and validating the change.
  • common/common.c, tests/Makefile.am, tests/become-user-root-warning-test.sh: common: warn when become_user() leaves root credentials Check the real and effective UIDs after the POSIX credential transition, and warn once if either remains zero. Keep non-root starts and the intentionally privileged upsmon -p path unchanged. Fixes #3471. AI assistance: OpenAI Codex gpt-5.6-sol at high reasoning was used for investigation, implementation and validation. The human contributor remains responsible for the change.
  • AGENTS.md, docs/developers.txt: docs: wrap agent guidance source lines Wrap agent guidance source lines consistently with existing NUT documentation while preserving the rendered text. AI assistance: OpenAI Codex gpt-5.6-sol was used for drafting and validation. The human contributor remains responsible for the change.
  • AGENTS.md, Makefile.am, docs/developers.txt: docs: add instructions for coding agents Add a root AGENTS.md which directs coding agents to existing NUT contribution requirements and recurring project-specific review checks. Tell contributors using coding agents to ensure their tool reads the file, and register it for source distribution. AI assistance: OpenAI Codex gpt-5.6-sol was used for research, drafting and review. The human contributor remains responsible for the change.
  • NEWS.adoc, drivers/apc-mib.c: apc: expose NMC3 ambient temperature APC Network Management Card 3 units expose universal I/O temperature at uioSensorStatusTemperatureDegC, but the APC subdriver does not map that OID to ambient.temperature. Add the OID as the first ambient provider, reject the MIB’s -1 invalid sentinel, bump APCC_MIB_VERSION to 1.62, and document the addition. Fixes #3250
  • clients/upssched.c, common/wincompat.c, drivers/dstate.c, include/wincompat.h: win32: restrict named-pipe access Replace the shared present NULL DACL with an explicit ACL granting pipe access to the current process identity, LocalSystem and Administrators. Keep PIPE_REJECT_REMOTE_CLIENTS and apply the same descriptor to initial and replacement daemon, event, driver-state and upssched listeners. Include FILE_CREATE_PIPE_INSTANCE for replacement listener creation, and release the temporary ACL after CreateNamedPipe returns. AI assistance: OpenAI Codex gpt-5.6-sol with high reasoning coordinated and reconciled the work. OpenAI Codex gpt-daybreak-blue-latest with high reasoning was used for security analysis, implementation, adversarial review, cross-toolchain validation, native Windows validation and contribution drafting. The contributor reviewed the change and remains responsible for its quality and content.

2.9. 2026-09-01 Yifeng Li <tomli@tomli.me>

  • UPGRADING.adoc: huawei-ups2000 instant commands and Modbus fixes [#3593, #3603]
  • NEWS.adoc: huawei-ups2000 instant commands and Modbus fixes [#3593, #3603]

2.10. 2026-09-01 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: mention new AGENTS.md and SECURITY.md [#3470, #3608]

2.11. 2026-08-31 user01010111 <lapses.50.booster@icloud.com>

  • drivers/bestups.c: bestups: validate fixed-format Q1 replies Validate the complete 46-byte Q1 layout before periodic polling or shutdown copies and interprets its fields. Use one driver-local parser for both entry points so malformed field boundaries, embedded NUL bytes and unexpected whitespace are rejected without partial state publication. Preserve legacy fixed-width content tolerance, shutdown retries and the unconditional shutdown command. Bump the driver version to 1.13. AI assistance: OpenAI Codex gpt-5.6-sol (high reasoning) assisted with implementation and validation. OpenAI gpt-daybreak-blue-latest (high reasoning) performed independent security-boundary and candidate reviews.
  • drivers/metasys.c: metasys: validate UPS_INFO response length Reject UPS_INFO payloads outside the protocol-defined range before accessing response fields or copying the serial number. Derive the copy limit from the destination buffer and reserve space for the terminator. This preserves valid 1-12-byte serial numbers, leaves generic frame handling unchanged, and bumps the driver version to 0.15.
  • NEWS.adoc, drivers/xppc-mib.c: xppc: map inverter standby to OL Line-interactive XPPC devices can report standBy(8) while utility power supplies the load and only the inverter is idle. Mapping that value to OFF makes upsmon treat a healthy UPS as not supplying power. Map standBy(8) to OL, clarify the inverter-versus-UPS distinction, bump the XPPC MIB version to 0.42, and document the correction. Fixes #3512
  • clients/nutclient.cpp, common/common.c, common/nutconf.cpp, common/nutstream.cpp, common/nutwriter.cpp, drivers/adelsystem_cbi.c, drivers/apc_common.c, drivers/apc_modbus.c, drivers/apcmicrolink.c, drivers/dummy-ups.c, drivers/generic_modbus.c, drivers/liebert-gxe.c, drivers/main.c, drivers/netxml-ups.c, drivers/nutdrv_qx.c, drivers/phoenixcontact_modbus.c, drivers/upscode2.c, drivers/usbhid-ups.c, include/Makefile.am, include/nut-pragmas-covered-switch-default-end.h, include/nut-pragmas-covered-switch-default.h, server/upsd.c, tools/nut-scanner/scan_avahi.c, tools/nutconf/nutconf-cli.cpp: include: share covered-switch pragmas [#3595] Move the repeated GCC and Clang diagnostic push/pop sequence for exhaustive switches with defensive default clauses into intentionally repeat-includable paired headers. Replace only the 41 regions that use the same suppression family and distribute the headers as private build inputs. Leave the mixed -Wswitch-enum case and compiler-specific regions local. Refs #3595 AI assistance: OpenAI Codex gpt-5.6-sol was used for repository analysis, implementation, review, drafting and validation with high reasoning. The human contributor remains responsible for the contribution.
  • common/snprintf.c: preserve wide integer values Keep fallback integer formatting at LLONG width. This prevents pointers and long-long values from being truncated through long on LLP64 systems. Handle the signed minimum without overflow and extend TEST_SNPRINTF with pointer and long-long regression comparisons. Fixes #1602 AI assistance: OpenAI Codex gpt-5.6-sol was used for repository analysis, implementation, review, drafting and validation. The human contributor reviewed the change and remains responsible for it.
  • clients/cgilib.c, clients/cgilib.h, clients/upsimage.c, clients/upsset.c, clients/upsstats.c, conf/upsstats-modern-single.html.sample.in, docs/man/upsstats.html.txt, docs/nut.dict, tests/Makefile.am, tests/test_cgilib.c: clients: apply contextual encoding to CGI output Encode dynamic values for their HTML, URL component, JSON, or numeric CSS contexts. Keep CGI debug records on stderr and add focused regression coverage for the shared encoders.

2.12. 2026-08-31 Yifeng Li <tomli@tomli.me>

  • docs/man/huawei-ups2000.txt: document bug #3593 and bug #3603. Two bugs #3593, #3603 can prevent users from sending any instant commands or changing any variables at all. Document these known bugs in the man page.
  • drivers/huawei-ups2000.c: huawei-ups2000: bump driver version to v0.14.
  • drivers/huawei-ups2000.c: huawei-ups2000: use modbus_write_register, not modbus_write_registers, fix #3593. On some Huawei UPS2000 models, no instant commands or variable writes are possible, because modbus_write_registers() doesn’t work correctly. Upon investigation, Huawei UPS2000 doesn’t support multiple-register writes at all (Modbus command 0x10), the official datasheet only supports single- register writes (Modbus command 0x06). Multiple-register writes worked on some models, but this turned out to be undefined behavior, it doesn’t work with all models. This commit switches modbus_write_registers() to modbus_write_register() to fix the problem.
  • drivers/huawei-ups2000.c: huawei-ups2000: fix broken instant command invocations, close #3603. A serious regression was introduced during the "fightwarn" campaign that completely broke bypass.start, shutdown.return, shutdown.reboot, and shutdown.reboot.graceful. Any attempt to use them will fail, with an error logged to syslog: huawei-ups2000: instcmd: command [bypass.start] reg1 is negative huawei-ups2000: instcmd: command [shutdown.return] reg1 is negative huawei-ups2000: instcmd: command [shutdown.reboot] reg1 is negative huawei-ups2000: instcmd: command [shutdown.reboot.graceful] reg1 is negative Because the regression was introduced shortly after the initial driver was merged into the upstream, these instant commands do no work in any NUT version (with the exception of the earliest development build). This problem was not discovered during pre-merge test for the same reason: it’s a regression that occurred after the Pull Request was merged. In huawei-ups2000, all instant commands are driven by a lookup table to consolidate the logic to a central dispatcher. Some commands are dispatched to a (*handler_func)(uint16_t reg1) function, which is used to handle commands that needs additional processing. Not all *handler_func actually reads "reg1", it’s an optional argument. Because declaring the function to accept a variadic or a (void *) would be over-engineering, a simple unsigned 16-bit value is used, *handler_func may or may not use it. During the project-wide "fightwarn" refactor campaign, it was noticed that "reg1" is stored in the lookup table as "int16_t" (-1 indicates an unneeded register), but *handler_func accepts a "uint16_t". In case that *handler_func doesn’t need the register ID from the lookup table, "reg1" is implicitly converted from -1 to 65535. This is harmless, since "reg1" is not actually used in this case. Unfortunately, in an attempt to make the code "safe" by suppressing compiler warning, an incorrect range check was added to the function, which refuses to invoke *handler_func if "reg1" is negative. As a result, all instant commands that don’t use "reg1" are broken. These commands include bypass.start, shutdown.return, shutdown.reboot, and shutdown.reboot.graceful. To fix this problem, fix the implicit type conversion correctly by change the "reg1" data type in the lookup table from "int16_t" to "uint16_t", and remove the "negative reg1" check. As defensive programming. all *handler_func that actually need "reg1" would check and abort if "reg1" is 0. Fixes: e9f02e242acf ("drivers/huawei-ups2000.c: instcmd(): range-check and cast for ups2000_write_register() and handler_func()")

2.13. 2026-08-31 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: mention metasys driver fixed UPS serial number parsing [GHSA-6p5c-r2jg-r9mj]
  • NEWS.adoc: mention update of bestups driver with Megatec Q1 parser fix [GHSA-hwqr-5pjj-3m25]
  • drivers/bestups.c: update comments and markup [GHSA-hwqr-5pjj-3m25]
  • docs/nut.dict: add quiesced [#3599]
  • docs/developers.txt: use repetitive pragma combinations via header files [#3599]

2.14. 2026-08-30 nmbro <nmbro@users.noreply.github.com>

  • NEWS.adoc: document apcmicrolink driver updates [PR #3591]
  • drivers/apcmicrolink-usb.c: fix(apcmicrolink): resolve conflicting switch-default warnings libusb_transfer_status already had every enumerator listed as an explicit case, so a trailing default was dead code under Clang’s -Wcovered-switch-default (-Werror). Dropping the default then broke the fightwarn build instead: -Wswitch-default demands a default on every switch regardless of enumeration coverage. The two rules cannot both be satisfied by the source text, so suppress -Wswitch-default for this switch via the guarded-pragma pattern already used in apcmicrolink.c, matching m4/ax_c_pragmas.m4’s per-warning detection.
  • docs/Makefile.am, docs/apcmicrolink-descriptors.txt, docs/new-drivers.txt: docs(apcmicrolink): document the descriptor format the driver parses Adds docs/apcmicrolink-descriptors.txt, covering the frame/page/descriptor structure this driver parses: frames as [page id][width data bytes][2 byte checksum], the flat-blob assembly from page N at offset N * width, and the descriptor table that maps this wire structure to descriptor paths

    • themselves MIB-like, with attribute IDs that are scope-relative (the same .11 means "test result" whether it hangs off the battery, UPS, or another collection). Everything in it was derived from a live APC SCL500RMI1UC, for anyone extending drivers/apcmicrolink-maps.c to cover more of a device. Wired into the developer guide’s build via docs/Makefile.am and docs/new-drivers.txt. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.
  • drivers/apcmicrolink-maps.c, drivers/apcmicrolink.c, drivers/apcmicrolink.h: fix(apcmicrolink): burst-poll a full pass so auth and page reads keep up Root cause: this driver fetched one tunnel record per pollinterval. The device answers requests roughly every 2-3 ms when asked continuously (as PowerChute does), but gates its live-measurement pages behind a slave-password handshake it does not acknowledge until about 20 further exchanges after the response goes out. At one record per pollinterval the handshake’s acknowledgement was never collected, and the driver’s own readiness check treated sending the response as "done" - so every measurement read 0 indefinitely on a device that was answering normally the whole time. microlink_poll_burst() now drives the tunnel like APC’s own client: keep requesting the next record until the device stops answering, once per upsdrv_updateinfo(), budgeted to one full pass over page0.count (bounded by MLINK_POLL_BURST_MIN/MAX and an MLINK_POLL_BURST_MAX_SEC wall-clock ceiling so a slow-answering device cannot hold updateinfo() open indefinitely). The burst also keeps going past its budget while a handshake is in flight (microlink_auth_pending(), MLINK_AUTH_GRACE_SEC), so a session re-established mid-updateinfo() does not send its auth response on the burst’s last record and then go quiet before collecting the answer. microlink_check_auth_result() diagnoses the handshake explicitly: experimental.microlink.diag.auth_status / .auth_refused, an hourly LOG_WARNING while refused, and a one-time warning if AUTH_STATUS ever sets a bit this driver does not know about. Readiness still accepts authentication_sent on its own once the grace window closes - a refused handshake still leaves identity data and the standard-HID fallback worth publishing. upsdrv_cleanup() now sends STOP before closing the session. Seen live on an SCL500RMI1UC: exiting mid-burst left the device answering a later client’s INIT with whatever page its cursor had reached instead of page 0, and it would not resync until re-enumerated. Also, a batch of descriptor-map corrections gathered while chasing the above (each verified against a live SCL500RMI1UC, several by watching PowerChute write or read the same usage): * 2:4.7.28 and 2:4.7.49 are percentages of nominal, not absolute power - they were mapped straight onto ups.realpower/ups.power, so both read two orders of magnitude low and ups.load then divided one of them by its nominal rating a second time. 2:4.7.28 now feeds ups.load directly, and microlink_publish_derived_power()/microlink_publish_scaled_percent() derive ups.realpower and ups.power (the apparent-power side kept as experimental.ups.load.apparent, since NUT has no standard name for it) by scaling against the nominal ratings, matching PowerChute’s own numbers. * ups.test.result now comes from 2:4.5.11 (the battery-scope test, which actually stepped Pending → InProgress → Passed during a PowerChute- triggered self test) rather than 2:11 (UPS-scope, never moved off None on this hardware). 2:11 stays mapped under an experimental name and microlink_publish_test_result() promotes it to ups.test.result on any device that lacks the battery-scope usage - descriptor attribute IDs are scope-relative, so a differently built model could populate the other one. * 2:4.5.18 is a self-test schedule enum, not the interval-in-seconds ups.test.interval calls for (two of its members have no interval at all). It is now experimental.microlink.battery.test.schedule, and microlink_publish_test_interval() derives ups.test.interval from the members that do imply a recurring period, matched on the raw bits rather than the label text. * New mappings: input.sensitivity (3:25, values confirmed against PowerChute’s dropdown), ups.beeper.status (2:4.B.3A, both values round- tripped by writing them from PowerChute), experimental.battery.firmware (2:4.5.9.4A) and experimental.statistics.battery.transfers (2:4.5.F.59, PowerChute’s "Number Of Times On Battery"). * Dropped the duplicate device-status publish at 2:4.A: it used the same apc_status_map already consumed via microlink_desc_publish_map into ups.status/alarms, so the second copy was strictly worse (missing LB and the charger flags) rather than a distinct value. * Renamed for consistency: experimental.device.sku and experimental.battery.sku to device.part / experimental.battery.part (they are part numbers, not SKUs); the microlink.* diagnostic namespace to experimental.microlink.* throughout, since none of it is a settled name; and 2:4.9.42 from experimental.device.sku to device.part directly (an existing standard name this driver had not picked up). Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.

2.15. 2026-08-30 Lucas Bocchi <lucas.bocchi@gmail.com>

  • drivers/nhs_ser.c: Simplify NHS initialization packet helper Pass the packet data, byte count and serial descriptor directly to the helper. Keep serial validation, reopening, write checks and diagnostics inside the helper so the initialization calls are easier to read.
  • NEWS.adoc, drivers/nhs_ser.c: Refresh NHS hardware discovery during driver lifecycle
  • NEWS.adoc, docs/nut.dict, drivers/nhs_ser.c: Apply NHS hardware feedback from freechurros Check the HWINFO checksum in its interpreter and publish the documented lower-case battery charger states. Log already-known HWINFO packets quietly and expose rejected frames through raw packet debugging. Credit the packet captures and follow-up analysis contributed in issue #3592 directly beside the affected code and in the release notes. Suggested-by: Free Churro <freechurrosnet@gmail.com>
  • docs/nut.dict, drivers/nhs_ser.c: Fix strict NHS driver CI diagnostics Initialize protocol and bypass metadata explicitly for every known model so Clang’s missing-field check remains clean. Add a documented defensive default to the initialization selector and teach the documentation spell checker the hexadecimal xFE token.
  • NEWS.adoc, drivers/nhs_ser.c: Fix NHS serial packet framing and model handling Honor the packet length declared at byte 1 so embedded 0xFF and 0xFE payload bytes do not restart or truncate valid frames. Preserve partial packet state across serial-read timeouts and validate the declared length and final marker before dispatch. Add the compatibility initialization request, model description override, protocol metadata and per-model bypass alarm behavior. Correct the published input voltage extrema and document the changes.

2.16. 2026-08-30 Jim Klimov <jimklimov+nut@gmail.com>

  • tools/asciidoc-github-links.pl: do not match standalone plus (asciidoc paragraph glue) on a line after a link [#1953]
  • tools/Makefile.am: add check-asciidoc-github-links goal [#1953]
  • docs/Makefile.am: handle possible absence of perl [#1953]
  • tools/asciidoc-github-links.pl: do not re-evaluate patterns in links generated earlier [#1953]
  • NEWS.adoc, docs/nut.dict, tools/asciidoc-github-links.pl: tools/asciidoc-github-links.pl, NEWS.adoc: handle lists started with plural "PRs, issues, advisories" [#1953, #3594]
  • tools/asciidoc-github-links.pl: use qr/.../ to define regexes [#1953]
  • tools/asciidoc-github-links.pl: streamline with separate definitions of GH URI parts [#1953] This way the script can be more easily reused by other projects and is easier to maintain with symbolic names for repetititve sequences.
  • docs/Makefile.am, tools/Makefile.am, tools/asciidoc-github-links.pl: tools/asciidoc-github-links.pl: DOCBUILD_FILTER_GITHUB_LINKS: transplant the stack of Makefile inline SED expressions into a more flexible separate perl script [#1953]
  • docs/Makefile.am: *.adoc-parsed: add support for GHSA links [#3594]
  • drivers/apcmicrolink-usb.c: new clang insists on casting *malloc() results [#3591]
  • .github/workflows/01-make-dist.yml: shell-print debug info using shell variable expansion from envvars Avoid potential code injection via crafted PR source git branch names [GHSA-3xvj-5pxf-f3hg]
  • NEWS.adoc: Fix aspell complaint regarding re-synchronization [#3591]

2.17. 2026-08-29 user01010111 <lapses.50.booster@icloud.com>

  • tests/Makefile.am, tests/cppunit-warnings-end.h, tests/cppunit-warnings.h, tests/cpputest-client.cpp, tests/cpputest.cpp, tests/example.cpp, tests/nutclienttest.cpp, tests/nutconf_parser_ut.cpp, tests/nutconf_ut.cpp, tests/nutipc_ut.cpp, tests/nutstream_ut.cpp: tests: consolidate CppUnit warning suppression Move the repeated CppUnit diagnostic setup and teardown into paired test-local headers. Keep the zero-as-null-pointer suppression local to cpputest.cpp, correct the Clang push and ignored pragma pair, and distribute the new headers. AI assistance: OpenAI Codex gpt-5.6-sol was used for planning, repository analysis, implementation, review, drafting and macOS validation. OpenAI Codex gpt-5.6-terra was used for Raspberry Pi validation. Both used high reasoning where applicable. The contributor reviewed the change and remains responsible for it.

2.18. 2026-08-29 nmbro <nmbro@users.noreply.github.com>

  • drivers/apcmicrolink.c: fix(apcmicrolink): use the HID fallback when it is needed The standard-HID-PDC fallback existed but could not engage in the case it was written for. Found on an SCL500RMI1UC that answered every poll while reporting an all-zero state: ups.status stayed empty for over two days with usable HID PDC reports arriving on the same endpoint the whole time, and upsmon therefore had nothing to act on. Four separate reasons it never took over, each fixed here: * microlink_start_session() refreshed last_poll_success on a bare handshake, so a device that answered the handshake but sent no data looked freshly polled forever. Data freshness is now tracked separately, and only real polled frames advance it. * upsdrv_updateinfo() treated a successful reconnect as good data. A handshake proves the device answers, not that the tunnel delivers. * Startup called fatalx() when no fallback snapshot had been decoded yet, even where the device clearly exposes the usages - they simply had not arrived in the startup window. It now starts and publishes them when they do. The old message also claimed no fallback was available on a device whose descriptor advertised one. * Staleness alone was not enough: this device answers on time and reports zeroes, so the data is fresh and useless. A poll that yields no ups.status flag at all now hands over too. MLINK_HID_FALLBACK_MAX_AGE_SEC goes from 10s to 30s. A usbmon capture showed the two streams are not concurrent: PDC reports arrive every 6.0s while the tunnel is idle, then stop for 19.2s whenever the device services tunnel traffic. A 10s window expired inside that gap, so the fallback became unpublishable exactly when the tunnel was also producing nothing, and ups.status went empty once per cycle. MLINK_DATA_STALE_SEC moves to 45s to stay above it. Handover is deliberately asymmetric. The fallback takes over immediately but only hands back after the Microlink source has looked plausible for 30s continuously; without that the two swapped on alternate polls, measured at 56 handovers in 3 minutes, and a status that flips every 2s is worse than one that lags a recovery. Plausibility is judged only from ups.status, which is rewritten every poll - testing battery.charge made the fallback read back a value it had published itself and hand over to a dead source. microlink_publish_hid_fallback() now writes its measurements before committing status, and states the charging condition explicitly. dstate’s status_commit() infers CHRG/DISCHRG from battery.charge movement when the driver reports neither, and it was comparing the degenerate Microlink value against the previous fallback one and synthesizing a DISCHRG that contradicted the OL being set in the same breath. Handovers are logged with their reason at debug level 1, once per transition. A device that answers normally while reporting nothing usable also warns at LOG_WARNING, hourly, and publishes microlink.diag.status_degenerate: a USB bus reset does not clear that state (tested, along with deauthorize and driver unbind - none re-enumerate the device), so the user needs to know the UPS itself likely needs a power cycle. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.
  • drivers/apcmicrolink-usb.c: fix(apcmicrolink): name the async transfer states CodeQL flagged the switch on transfer→status as handling only two of the enum’s values by name. Behaviour is unchanged - everything not cancelled or gone still falls through to the resubmit below - but the remaining states are now listed explicitly so the intent is readable and a future libusb addition shows up as a warning rather than silently joining the default. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.
  • drivers/apcmicrolink.c: feat(apcmicrolink): warn on unimplemented stuffing Page 0 bit 1 (MLINK_PAGE0_FLAG_IMPLICIT_STUFFING) asks for implicit byte stuffing on the wire. Nothing in the parser implements it; the bit was only published as microlink.flag.implicit_stuffing and otherwise ignored, so a device that set it would have its frames read as if stuffing were disabled and would fail with checksum errors that point nowhere near the actual cause. No device seen so far sets the bit - both an SCL500RMI1UC and an SMX1500RM2U report flags 0x09, i.e. AUTH_REQUIRED and DESCRIPTOR_PRESENT with stuffing clear - so rather than ship an unstuffing routine that cannot be exercised or tested, say plainly what the driver is doing and ask for a report. Logged once per process at LOG_WARNING, since it is invisible at the default debug level otherwise and describes a condition the user cannot work around. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.
  • NEWS.adoc, drivers/apcmicrolink.c: fix(apcmicrolink): reject a contradictory page 0 Page 0 announces the frame width that every later frame is parsed with, and microlink_cache_object() accepted any checksum-valid copy of it. A corrupt copy is therefore unrecoverable: the bogus width becomes the length microlink_try_extract_frame_at() demands, no frame checksum- validates again, and the session dies with the device having done nothing wrong. Seen live on a Smart-UPS X 1500 (FW 03.8) in issue #3587. The device served 60 populated pages, then emitted STOP-filled pages and restarted its page index. The wrapped page 0 was checksum-valid and announced a width of 247 inside a 16-byte frame, which replaced the correct width=16/pages=154 header decoded a second earlier. From that point the parser needed 250 contiguous valid bytes out of 19-byte records, so it never extracted another frame. A genuine page 0 always arrives in a frame of exactly the width it announces: on the first read the frame length is derived from that byte, and on every later read the frame length is the established width. So require the two to agree, and keep the previous page 0 when they do not. This is in the shared framing layer, so it applies to the serial transport as well. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.

2.19. 2026-08-28 nmbro <nmbro@users.noreply.github.com>

  • NEWS.adoc, docs/man/apcmicrolink.txt, drivers/apcmicrolink-maps.c, drivers/apcmicrolink-usb.c, drivers/apcmicrolink-usb.h, drivers/apcmicrolink.c, drivers/apcmicrolink.h, drivers/libusb1.c, drivers/nut_libusb.h: fix(apcmicrolink): async USB reads and stability Long-run testing of the USB HID tunnel transport against an SCL500RMI1UC found the driver dropping the session after an inconsistent interval and then failing to recover. Several distinct causes, fixed together here: * The interrupt-IN read was issued per call, so the driver only listened while it happened to be blocked waiting for a reply. This device can go several seconds between replies, and pushes unrelated HID reports on the same pipe meanwhile, so replies arriving outside that window were lost. Reads are now served by a permanently outstanding asynchronous transfer serviced by a dedicated pump thread; only that thread touches libusb’s event loop, and everything else takes a lock to drain the queue it fills. Builds without libusb-1.0 or pthreads, and any failure to start the listener, still use the previous synchronous per-call read. This needs the libusb-1.0 context, exposed as nut_libusb_get_context(). * A USB bus reset was triggered off a retry count, which fired just as often for a live-but-stalled device that a reset never helped. It is now triggered only when a read, write or transfer completion has actually reported NO_DEVICE, i.e. a genuine unplug or power cycle. * The cached page 0 contents were wiped on every session (re)start, and the read queue was flushed on every session retry - the latter discarding genuine replies that had already arrived. Neither is done any more. * The authentication challenge was a fixed 0x00 0x00, making the exchange trivially predictable; it is now randomized, as APC’s own client does. * When the kernel’s usbhid driver reclaims the interface, every submission fails instantly with EBUSY and nothing paced the retries - one observed incident spun at roughly 14,000 failed submissions per second for over half an hour. That case now backs off and logs, rate-limited. * Warn once when built against libusb-0.1, whose interrupt-IN read did not honor its timeout on at least one tested system, hanging the driver. Also maps two further descriptor usages that were previously visible only as microlink.unmapped.*: experimental.battery.serial (the battery pack’s own serial, confirmed against the compartment label on a real unit) and microlink.diag.slave_password_echo (the register the auth challenge is written to, echoed back - useful for troubleshooting, but not by itself proof that authentication was accepted). The apcmicrolink(8) USB MODE section is updated to match: it described a periodic reset attempt whenever the tunnel stayed unresponsive, which is no longer what happens, and now also notes the usbhid/EBUSY backoff. It gains a "libusb backend" subsection recording that USB mode is tested only against libusb-1.0, what goes wrong on libusb-0.1, and that the always-listening reader needs libusb-1.0 plus pthreads. Comments in apcmicrolink-usb.c were trimmed throughout: design history, references to material outside this repository, and restatements of what the code already says are gone. Use of coding helper tools and AI disclosed: Claude Code was used for development assistance.

2.20. 2026-08-28 Jim Klimov <jimklimov+nut@gmail.com>

  • server/netssl.c: ssl_init(): debug-log the list of enabled OpenSSL ciphers - wrap into pragmas against warnings for OpenSSL headers on some systems [#3331]

2.21. 2026-08-27 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/PyNUT.py.in: adapt to lack of overlapping default SSL ciphers [#3331]
  • NEWS.adoc: made hidparser.c UsageTab size dynamic [#3588]
  • include/nut_float.h, tests/ecoflow_cdc_protocol_utest.c: define f_equal_1e_2() etc. with higher tolerance (1e-2) [#3569]

2.22. 2026-08-27 Adam Bedard <adam.bedard@gmail.com>

  • drivers/hidparser.c, drivers/hidtypes.h: drivers/hidparser.c: size the HID usage stack from the report descriptor length Follow-up to #3586, where the question was raised whether a real device could legitimately report more than USAGE_TAB_SIZE usages and get its descriptor truncated by the bound added there. It could. The usage stack was a fixed HIDNode_t[50] inside HIDParser_t, and once it filled, parsing stopped and every item after that point was dropped. Before #3586 the same descriptor walked off the end of the allocation instead, so the truncation is new, but the 50-usage ceiling is not - it just used to be enforced by a log line after the damage was done. Parse_ReportDesc() already knows the descriptor length, so use it. Every Usage item costs at least the one prefix byte it is introduced by, so an n-byte descriptor can push at most n usages, and an n-slot stack can always hold all of them. The ceiling is gone for any descriptor that fits in the length we were given. USAGE_TAB_SIZE stays as the floor so short descriptors keep the capacity they have always had, and the overflow check stays as a backstop - it is now unreachable for a descriptor that fits in n, but it costs one comparison and the invariant it guards is worth stating in code. UsageSize goes from uint8_t to size_t. At its old width it wrapped at 256 regardless of how large the table was, so widening the table alone would not have raised the effective limit. Cost is one calloc of 4 bytes per descriptor byte, freed before Parse_ReportDesc() returns: at most 24kB for the 6144-byte MAX_REPORT_SIZE the libusb and libshut backends cap at, and at most 8kB for the 2048-byte buffer apc_modbus gets from libmodbus. It happens once per device open, not in the data update loop.
  • drivers/hidparser.c: refuse truncated HID items instead of reading past the descriptor The item loop checks that Pos is inside the descriptor before it reads an item prefix, but nothing checks that the 1, 2 or 4 payload bytes the prefix promises are there as well. A descriptor whose last byte is a size-bearing prefix therefore reads up to 4 bytes past the end of the buffer the caller handed us. Where that lands depends on the backend, and in every case today it is a short read just off the end of a fixed buffer rather than anything wilder: libusb1 / libusb0 / libshut rdbuf is a MAX_REPORT_SIZE (6144) byte stack array and rdlen is capped at sizeof(rdbuf), so the read leaves the array only for rdlen in [6141, 6144]. apc_modbus the buffer is libmodbus’s 2048 byte stack array in _modbus_rtu_usb_connect() and the length is the actual transfer count, so the equivalent window is [2045, 2048]. Both windows are device-supplied, so both are reachable, and either way the parser should not read past the length it was given. Refuse the truncated item and stop, which is the same handling the other malformed-descriptor cases get. Reported at debug level rather than LOG_ERR: a descriptor that ends mid-item is malformed, but a truncated USB transfer is a plausible way for a real device to produce one, so it belongs with the PATH_SIZE refusals rather than with the two that mean malformed or hostile.

2.23. 2026-08-26 Jim Klimov <jimklimov+nut@gmail.com>

  • server/netssl.c: ssl_init(): debug-log the list of enabled OpenSSL ciphers [#3331]
  • drivers/microsol-apc.c, drivers/upscode2.c: drivers/upscode2.c: revise use of fabs() ⇒ fabsf() for floats
  • tests/ecoflow_cdc_protocol_utest.c: revise use of fabs() ⇒ f_equal() [#3569]

2.24. 2026-08-26 Adam Bedard <adam.bedard@gmail.com>

  • drivers/hidparser.c: log HID path depth refusals at debug level [#3586] The five refusals the two prior commits added were all LOG_ERR, which is right for the two that only a malformed or hostile descriptor produces - the usage stack overflow and the unbalanced End Collection - but heavy for the three PATH_SIZE ones. A path deeper than ten collections is the one case here with a plausible innocent reading: a real device nested further than the parser was built for. Those three drop to upsdebugx(1). The refusal itself is unchanged; only where it is reported moves.

2.25. 2026-08-25 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: document hidparser bound-usage and path stacks [#3586]

2.26. 2026-08-25 Adam Bedard <adam.bedard@gmail.com>

  • NEWS.adoc: reword to clear the aspell spellcheck [#3586] The dist tarball job runs aspell over NEWS.adoc against docs/nut.dict, which has "parsers" but not the possessive "parser’s", so the previous wording failed the spellcheck. Reword instead of growing the dictionary; the meaning is unchanged.
  • NEWS.adoc: clarify what triggers the hidparser overflow [#3586] The entry said "too many descriptors", which reads as a count of report descriptors. The overflow needs a single report descriptor carrying more usage items than USAGE_TAB_SIZE holds; a device with several descriptors is fine as long as no individual one is over-long.
  • drivers/hidparser.c: bound the HID path stack and reject unbalanced End Collection Same shape as the usage stack: the PATH_SIZE check was a upslogx() after the loop rather than a test at the store. The three places that append to pParser→Data.Path.Node[] (ITEM_COLLECTION, the index node in ITEM_FEATURE and friends, and the main-item path store) all wrote first and complained later. ITEM_END_COLLECTION had a second problem: it decremented Path.Size unconditionally, so an End Collection with no matching Collection wrapped the unsigned counter to 255 and the "remove index if any" test on the next line read far past Path.Node[PATH_SIZE]. Unlike the usage stack this one stays inside the HIDParser_t allocation, so a sanitizer does not flag it, which is why it is folded in here rather than reported separately. It is not only an overread: on an unbalanced End Collection the unpatched parser returns success with wrong paths - the first node of every path missing and pathsize 2 where it should be 3 - after logging "HID path too long". A device reporting silently wrong values is harder to notice than a crash.
  • drivers/hidparser.c: bound the HID usage stack to fix a heap out-of-bounds write [GHSA-f444-v6f2-2vcj] ITEM_USAGE stored into pParser→UsageTab[pParser→UsageSize] without ever checking UsageSize against USAGE_TAB_SIZE. The only USAGE_TAB_SIZE check in the file sits after the parsing loop and just calls upslogx(), so it reports the problem once the writes have already happened. UsageTab is HIDNode_t[50] and it is the last member of the heap-allocated HIDParser_t, so a report descriptor carrying more than 50 usage items writes attacker-supplied 32-bit values past the end of the allocation. Check before the store instead and return -1, which is the established "stop here" return in this parser: Parse_ReportDesc() breaks out of its loop on ret < 0, so everything parsed up to that point is kept and the rest of the descriptor is dropped. Letting UsageSize reach USAGE_TAB_SIZE exposes a one-past-the-end read in the two unstack loops, which shifted UsageTab[j\+1] down for every j < UsageSize. The last slot has no successor, so shift in 0 there; unused slots are zeroed by ResetLocalState() anyway. Reproducer and a before/after test are in the linked advisory. Reachable from a malicious USB device or compromised UPS firmware by way of the report descriptor read in nut_libusb_open().

2.27. 2026-08-24 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: fix exclusion for freebsd-15 C89 [#3583]
  • Jenkinsfile-dynamatrix: skip failing platform combo for FreeBSD 15 and strict C89 for now [#3583]

2.28. 2026-08-23 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/.gitignore: add ecoflow-cdc-protocol* file names [#3569]
  • common/snprintf.c, include/proto.h: refine check for header availability; indent preprocessor macros per style guide [#3577]
  • tests/Makefile.am: link fixreportdesctest-tripplite-hid against libm [#3581]
  • tests/fixreportdesctest-tripplite-hid.c: fix "duplicate declaration" of mock disable_fix_report_desc [#3581]
  • drivers/tripplite-hid.c: tripplite_chemistry_fun(): fix for the case of unresolved model [#3581] At least when included into tests, this code fails on some static analyzers: In file included from fixreportdesctest-tripplite-hid.c:60: ../drivers/tripplite-hid.c: In function ‘tripplite_chemistry_fun’: ../drivers/tripplite-hid.c:179:14: error: argument 1 null where non-null expected [-Werror=nonnull] 179 \| if (!strcmp(model, "1003")) { | ~~~~~~~~~~ In file included from fixreportdesctest-tripplite-hid.c:35: /usr/include/string.h:156:12: note: in a call to function ‘strcmp’ declared ‘nonnull’ 156 \| extern int strcmp (const char *s1, const char *s2) CC gpiotest-generic_gpio_liblocal.o \| ~~~ In function ‘tripplite_chemistry_fun’, inlined from ‘tripplite_chemistry_fun’ at ../drivers/tripplite-hid.c:171:20: ../drivers/tripplite-hid.c:184:14: error: argument 1 null where non-null expected [-Werror=nonnull] 184 \| if (!strcmp(model, "2005")) { | ~~~~~~~~~~ /usr/include/string.h: In function ‘tripplite_chemistry_fun’: /usr/include/string.h:156:12: note: in a call to function ‘strcmp’ declared ‘nonnull’ 156 \| extern int strcmp (const char *s1, const char *s2) | ~~\~ cc1: all warnings being treated as errors
  • NEWS.adoc: refer to issue and PR for tripplite-hid realpower mapping fix [#3581]
  • NEWS.adoc: refer to PR which updated apcmicrolink driver with USB HID support [#3579]
  • NEWS.adoc: refer to PR that added omron subdriver [#3554]

2.29. 2026-08-22 nmbro <nmbro@users.noreply.github.com>

  • drivers/apcmicrolink.c, m4/ax_c_pragmas.m4: fix(apcmicrolink): guard -Wswitch-default/-Wswitch-enum pragmas with feature checks Some compilers lack these specific pragmas even when other diagnostic pragmas are supported; add ax_c_pragmas.m4 detection and guard the unconditional uses in apcmicrolink.c accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

2.30. 2026-08-21 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/fixreportdesctest-tripplite-hid.c: fix compiler warnings about static-or-not variable [#3581]
  • tests/NIT/nit.sh: log failed test cases with same pattern as gmake errors, for easier search in build logs [#1711]

2.31. 2026-08-20 nmbro <nmbro@users.noreply.github.com>

  • .gitattributes, .gitignore, NEWS.adoc, data/driver.list.in, docs/man/apcmicrolink.txt, drivers/apcmicrolink.c, tools/nut-usbinfo.pl: refactor(apcmicrolink): use port=auto for USB mode instead of porttype=usb Replace non-standard porttype=usb with standard NUT convention port=auto for USB device selection. Matches nutdrv_qx and other USB drivers. Changes: - USB mode now triggered by port=auto or any USB matching option (vendorid, productid, vendor, product, serial, bus, device) - Removed porttype config option entirely - Updated all documentation and examples - Added proper switched outlet group counting - Minor time() call optimizations in updateinfo - Fixed nut-usbinfo.pl to recognize apcmicrolink-usb.c BREAKING CHANGE: porttype=usb no longer recognized. Use port=auto instead.
  • drivers/apcmicrolink-usb.c: fix(apcmicrolink): use usb_reset() abstraction instead of libusb_reset_device() Direct call to libusb_reset_device() breaks NUT_USB_VARIANT=0.1 builds. Use usb_reset() wrapper from usb-common.h which resolves to correct function per USB library version (libusb_reset_device on 1.0, usb_reset on 0.1).
  • NEWS.adoc, data/cmdvartab, data/driver.list.in, docs/man/apcmicrolink.txt, docs/nut-names.txt, docs/nut.dict, drivers/Makefile.am, drivers/apcmicrolink-usb.c, drivers/apcmicrolink-usb.h, drivers/apcmicrolink.c: feat(apcmicrolink): add USB HID transport with HID-PDC fallback Tunnel the Microlink protocol over USB HID interrupt IN/OUT for models with no serial port (porttype = usb). Tested against a single APC Smart-UPS 500 (SCL500RMI1UC); experimental even relative to the rest of this already-experimental driver. - Session startup on this hardware is intermittently unreliable and can take 90s\+ to respond; refusing to start on handshake failure was the top "driver won’t start" complaint. If the device also exposes standard HID Power Device usages, start on that fallback data (ups.status/battery.charge/battery.runtime) instead, and bring up outlet-group data/commands automatically once the Microlink tunnel connects, no restart needed. hid_fallback=no opts out. - USB read timeout raised to 1s (vs 100ms serial) and handshake retries raised to 10: this device’s interrupt pipe interleaves unrelated Input reports with Microlink replies, and a 100ms/3 retry budget starves the handshake before real data arrives. - Add load.cycle and per-outlet-group instant commands (outlet.group.N.load., outlet.group.N.shutdown.), and outlet.group.N.switchable to mark the unswitched main bank (group 0) as having no switching commands.

    • The kernel’s usbhid driver claims this device by default and NUT doesn’t detach it; document required udev rule in the man page.

2.32. 2026-08-20 Kyle Mason <masonkr@gmail.com>

  • tests/.gitignore, tests/Makefile.am, tests/fixreportdesctest-tripplite-hid.c: tests: cover the tripplite-hid Report Descriptor repair Add fixreportdesctest-tripplite-hid, modelled on the existing getexponenttest-belkin-hid, exercising tripplite_fix_report_desc() against synthetic descriptor items so the repair can be verified without the physical UPS attached. Covers the malformed ActivePower case proven on a SMART1500LCD, a conformant descriptor that must be left alone, a matching usage without the PDC power unit, the power unit on an unrelated usage, and disable_fix_report_desc. Three negative cases document the deliberate scope boundaries: ApparentPower and voltage usages are not repaired, and the repair never triggers on value magnitude alone. Note that get_unit_expo() and the HIDUnits table are both static to libhid.c and cannot be linked from a unit test without a live USB handle, so the test names the expected exponent as a constant and asserts the repair’s decision and resulting exponent rather than re-deriving libhid’s arithmetic.
  • NEWS.adoc, docs/nut.dict, drivers/tripplite-hid.c: drivers/tripplite-hid.c: recover output power reading, published as 0.0 Two defects combined to make ups.power read a constant 0.0 on the tested Tripp Lite SMART1500LCD (09ae:2012) under real load. First, the HID ActivePower usages were mapped to ups.power. docs/nut-names.txt defines ups.power as apparent power (VA) and ups.realpower as real power (W), so the subdriver published a watts reading under a VA variable name and never published ups.realpower at all. The cps-hid and mge-hid subdrivers already map ActivePower to ups.realpower. Second, that unit declares its ActivePower item with the HID PDC unit for watts/VA (0x0000D121) but leaves the Unit Exponent at 0, while the report already carries a plain integer in watts. NUT’s HIDUnits table normalizes that unit against an exponent of 7, so get_unit_expo() computes 0 - 7 and scales the value by 1e-7. A genuine 520 W reading arrives as raw 0x0208 and is published as 5.2e-05, which the "%.1f" format renders as a confident "0.0". Map both ActivePower usages to ups.realpower, map ups.power to the corresponding ApparentPower usages, add ups.realpower.nominal from UPS.Flow.ConfigActivePower, and add tripplite_fix_report_desc() to supply the omitted exponent. The descriptor repair is limited to the ActivePower item, matching on Unit == 0x0000D121 && UnitExp == 0. No Tripp Lite descriptor is known to expose an ApparentPower item, so repairing that usage would be speculative; it remains a mapping target only. A conformant descriptor is untouched, and disable_fix_report_desc is honored. Also add ApparentPower to docs/nut.dict, since the NEWS entry names it. Verified on a SMART1500LCD (09ae:2012): ups.realpower now reads 512.0 at ups.load 56, consistent with the unit’s 900 W rating.

2.33. 2026-08-20 Jun Kurihara <junkurihara@users.noreply.github.com>

  • NEWS.adoc, drivers/blazer_usb.c: blazer_usb: bound the remaining USB reply scans by the received length Same defect as the one just fixed in the corresponding nutdrv_qx transports, in the code those were forked from. cypress_command() and phoenix_command() fill the caller’s buffer in 8-byte chunks under "i ⇐ buflen - 8", so the last iteration can consume the final byte and leave no NUL, after which the "read: %.*s" debug line scanned on with an unbounded strcspn(). The precision now comes from memchr(buf, \r, i), falling back to i, the accumulated write offset both functions already return. Each block sits behind an explicit "nut_debug_level >= 3" check mirroring the upsdebugx() macro’s own guard, so the new scan too stays off the normal, non-debugging path. An embedded NUL still stops "%.*s" by itself, so a well-formed reply displays as before. That scan sits inside upsdebugx(), a macro guarded by nut_debug_level, so it was only reachable with debugging at level 3 or above, and neither function derives its return value from it. The loop conditions in both functions also scanned the raw receive buffer with strchr(buf, \r). Those become memchr(buf, \r, buflen), which is what the nutdrv_qx counterparts already do. This one is consistency rather than a fix: the condition short-circuits on "i ⇐ buflen - 8", so the unbounded strchr() was never reached with a completely full buffer. DRIVER_VERSION is left alone: it was already bumped for the Ippon fix in this same series. The NEWS entry is widened to name the transports actually covered. A harness includes this translation unit and calls both functions over mocked USB transfers, with the device returning bytes that contain neither CR nor NUL until the buffer is full; its debug stub formats for real, so an oversized precision would be caught too. Against the unpatched tree AddressSanitizer reports a heap-buffer-overflow read in strcspn() for both, at debug level 3 or above and not below it. Both pass after the change, as does the existing Ippon harness, the build with GCC at -Wall -Wextra -Wsign-compare -pedantic -Werror, make check and make spellcheck. No hardware was involved and I have no such unit, so this rests on review and the sanitizers. Coding helper tools were used here: OpenAI Codex and Anthropic Claude Code. The result was reviewed line by line.
  • NEWS.adoc, drivers/nutdrv_qx.c: nutdrv_qx: bound the remaining USB reply scans by the received length The Cypress, Phoenix, Fuji and Gtec transports fill the caller’s buffer in chunks, guarded by "i ⇐ buflen - chunk", so the final iteration can write its last byte and leave no NUL behind. The trailing scans then had nothing to stop them. Two cases, with different reach. fuji_command() returned strlen(buf) to its caller, which runs unconditionally, so a reply that filled the buffer with neither a CR nor a NUL both read past the end and reported a length derived from whatever followed. It now returns strnlen(buf, i), which keeps the padding semantics the comment describes (Fuji pads 8-byte chunks with 0x00) while it cannot run past what was received. strnlen() needs no new portability work here: configure.ac probes for it, common/strnlen.c is the fallback, and this file already uses it. The other case is the "read: %.*s" debug line in all four transports, whose precision came from an unbounded strcspn(). That sits inside upsdebugx(), which is a macro guarded by nut_debug_level, so it was only evaluated with debugging at level 3 or above, and the functions return the accumulated write offset rather than a string length, so nothing outside the debug path consumed the bad value. The precision now comes from memchr(buf, \r, i) and falls back to i, the same offset the function already returns. Each block sits behind an explicit "nut_debug_level >= 3" check mirroring the upsdebugx() macro’s own guard, so the new scan too stays off the normal, non-debugging path. An embedded NUL still stops "%.*s" by itself, so the text shown for a well-formed reply does not change. DRIVER_VERSION is left alone: it was already bumped for the Ippon fix in this same series. The NEWS entry is widened to name the transports actually covered. A harness per transport includes this translation unit and calls the real static function over mocked USB transfers, with the device returning bytes that contain neither CR nor NUL until the buffer is full. Its debug stub formats its arguments for real, so an oversized precision would be caught as well as the scan computing it. Against the unpatched tree, AddressSanitizer reports a heap-buffer-overflow read in every one: fuji at debug level 0, and cypress, phoenix and gtec only at level 3 or above, which matches the split above. All pass after the change, as do the existing Ippon harness, the build with GCC at -Wall -Wextra -Wsign-compare -pedantic -Werror, make check and make spellcheck. No hardware was involved and I have none of these units, so this rests on review and the sanitizers. Coding helper tools were used here: OpenAI Codex and Anthropic Claude Code. The result was reviewed line by line.

2.34. 2026-08-20 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: For Strict-C builds, skip old GCC on OpenBSD [#3577]
  • NEWS.adoc, clients/upsmon.c, common/parseconf.c, common/snprintf.c, common/state.c, common/str.c, configure.ac, docs/nut.dict, drivers/dstate.c, include/common.h, include/proto.h, tests/test_authconf.c, tools/nut-scanner/nut-scanner.c: configure.ac, *.c, *.h: for varargs consider both stdarg.h and sys/stdarg.h [#3577]

2.35. 2026-08-19 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upslog.c: only upscli_splitname() if we do not know the answer already
  • clients/upslog.c: free the monhost_ups_anchor based list items
  • clients/upslog.c: free contents of monhost_ups_anchor based list items
  • clients/upslog.c: just in case upscli_disconnect() even for asterisky cleanup
  • clients/upslog.c: address valgrind findings for flist and logtarget, and secondary logformat_allocated
  • drivers/dummy-ups.c: do not xstrdup() and forget the arg to upscli_upslog_setproctag() [#3379] The setproctag() behind it makes a copy already, so the callers may free() theirs if they like.
  • scripts/valgrind/.valgrind.supp.in: suppress another OpenSSL pattern
  • server/upsd.c: upsd_cleanup(): free more of what we allocated [#3329]
  • scripts/valgrind/valgrind.sh.in: speed up process startup (do not check/download debuginfo files) Thanks to https://bbs.archlinux.org/viewtopic.php?id=279297 for the hints.
  • server/upsd.c: client_disconnect(): avoid re-closing sock_fd twice
  • clients/upsclient.c: upscli_cleanup(), upscli_disconnect(): free more of what we allocated [#3329]
  • Makefile.am, tests/Makefile.am, tests/NIT/Makefile.am, tests/NIT/nit.sh: tests/NIT/nit.sh, */Makefile.am: add support for running make memcheck-NIT{-sandbox,}{-devel,} attempts
  • tests/Makefile.am: add aliases for check-NIT-sandbox{,-devel}
  • Makefile.am: introduce memcheck-quick shortcut
  • Makefile.am: rephrase pre-set DISTCHECK_VALGRIND_FLAGS
  • clients/authconf.c: handle_authconf_args(): be more diligent about xstrdup() cleanup [#3329]
  • docs/configure.txt: clarify inconsistency of NUT --with/--enable flags vs autotools semantics
  • tests/test_authconf.c: call upscli_cleanup() to pass memcheck [#3329]
  • clients/upsclient.c: upscli_cleanup(): track specifically nss_initialized to avoid teardown when we never initialized NSS (this causes the library to use some memory and fail memcheck)
  • ci_build.sh: default-all-errors: report "[Matrix] Error…" for other FAILED scenarios (configure, *clean)
  • Jenkinsfile-dynamatrix: in "Strict-C…" filter qw had strict C excluded - bring it back [#3567]
  • drivers/main.c: comment on poll_interval sleep location
  • clients/upsclient.c, clients/upslog.c, clients/upsstats.c, common/timegm_fallback.c, drivers/generic_gpio_libgpiod.c, drivers/nutdrv_qx_voltronic-axpert.c: */.c: fix comments to C89 block style [#3567] Pass at least GCC builds

2.36. 2026-08-18 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: rephrase dynacfgPipeline.excludeCombos_AUTOTOOLS_REQUIRE_TWEAK definition as a nested collection [#3567]
  • Jenkinsfile-dynamatrix: "Strict-C and GNU C89/C\+\+98 standard builds with fatal warnings (auto level) and GCC toolkits" filter does not actually select Strict-C - be more explicit about this in code/comments [#3567]
  • m4/nut_arg_with.m4, configure.ac, NEWS.adoc: revise NUT_ARG* methods to remember nut_withGiven_*/nut_enableGiven_* status of the option, and fix passing one default and one custom user/name value [#3513, #3140]

2.37. 2026-08-17 user01010111 <lapses.50.booster@icloud.com>

  • drivers/ecoflow-cdc-protocol.c, tests/ecoflow_cdc_protocol_utest.c: Decode EcoFlow telemetry floats portably

2.38. 2026-08-17 Costa Tsaousis <costa@netdata.cloud>

  • docs/nut.dict: add PR3000ELCDSL/SXL to the spellcheck dictionary The NEWS entry for the cps-hid logical-maximum fix references the CyberPower PR3000ELCDSL and PR3000ELCDSXL models; aspell flags ELCDSL/ELCDSXL as unknown words and fails the NUT spellcheck and distcheck stages on the PR CI. Add both model-name fragments to the accepted words list (alphabetically, next to ELCD).
  • NEWS.adoc, drivers/cps-hid.c, tests/getvaluetest.c: drivers/cps-hid.c: fix clipped input and battery voltage on CyberPower 0764:0601 PR3000ELCDSL/PR3000ELCDSXL and sibling models sharing USB ID 0764:0601 declare HID Logical Maximums below the values they transmit for UPS.Input.Voltage/ConfigVoltage (LogMax 70) and UPS.PowerSummary.Voltage/ConfigVoltage (LogMax 255). GetValue() masks with hibit(LogMax) and clamps to [LogMin..LogMax], so input.voltage is reported as 70 and battery.voltage as 22.4 instead of the 225 / \48 V values actually sent on the wire. Extend cps_fix_report_desc() to widen the under-declared maximums using the patterns already established for this device family: - input voltage: also trigger when the HVT LogMax is greater than the input voltage LogMax (oracle sub-form, as used in apc-hid.c), placed as a sibling of the output voltage fix under the HVT block - input config voltage: set LogMax to 255 when the HVT LogMax is greater (byte-sized item; mirrors apc-hid.c) - battery voltage / battery config voltage: raise to CPS_BATTVOLT_LOGMAX when declared below it (constant sub-form, as used for the nominal power fix) All new fix-ups skip items with an active physical scaling (logical_to_physical() would rescale their already-in-range values), stay gated to the existing CPS_VENDORID / 0x0501 / 0x0601 product list, and remain skippable via disable_fix_report_desc. Add getvaluetest cases locking the mask/clamp semantics the fix relies on (wire values 225/230/480 against declared LogMax 70/255 and the widened 511/255/4096). Tested on a PR3000ELCDSXL (0764:0601, firmware CR01901A5P1): before the change input.voltage 70 / battery.voltage 22.4; after 227 / 48.0, with output.voltage and input transfer limits unchanged. [issue #3089]

2.39. 2026-08-16 Jim Klimov <jimklimov+nut@gmail.com>

  • conf/upsmon.conf.sample.in, docs/man/upsmon.txt, docs/nut.dict: docs/man/upsmon.txt, conf/upsmon.conf.sample.in: document SUSPEND_TIMEJUMP_UNEXPECTED and update other suggestions [#3405]
  • drivers/apc_modbus.c: EMBBADSLAVE is not defined in older modbus.h releases [#3571]

2.40. 2026-08-15 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: move BUILD_TYPE requirements back from labels to env [#3567]

2.41. 2026-08-14 user01010111 <lapses.50.booster@icloud.com>

  • tests/Makefile.am: Keep EcoFlow test dependencies local

2.42. 2026-08-14 d01 <d01@users.noreply.github.com>

  • drivers/apc_modbus.c: apc_modbus: log a read failure once it is one, not once per attempt The retry loop from #3414 kept the pre-retry code’s LOG_ERR inside the new loop, so every attempt is logged as an error — including the ones the very next retry recovers, which is the exact case the loop was added to handle. On a device that needs retries routinely this floods the system log with errors the driver does not have. Measured on an APC Smart-UPS X1500 in a degraded state: \~310 LOG_ERR/min while the connection was never closed once and every read ultimately succeeded — about 890k lines/day and 42% of the journal, doubled again by upsdrvctl forwarding the driver’s stderr alongside its own syslog write. Log attempts with upsdebugx(1), and the error once, after the loop, where the read has really failed and the connection is about to be closed. Both lines carry the attempt count. Nothing is logged when exit_flag broke the loop: the driver is stopping, not failing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
  • drivers/apc_modbus.c: apc_modbus: retry reads whose reply belongs to another request The retry loop from #3414 retries only on ETIMEDOUT and gives up on every other error. That leaves out the one case where a retry is most obviously correct: a reply arrived, but it is not the reply to this request. libmodbus reports those as EMBBADSLAVE, EMBBADCRC or EMBBADDATA. On a packetised transport such as rtu_usb this is common, because a reply the device deferred past our timeout stays queued on the endpoint rather than ageing off the wire as it would on an idle serial line. The next read then collects the previous request’s answer. Reading it has consumed it, so the very next attempt can succeed — but the loop breaks out instead of making it. On an APC Smart-UPS X1500 this is exactly what happens on the first driver start after a host reboot (the self-powered UPS keeps its queue across the host’s power cycle): the initial read times out, the retry collects the stale reply, and the loop gives up, so startup fails with "Can’t read inventory information from the UPS". systemd then restarts the driver, and each failed start abandons further exchanges, degrading the device until only reseating the USB cable recovers it. Genuine Modbus exception responses still break out of the loop immediately: the device answering properly will not improve on a retry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
  • drivers/apc_modbus.c: apc_modbus: stop retrying reads once exit_flag is set The read retry loop added in #3414 does not check exit_flag, so a driver asked to stop keeps working through its remaining retries: up to retries * response_timeout, which with non-default settings comfortably exceeds the 5 seconds upsdrvctl allows for SIGTERM before it escalates to SIGKILL (drivers/upsdrvctl.c, the "retrying harder" path). On some hardware that escalation is not merely untidy: an APC Smart-UPS X1500 (051d:0003, FW "UPS 16.0") killed mid-exchange stops serving Modbus altogether — it accepts writes and answers nothing — and only physically reseating the USB cable recovers it. Every clean shutdown then wedges the UPS. Break out of the loop as soon as exit_flag is set. Verified on that hardware: shutdowns now log "Signal 15: exiting" and deactivate cleanly where they previously hit "Stopping … failed, retrying harder" followed by status=9/KILL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

2.43. 2026-08-14 Jim Klimov <jimklimov@gmail.com>

  • NEWS.adoc: update the note about snmp-ups voltronic subdriver [#3570]

2.44. 2026-08-14 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: introduce dynacfgPipeline.excludeCombos_AUTOTOOLS_NOT_REQUIRE_TWEAK and dedup "manpage docs builds" vs. "autotools builds" [#3567]
  • Jenkinsfile-dynamatrix: define excludeCombos_AUTOTOOLS_REQUIRE_TWEAK to simplify rule sets maintenance [#3567]
  • Jenkinsfile-dynamatrix: comment on "all/manpage docs types" vs. "autotools-driven builds" [#3567]
  • Jenkinsfile-dynamatrix: update labels (non-docs), comment on dedup potential of "all driver types on capable systems" vs. "combos with Python versions for helper scripts" [#3567]
  • Jenkinsfile-dynamatrix: run "Various GNU C99+/C++11+ standard builds with fatal warnings (auto level), without distcheck and docs" scenario also out-of-dir [#3567]
  • Jenkinsfile-dynamatrix: dedup: "Various non-docs non-distchecked target builds with main and \~newest supported C/C+\+ revisions" (default-nodoc) is nowadays a subset of several default-all-errors scenarios [#3567]
  • Jenkinsfile-dynamatrix: dedup: limit "Various GNU C99+/C++11+ standard builds with fatal warnings (auto level), without distcheck and docs" scenario to non-GCC toolkits [#3567] They ran essentially same workload twice, as "Various GNU C99+/C++98+ standard out-of-tree builds with fatal warnings (hard level) with GCC"
  • ci_build.sh: comment about DISTCHECK_TGT="distcheck-ci" in BUILD_TYPE="default-all-errors"* handler [#3567] Defined just in case, but currently not used (DAE does not do distcheck now).
  • Jenkinsfile-dynamatrix: annotate build scenarios currently used for master branch and common PR builds [#3567]
  • NEWS.adoc: document the three fixes for apc_modbus [#3571]
  • drivers/apc_modbus.c: bump DRIVER_VERSION, small style fixes [#3571]
  • docs/nut.dict: docs/nut.,dict: add PSCERT entry [#3570]

2.45. 2026-08-13 user01010111 <lapses.50.booster@icloud.com>

  • drivers/Makefile.am, drivers/apc-hid.c, drivers/arduino-hid.c, drivers/belkin-hid.c, drivers/cps-hid.c, drivers/delta_ups-hid.c, drivers/ecoflow-cdc.h, drivers/{ecoflow-cdc.c ⇒ ecoflow-hid-aux-cdc.c}, drivers/ecoflow-hid-aux-cdc.h, drivers/ecoflow-hid.c, drivers/ever-hid.c, drivers/explore-hid.c, drivers/idowell-hid.c, drivers/legrand-hid.c, drivers/liebert-hid.c, drivers/mge-hid.c, drivers/openups-hid.c, drivers/powercom-hid.c, drivers/powervar-hid.c, drivers/salicru-hid.c, drivers/tripplite-hid.c, drivers/usbhid-ups.c, tests/ecoflow_cdc_protocol_utest.c: Address HID auxiliary review feedback
  • drivers/Makefile.am: Fix Windows static link order for EcoFlow CDC
  • docs/nut.dict: Accept EcoFlow option name in documentation spellcheck
  • drivers/Makefile.am: Include EcoFlow CDC headers in source tarballs
  • docs/man/usbhid-ups.txt, drivers/Makefile.am, drivers/apc-hid.c, drivers/arduino-hid.c, drivers/belkin-hid.c, drivers/cps-hid.c, drivers/delta_ups-hid.c, drivers/ecoflow-cdc-protocol.c, drivers/ecoflow-cdc-protocol.h, drivers/ecoflow-cdc.c, drivers/ecoflow-cdc.h, drivers/ecoflow-hid.c, drivers/ever-hid.c, drivers/explore-hid.c, drivers/idowell-hid.c, drivers/legrand-hid.c, drivers/liebert-hid.c, drivers/mge-hid.c, drivers/openups-hid.c, drivers/powercom-hid.c, drivers/powervar-hid.c, drivers/salicru-hid.c, drivers/tripplite-hid.c, drivers/usbhid-ups.c, drivers/usbhid-ups.h, tests/Makefile.am, tests/ecoflow_cdc_protocol_utest.c: Add optional EcoFlow CDC telemetry to usbhid-ups
  • NEWS.adoc, drivers/libusb0.c, drivers/libusb1.c: Prefer complete HID descriptor for EcoFlow devices

2.46. 2026-08-13 Jun Kurihara <junkurihara@users.noreply.github.com>

  • NEWS.adoc, drivers/blazer_usb.c: blazer_usb: bound Ippon USB reply parsing by the number of bytes read The legacy Ippon transport carries the same defect just fixed in the nutdrv_qx Ippon subdriver, which was forked from this code. It reads up to 64 bytes into a 64-byte stack buffer with usb_interrupt_read() and then found the end of the reply with strcspn(), unbounded by the transfer length, so a reply filling the buffer without a CR or a NUL scanned past the end of the array. The length that came back drove snprintf("%.*s", len, tmp) into the caller’s buffer, so bytes adjacent on the stack could be handed back as reply data. The driver is obsoleted, but it is still built and shipped. The fix is not a copy of the nutdrv_qx one, because the two functions do not agree on where a reply ends. strcspn(tmp, "\r") stops at whichever of CR or NUL comes first, which is why the Ippon empty-reply frame 0x00 0x0D yields 0 here while nutdrv_qx yields 2. The bounded replacement therefore takes the earlier of memchr(tmp, \r, ret) and memchr(tmp, \0, ret), and falls back to ret when the reply contains neither. The following "\+1 to include the terminator" step is kept, but skipped when no terminator was received: there is nothing to count in that case, and incrementing would hand snprintf() a precision of 65 against a 64-byte array holding no NUL, reintroducing the overread it is meant to remove. Behavior is otherwise unchanged, including two quirks left alone deliberately rather than corrected inside this change. The returned length counts a terminating NUL that snprintf() did not copy, and it is not clamped when the destination truncates. blazer.c only compares that value against thresholds and treats the buffer as a C string, so neither is harmful. DRIVER_VERSION is bumped to 0.27 and NEWS.adoc records the change. No Ippon hardware was available, so this rests on review and on a local harness that calls the real ippon_command() over mocked USB transfers. The harness pinned the four behaviors above against the unpatched tree first, and they are unchanged after the fix. Under AddressSanitizer the old code reports a stack-buffer-overflow read of 65 bytes at the strcspn() call with nut_debug_level at 0, confirming the overread is not confined to a debug path; the patched code is clean at debug levels 0 and 5. Built warning-free with GCC at -Wall -Wextra -Wsign-compare -pedantic -Werror. Coding helper tools were used here: OpenAI Codex and Anthropic Claude Code. The result was reviewed line by line.
  • NEWS.adoc, drivers/nutdrv_qx.c: nutdrv_qx: bound Ippon USB reply parsing by the number of bytes read ippon_command() reads up to 64 bytes into a 64-byte stack buffer with usb_interrupt_read(). The end of the reply was then located with strlen() and strcspn(), neither of which is bounded by the transfer length, so a reply filling the buffer without containing a CR or a NUL made both scan past the end of the array. The resulting length was not merely logged: it reached the memcpy() into the caller’s buffer and the returned length, so bytes adjacent to the buffer on the stack could be handed back as reply data. Take the count returned by usb_interrupt_read() as the authoritative extent instead. A CR within those bytes ends the reply as before, CR included. Failing that, a NUL within them ends it, which is the bounded equivalent of the old strlen() fallback. Failing both, every received byte is payload. Hex logging, text logging and the copy all use that bounded length, and the text log strips a trailing CR with an explicit length check rather than strcspn(). Behavior is otherwise unchanged: truncation to buflen - 1, NUL termination of the destination, and appending a missing terminating CR when there is room. One consequence is intended. When the device replied with fewer bytes than the command just written from the same buffer, the old strlen() fallback could run into the leftover command bytes and count them as reply data; the bounded scan cannot. DRIVER_VERSION is bumped to 0.55 and NEWS.adoc records the change. Coding helper tools were used here: OpenAI Codex and Anthropic Claude Code. The result was reviewed line by line, built warning-free with GCC at -Wall -Wextra -Wsign-compare -pedantic -Werror, and exercised with a local harness that calls the real ippon_command() over mocked USB transfers under AddressSanitizer and UndefinedBehaviorSanitizer.

2.47. 2026-08-13 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/developers.txt: revise sections on indentation, line breaks, etc. [#3569]
  • Jenkinsfile-dynamatrix: make slowBuild definitions more similar - move BUILD_TYPE into dynamatrixAxesVirtualLabelsMap everywhere [#3567]
  • NEWS.adoc, docs/nut.dict, drivers/snmp-ups.c: NEWS.adoc, drivers/snmp-ups.c: announce voltronic MIB subdriver, bump snmp-ups version [#3570]

2.48. 2026-08-13 Chris Allen <203867+chrisallen@users.noreply.github.com>

  • data/driver.list.in, docs/man/snmp-ups.txt, docs/nut.dict, drivers/Makefile.am, drivers/snmp-ups.c, drivers/voltronic-mib.c, drivers/voltronic-mib.h: drivers/snmp-ups: add "voltronic" subdriver for enterprise 43943 UPSes Add support for Voltronic enterprise 43943 SNMP cards, tested with a PowerShield Centurion RT 1000VA and PSSNMPV4 card. The private MIB gives better resolution for most readings, so use it first and keep RFC1628 as a fallback. Direct GETs showed that the private line tables use index 0 while the RFC1628 tables use index 1, despite the MIB declarations not making that difference clear. Map identity, topology, status, battery data, input, output, bypass, ratings, load and measured real power. Publish the canonical aggregate power names alongside the useful output aliases. The card returns zero for apparent power, power factor and the generic transfer limits even when the UPS is carrying load. Leave those values out instead of presenting them as measurements or trying to calculate them from load. Leave the optional PSEMD branch out as well because no connected sensor was available to verify it. Delayed shutdown, startup and beeper controls follow the vendor MIB and firmware definitions. They have not been tested on live hardware, and no control command was sent during development. Add the subdriver to the build, document its limits, and add the tested PowerShield model to the compatibility list.

2.49. 2026-08-12 Yuriy_Y <alliancesko@gmail.com>

  • NEWS.adoc, data/driver.list.in, docs/man/nutdrv_qx.txt, drivers/nutdrv_qx.c: nutdrv_qx: add richcomm-svc USB transport [#3562] Add an explicit USB communication subdriver for older session-initialized Richcomm USB-to-UART bridges. The tested SVC V-1000F uses the existing Megatec protocol and Armac command framing, but requires a 2400-baud report and a 64-byte session-opening sequence before it accepts commands. Ignore leading empty HID reports from this bridge and reset its transport state after USB reconnection. Use the 6-byte reads required by the V-1000F without changing the 8-byte reads used by normal armac devices. Keep the mode opt-in because 0925:1234 is shared by devices with different communication behavior. Document the mandatory configuration, tested hardware and untested command scope in the man page, HCL and NEWS. Development and documentation were assisted by OpenAI Codex. The submitter reviewed the changes and verified the hardware behavior.

2.50. 2026-08-12 Falusi Tamás <tamas.falusi@gmail.com>

  • NEWS.adoc, docs/nut.dict, drivers/apc-hid.c: apc-hid: map shutdown.return for APC Back-UPS BX*MI units [#2683] On newer APC Back-UPS devices (e.g. Back-UPS BX750MI, 051d:0002) the only shutdown-capable HID usage is UPS.APCGeneralCollection.APCDelayBeforeReboot. The previously only matching command mapped to it, shutdown.reboot, writes the default value 10, which this firmware accepts (USB-level success) but never executes — so the built-in shutdown sequence reported success without ever powering the load off. Map shutdown.return to the same usage with value 1, matching the write APC PowerChute Serial Shutdown performs (confirmed by decoding the USBPcap captures archived in issue #2683, and live on a 2025-11 BX750MI-GR unit): the load is cycled after a fixed \~2 minute grace (the value is not a seconds count on this firmware), the command executes only while on battery, and output returns when wall power is present, even if AC came back during the grace period. With this mapping the driver-default shutdown command chain (shutdown.return,shutdown.reboot,load.off.delay,shutdown.stayoff) succeeds on its first command: verified on battery with usbhid-ups -k on a BX750MI-GR — output cut 2 min 2 s after the write and re-energized on AC restore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQEAfDBxtHupEdAcv3Bgz2

2.51. 2026-08-11 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/nut.dict: Update with new entries [#3563]
  • drivers/nutdrv_qx.c: upsdrv_initups(): define val for all code paths [#3563]
  • docs/man/nutdrv_qx.txt: document cypress_drain_quirk setting [#3563]
  • UPGRADING.adoc: nutdrv_qx added cypress_0665_5161_subdriver method [#3563] Clarified impact of cypress/cypress_0665_5161_subdriver subdriver fix on deployments and provided workaround instructions.
  • NEWS.adoc: nutdrv_qx added cypress_0665_5161_subdriver method [#3563] Clarified impact of cypress/cypress_0665_5161_subdriver subdriver fix on deployments and provided workaround instructions.
  • drivers/nutdrv_qx.c: bump version, let cypress_drain_quirk be configured via ups.conf [#3563]

2.52. 2026-08-10 pixi-p <pixip@o2.pl>

  • data/driver.list.in, drivers/nutdrv_qx.c: nutdrv_qx: fix Cypress 0665:5161 replies Drain stale interrupt reports before issuing a command for the Cypress 0665:5161 transport, and use a longer reply timeout for this device quirk. This allows Voltronic-QS autodetection for a VOLT Polska RackUPS 1200VA/720W. Related to #2534.

2.53. 2026-08-09 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsc.c, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsrw.c, clients/upsset.c, clients/upsstats.c: Honour per-authconf flags_ssl (do not get stuck on default) [#3331]
  • clients/upsclient.c: upscli_init2(): avoid potential leak if re-entered [#3331]
  • server/upsd.c: log whether incoming data from a CLIENT is in an encrypted or plaintext session [#3331]
  • docs/Makefile.am: SOURCE_CHECK_UNICODE: avoid printing useless SRCDIR="." in some locations
  • clients/authconf.c, clients/authconf.h, clients/upsc.c, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsrw.c, clients/upsset.c, clients/upsstats.c, docs/man/upscli_read_authconf_file.txt, drivers/dummy-ups.c, tests/test_authconf.c, tools/nut-scanner/scan_nut.c: clients/*.c et al: upscli_read_authconf_file(): add an argument to hide best-effort nutauth.conf discovery failure into debug log [#3329]

2.54. 2026-08-06 Jim Klimov <jimklimov+nut@gmail.com>

  • README.adoc: switch to star-history.dera.page fork for GitHub stars graph See https://github.com/star-history/star-history/issues/539
  • Jenkinsfile-dynamatrix: update different group names to highlight what they do now, update comments to mark up what can be optimized
  • Jenkinsfile-dynamatrix: revise excludeCombos stacking style to minimize diffs in comparisons
  • Jenkinsfile-dynamatrix: update comment about excluding NetBSD from default autotools builds
  • Jenkinsfile-dynamatrix: update autotools group names to highlight that they run distcheck-ci too
  • Jenkinsfile-dynamatrix: silence default MAKE_OPTS for dynacfgPipeline.buildPhases[distcheck] (autotools)
  • Jenkinsfile-dynamatrix: add *.adoc do dynacfgPipeline.appliesToChangedFilesRegex_FILES_TXT

2.55. 2026-08-05 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, docs/FAQ.txt, docs/Makefile.am, docs/developers.txt, docs/man/upsd.conf.txt, docs/security.txt, scripts/Windows/README.adoc: docs/Makefile.am, *.txt, *.adoc: Revise asciidoc markup around monospaced words Various parsers have trouble with closing backtick followed by a character which is not a word separator (dash seems OK, an apostrophe precludes closing the monospace context). Added a spellcheck-like rule to avoid regressions about this, and fixed earlier reporting in that docs/Makefile.am goal.

2.56. 2026-08-04 Marcelo Pacheco <marcello.mpacheco@gmail.com>

  • NEWS.adoc, common/wincompat.c: win32: fix short reads on serial timeout

2.57. 2026-08-04 Jun Kurihara <junkurihara@users.noreply.github.com>

  • drivers/nutdrv_qx.c: fix C+\+ compatibility in OMRON transport Cast memchr() results explicitly to char * so C+\+ compatibility checks do not reject the implicit conversion from void *.

2.58. 2026-08-04 Jim Klimov <jimklimov+nut@gmail.com>

2.59. 2026-08-03 mkiera <itskiera20@gmail.com>

  • NEWS.adoc: note the GoldenMate LiFePO4 Logical Maximum fix [PR #3555]
  • drivers/idowell-hid.c: idowell-hid: fix bogus battery.runtime/battery.voltage on GoldenMate LiFePO4 GoldenMate 1000VA/800W LiFePO4 packs (06da:ffff, and the same firmware on 075d:0300) declare their data twice: as Feature items in ReportID 0x01 with sane limits, and again as Input items in ReportID 0x02. In ReportID 0x02 the firmware emits the 4-byte Logical Maximum for UPS.PowerSummary.RunTimeToEmpty byte-reversed, as "27 ff ff ff fe" (0xFEFFFFFF) where 0xFFFFFFFE was intended: 09 68 Usage (RunTimeToEmpty) 75 20 Report Size (32) 15 00 Logical Minimum (0) 27 ff ff ff fe Logical Maximum (0xFEFFFFFF = 4278190079) 81 83 Input 0xFEFFFFFF does not fit a signed 32-bit long, so on LLP64 platforms (Windows, where long is 32-bit) the generic "LogMax < LogMin" recovery in HIDParse() stores it back via "(long) pParser→Value" and it wraps to -16777217. Logical values persist in HID global state, and the UPS.PowerSummary.Voltage Input item that follows declares no Logical Maximum of its own, so it inherits the same broken limit: 05 84 09 30 Usage (Voltage) 67 21 d1 f0 00 Unit 55 05 Unit Exponent (5) 75 10 Report Size (16) 81 82 Input ← no new Logical Maximum Both readings then come out as nonsense (battery.runtime = -16777217, battery.voltage = -167772.2) while the ReportID 0x01 Feature copies of the very same usages read correctly (1740 and 13.2). LP64 platforms are unaffected, since 4278190079 fits a 64-bit long there. Add idowell_fix_report_desc() to repair the two ReportID 0x02 items. Rather than invent limits, use the values this device itself declares for the same usages in ReportID 0x01: 0x75FFFFFF for RunTimeToEmpty (32-bit) and 65535 for Voltage (16-bit). Only items whose maximum is demonstrably broken (below the minimum) are touched, so a firmware revision that encodes this correctly is left alone, and the user’s disable_fix_report_desc toggle is honored.

2.60. 2026-08-03 Jun Kurihara <junkurihara@users.noreply.github.com>

  • docs/man/nutdrv_qx.txt, drivers/nutdrv_qx.c: nutdrv_qx: clarify OMRON USB transport details [#3112] Describe the subdriver option as selecting a USB communication subdriver so it also covers the OMRON HID transport. Document that OMRON’s non-standard SET_REPORT value 0x0002 is intentional, matches both vendor driver releases, and was verified on the BN150T. This changes only the literal’s presentation, not its value or behavior.
  • docs/nut.dict: docs: update spelling dictionary for OMRON documentation [#3112] Add DSM, OMRON’s, QNAP and Synology to the spelling dictionary for terms introduced by the OMRON documentation.
  • NEWS.adoc, docs/man/nutdrv_qx.txt, drivers/nutdrv_qx.c, drivers/nutdrv_qx_omron.c: docs: refine OMRON documentation and comments [#3112] Improve the wording of the OMRON NEWS entry, manual section and source comments. Clarify the tested scope, protocol behavior and source provenance while keeping the private report details out of the public documentation. No driver logic is changed.
  • NEWS.adoc, data/driver.list.in, docs/man/nutdrv_qx.txt, drivers/nutdrv_qx_omron.c: docs: record OMRON BN150T hardware verification [#3112]
  • NEWS.adoc, data/driver.list.in, docs/man/nutdrv_qx.txt, drivers/nutdrv_qx_omron.c: docs: clarify provenance of OMRON GPL driver sources [#3112] Identify the OMRON-authored driver versions as 1.00 from the Synology DSM 7.3-86009 GPL sources and 1.02 from the QNAP QTS 5.2.3 GPL sources. Replace ambiguous references to vendor releases consistently across the manual, NEWS, driver list and source comments.

2.61. 2026-07-31 Jun Kurihara <junkurihara@users.noreply.github.com>

  • NEWS.adoc, docs/man/nutdrv_qx.txt, drivers/nutdrv_qx.c, drivers/nutdrv_qx_omron.c: nutdrv_qx: add OMRON protocol extensions Add BN150T extension queries, OMRON-specific status handling, and shutdown command encoding and acknowledgement handling. Prefer the vendor-ID-gated OMRON protocol during USB autodetection, and document the tested scope and known limitations.
  • NEWS.adoc, data/driver.list.in, docs/man/nutdrv_qx.txt, docs/nut.dict, docs/nutdrv_qx-subdrivers.txt, drivers/nutdrv_qx.c: docs: document the "omron" protocol and USB subdriver [#3112] Adds the OMRON BN150T to the compatibility list at support level 4, the protocol to the nutdrv_qx man page, the subdriver source files to the developer guide’s list, and the two words the spell checker needs. The man page states what was actually exercised: the polling path on one model over USB. None of the shutdown commands has been sent to real hardware, so the section says so rather than implying coverage. ondelay is accepted for this protocol but does not reach the device: OMRON’s shutdown commands carry no return-delay field.
  • drivers/Makefile.am, drivers/nutdrv_qx.c, drivers/nutdrv_qx_omron.c, drivers/nutdrv_qx_omron.h: drivers/nutdrv_qx: add "omron" protocol subdriver for OMRON BN150T [#3112] The BN150T reports the third Q1 status bit as 1 continuously while on mains, idle and unloaded, which "q1" turns into BYPASS - in NUT convention a state where protection has been bypassed. OMRON’s own driver does not evaluate that bit at all: the block deciding between TRIM, BYPASS and BOOST is commented out in its entirety, identically in the 1.00 and 1.02 vendor releases, while every other status bit is handled. The row is dropped here rather than remapped, since what the bit reports on this hardware is not known. Instant commands the vendor driver does not implement are not registered: test.battery.start, .deep, .quick and .stop have no T or TL command anywhere in either vendor tree, the vendor’s instant command set has no whole-load on/off behind load.on and load.off, and beeper.toggle has no bare Q. Registering an instant command tells clients the device can perform it. The shutdown commands use OMRON’s byte strings. The vendor sends "S<n>" and "Sf<n>" with the delay in tenths of a minute below one minute and in whole minutes above, and never appends the Megatec "R<mmmm>" return-delay field; blazer_process_command() appends it to both as soon as ups.delay.start is non-zero, and spells stay-off as "S<n>R0000" where OMRON has a distinct "Sf". shutdown.return and shutdown.stayoff are also two transactions rather than one: the auto-restart flag is set with "An" or "Af" first, as both the vendor driver and docs/nut-names.txt require, and a failure there abandons the command instead of powering the unit down with its restart behaviour unknown. A command the UPS accepts is acknowledged with "OK", not the "ACK" that the Megatec-derived subdrivers expect. The claim is blazer_claim_light() gated on OMRON’s USB vendor ID. Without the gate this subdriver, which checks no more than "q1" does, would claim every device the generic q1 fallback exists to serve. A serial-attached unit therefore needs protocol=omron set explicitly. The USB transport handler added earlier is renamed omron_usb_subdriver(), because the subdriver_t structure takes the plain name; same treatment as fabula_hunnox_subdriver(). Verified on 0590:00b7: polling reports OL without the spurious BYPASS. No instant command has been sent to the hardware, so every command string here rests on the vendor source alone. Other OMRON models untested.

2.62. 2026-07-30 Jun Kurihara <junkurihara@users.noreply.github.com>

  • drivers/nutdrv_qx.c: register OMRON BN150T (0590:00b7) for the "omron" subdriver [#3112] nut-usbinfo.pl generates the udev, hotplug and devd rules from the driver device tables, so without a qx_usb_id[] row a packaged install grants the device no permissions at all. The row needs a matching omron_subdriver(): qx_is_usb_device_supported() only reports SUPPORTED once a handler has assigned subdriver_command. Only the tested model is listed; the other OMRON IDs shipped by the vendor driver are untested.
  • drivers/nutdrv_qx.c: add "omron" USB subdriver for OMRON BN150T [#3112] Same transport as "ippon", except the control transfer carries a 16-byte HID Output report, the size declared by the BN150T’s report descriptor. The send loop advances by the report size and rejects a short transfer, and the reply length and both debug prints are bounded by the number of bytes actually read. Verified on 0590:00b7 with protocol=q1. Other OMRON models untested.

2.63. 2026-07-30 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: relocate USB rdlen fix notice to "fallout from earlier development" section and detail a bit more [#3550]

2.64. 2026-07-28 John Grant <john.grant@dronedeploy.com>

  • NEWS.adoc: note the report descriptor length fix [PR #3550]
  • drivers/libusb0.c, drivers/libusb1.c: drivers/libusb{0,1}.c: bound rdlens loop by element count, not sizeof [#3136] nut_libusb_open() iterates the two candidate HID report descriptor lengths: int32_t rdlen1, rdlen2, rdlens[2]; size_t j; … for (j = 0; j < sizeof(rdlens); j+\+) sizeof(rdlens) is 8 (bytes), not 2 (elements), so the loop reads rdlens[2..7] - 24 bytes past the end of the array - and uses that stack garbage as rdlen. The "ran out of candidates" check after the loop has the same bug. The out-of-range indices are only reached when neither real candidate satisfies the caller, i.e. exactly when a device is in a bad state and both descriptor reads fail. Garbage that happens to pass the rdlen sanity checks is then handed to libusb_control_transfer() and on to the HID parser callback, which is where it segfaults. Seen with usbhid-ups against an EcoFlow DELTA 3 Plus (3746:ffff) on an NVIDIA Jetson Orin Nano whose USB device had wedged; "usbreset" on the device cleared the wedge and hid the crash. Use SIZEOF_ARRAY() from common.h, which both files already include. libusb0.c carries the identical defect and is fixed the same way. Regression from aba6f43544b17d2f16e69ca19f162c6c19748fc7 ("if the tried rdlen did not succeed, fall back to the other value we had in mind"), first released in v2.8.5.

2.65. 2026-07-28 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, docs/nut.dict, tools/nut-scanner/scan_eaton_serial.c, tools/nut-scanner/scan_ipmi.c, tools/nut-scanner/scan_nut.c, tools/nut-scanner/scan_snmp.c, tools/nut-scanner/scan_xml_http.c: tools/nut-scanner/scan_*.c, NEWS.adoc, docs/nut.dict: fix mutex release in failure code path [#3551]
  • clients/upssched.c: conf_arg(): do not hard-code use of \\.\pipe\upssched, only fall back to it [#3525]
  • common/common.c, include/common.h, clients/upssched.c, drivers/dstate.c, NEWS.adoc: introduce upsdebug_ascii_compact() for better readability [#3525]
  • drivers/dstate.c: send_to_one()/send_to_all(): report sent buffer content via upsdebug_ascii() to avoid confusing trailing empty line for \n [#3525]
  • clients/upssched.c: send_to_one(): report sent buffer content via upsdebug_ascii() to avoid confusing trailing empty line for \n [#3525]

2.66. 2026-07-27 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/config-prereqs.txt: update for FreeBSD 15 fresh install
  • drivers/apcsmart.c, drivers/belkin.c, drivers/bestfortress.c, drivers/genericups.c, drivers/nhs_ser.c, drivers/powerpanel.c, drivers/powervar_cx_usb.c, drivers/tripplite.c, drivers/tripplite_usb.c, drivers/usbhid-ups.c: drivers/*.c: remove useless custom logging, hide useful messages behind may_log_reconnect_trying() [#3541]
  • drivers/apcsmart.c: revise tracking of serial port file descriptor [#3541]
  • drivers/main.c, drivers/main.h: drivers/main.{c,h}: introduce may_log_reconnect_trying() for drivers that retain custom logging of reconnection details for now [#3541]
  • drivers/main.c, conf/ups.conf.sample, docs/man/ups.conf.txt: Introduce reconnect_report_freq to manage verbosity of on-going reconnect_trying() streak [#3541]
  • drivers/tripplite_usb.c, UPGRADING.adoc: remove old bespoke reconnection limit tracking [#3541]
  • drivers/tripplite.c, NEWS.adoc: retry failed connections [#3541]
  • NEWS.adoc: list drivers impacted by reconnect_max_tries in one place [#3541]
  • drivers/powervar_cx_usb.c, UPGRADING.adoc: remove old bespoke reconnection limit tracking [#3541]
  • drivers/powerpanel.c, NEWS.adoc: retry failed connections [#3541]
  • drivers/genericups.c: handle valid/invalid upsfd like in bestfortress.c [#3541]
  • drivers/genericups.c, NEWS.adoc: retry failed connections [#3541]
  • drivers/belkin.c: handle valid/invalid upsfd like in bestfortress.c [#3541]
  • drivers/bestfortress.c, NEWS.adoc: retry failed connections [#3541]
  • NEWS.adoc: fix indentation style
  • drivers/belkin.c, NEWS.adoc: retry failed connections [#3541]
  • clients/upssched.c: update comment about HANDLE passing [#3525]
  • clients/upssched.c, NEWS.adoc: rewrite WIN32 timer daemon spawning code path [#3525] Co-authored-by: @alexdruqn-cpu Co-authored-by: Claude Sonnet 4.6
  • NEWS.adoc: fix indentation typo [#3546]

2.67. 2026-07-25 José San Juan <josesj@gmail.com>

  • NEWS.adoc, drivers/riello_ser.c, drivers/riello_usb.c: riello_usb, riello_ser: report NAK on RE as failure

2.68. 2026-07-24 Lucas Willian Bocchi <lucas@lucas.inf.br>

  • NEWS.adoc, docs/man/nhs_ser.txt: Document NHS serial configuration changes

2.69. 2026-07-24 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, drivers/adelsystem_cbi.c, drivers/apc_modbus.c, drivers/apcsmart.c, drivers/bcmxcp_usb.c, drivers/blazer_usb.c, drivers/generic_modbus.c, drivers/nhs_ser.c, drivers/nutdrv_qx.c, drivers/powerman-pdu.c, drivers/powervar_cx_usb.c, drivers/richcomm_usb.c, drivers/riello_usb.c, drivers/tripplite_usb.c, drivers/usbhid-ups.c: drivers/*.c, NEWS.adoc: update drivers to use reconnect_trying() with enum arg values for consistent reporting [#3541]
  • drivers/main.c: reconnect_trying(): fix syslog facility usage [#3541]
  • drivers/main.c, drivers/main.h: drivers/main.{c,h}: reconnect_trying(): refactor to use reconnect_state_t as enum with symbolic names instead of magic numbers [#3541]
  • drivers/main.c, drivers/main.h: drivers/main.{c,h}: accept reconnect_trying(2) to consistently process "reconnect.updateinfo" [#3541]
  • drivers/nhs_ser.c: use set_exit_flag() with symbolic EF_EXIT_* values [#2670]
  • drivers/main.c, drivers/dstate.c: use set_exit_flag() and symbolic EF_EXIT_* values instead of exit_flag=NUM [#2670] Also compare symbolic not numeric values when deciding on exit code.
  • drivers/main.c: set_exit_flag(): use common EF_EXIT_FAILURE and EF_EXIT_SUCCESS symbolic values to log debug traces [#2670, #1923]
  • drivers/main.c: set_exit_flag(): log debug traces based on new value, not old [#1923]

2.70. 2026-07-23 Lucas Bocchi <lucas.bocchii@gmail.com>

  • drivers/nhs_ser.c: Modernize NHS serial communication and add configurable port settings

2.71. 2026-07-23 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, conf/ups.conf.sample, docs/man/ups.conf.txt, drivers/main.c, drivers/main.h: drivers/main.{c,h}, docs/man/ups.conf.txt, conf/ups.conf.sample, NEWS.adoc: introduce reconnect_max_tries setting for NUT drivers [#3541]
  • clients/nutclient.cpp, scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: clients/upsclient.c, clients/nutclient.cpp, scripts/python/module/PyNUT.py.in, scripts/perl/UPS/Nut.pm.in: be sure to normalize authconf sections with various spellings for user, host and port in the same manner [#3329]
  • tests/test_authconf.c: verify that empty hostname is normalized to localhost [#3329]
  • scripts/python/module/PyNUT.py.in: findAuthConf(): add optional debug tracing [#3329]
  • clients/upsclient.c, clients/nutclient.cpp, scripts/python/module/PyNUT.py.in, scripts/perl/UPS/Nut.pm.in: be consistent about empty hostname in [admin@:12345] being treated as localhost [#3329]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: add support for NUT_DEBUG_LEVEL envvar

2.72. 2026-07-22 dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • .github/workflows/08-PyNUTClient.yml: build(deps): bump actions/setup-python from 6 to 7 Bumps actions/setup-python from 6 to 7. - Release notes - Commits --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: 7 dependency-type: direct:production update-type: version-update:semver-major …

2.73. 2026-07-22 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: when username gets manually edited, update auto-suggested password accordingly [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in: fix ups_port_entry handler to not block TAB through the field [#3514]

2.74. 2026-07-21 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: use the more idiomatic approach to focus order chains [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in: explicitly set initial focus to host field [#3514] It was like that before, but leave nothing to chance :)
  • scripts/python/app/NUT-Monitor-py2gtk2.in: __check_gui_fields(): get port value more reliably [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in: update authconf suggestions as the port number is being typed [#3514]
  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py3qt{5,6}.in: set initial focus to host field [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: call self.__check_gui_fields() when ups_port_entry is edited [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: removed the empty gui_status_message() call from __check_gui_fields [#3514] It was causing relevant messages (like mismatches or discovery results) to disappear instantly.
  • scripts/python/app/NUT-Monitor-py2gtk2.in: self.refresh_gui() immediately after updating the status message [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: clear the "Use auth" checkbox if no longer suggesting from authconf [#3514]
  • drivers/upsdrvctl.c: help(): clarify wording for list <ups> operation [#2567]
  • drivers/upsdrvctl.c, NEWS.adoc: start_driver(): we can re-check started instance also for single device runs [#3302]
  • drivers/upsdrvctl.c: start_driver(), forkexec(): pepper with debug traces [#3302]

2.75. 2026-07-20 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in, scripts/python/app/locale/NUT-Monitor.pot, …/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo, scripts/python/app/locale/fr/fr.po, …/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo, scripts/python/app/locale/it/it.po, …/python/app/locale/ru/LC_MESSAGES/NUT-Monitor.mo, scripts/python/app/locale/ru/ru.po, scripts/python/app/ui/aboutdialog1.ui, scripts/python/app/ui/gui-1.3.glade, scripts/python/app/ui/gui-1.3.glade.h: scripts/python/app/NUT-Monitor-py*.in, NEWS.adoc: bump NUT-Monitor client versions to 2.1.0 [#3514, #3509]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: fix focus chain (TAB/SHIFT-TAB order) of login page interface elements
  • docs/man/NUT-Monitor.txt: update with nutauth.conf support [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: revise enablement of login/pass fields in connection with "User auth" button [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: fix real-time update of credential suggestion as host/port are being edited [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: revise ups_authentication_check checkbox for authconf driven suggestions [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: fix tracking of suggested/edited login/pass flag and style as other fields get edited [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: fix code style after manual and AI edits [#3509]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: if the default authconf section suggested credentials already, highlight that too [#3514]
  • scripts/python/module/PyNUT.py.in: readAuthConfFile(): fix handling of section name followed by comment [#3329, #3514]
  • scripts/python/module/PyNUT.py.in: refactor with AuthConf.printDebug() and print to stderr [#1711]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: consider entered login/password or unchecked box for use of them when logging in with authconf (for SSL etc) [#3514]
  • NEWS.adoc: update for refactor of background() method [#3437]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: refactor handling if user/password fields were suggested vs manually edited (at least one of them) [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: readAuthConfFile() calls: use options to trace absent nutauth.conf files [#3329, #3514]
  • scripts/python/module/PyNUT.py.in: readAuthConfFile(): add options to trace absent nutauth.conf files [#3329, #3514]
  • scripts/python/module/PyNUT.py.in: add AuthConf.clone() helper method [#3329, #3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: track if user/password fields were suggested (per authconf) and highlight them then [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: avoid a RecursionError on console when closing the UI window [#3514] Avoid Error in sys.excepthook: RecursionError: maximum recursion depth exceeded
  • drivers/main.c: fix comment style to old C [#3437]
  • NEWS.adoc, docs/nut.dict, drivers/nutdrv_qx_voltronic-qs.c: drivers/nutdrv_qx_voltronic-qs.c, NEWS.adoc: bump VOLTRONIC_QS_VERSION and reword the news entry [#3521]
  • drivers/nutdrv_qx_voltronic-qs-hex.c, NEWS.adoc: fix format string for ups.load [#3532]
  • INSTALL.nut.adoc: update with a larger note about packages lagging behind development tip [#3531] Also remove references to package maintainers being part of core team, there was a bit of turbulence in the past years, such notes get obsolete…

2.76. 2026-07-19 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-*.in: fix loading of saved "favorites" passwords (not base64-decoded before) [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-*.in: consider authconf hits for SSL setup [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-*.in: only replace login/pass if never edited by user but rather suggested from authconf [#3514]
  • NEWS.adoc, scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py*.in: add support for NUT Auth configuration files [#3514]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-*.in: refactor old_handler teardown with new graceful and harsher disconnect() handling [#3509]
  • scripts/python/app/NUT-Monitor-py2gtk2.in, scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in, scripts/python/module/PyNUT.py.in: scripts/python/app/NUT-Monitor-*.in, scripts/python/module/PyNUT.py.in: refactor with a graceful and harsher disconnect() handling [#3509]
  • scripts/python/app/NUT-Monitor-py2gtk2.in: handle lack of pynotify more gracefully [#3509]
  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py3qt{5,6}.in: refactor gui_status_message() with try/except handling [#3509]
  • scripts/python/app/NUT-Monitor-py2gtk2.in: gui_status_message(): fix indentation [#3509]
  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py3qt{5,6}.in: fix "Broken pipe" and possible stall in reconnection attempts [#3509]
  • scripts/python/module/PyNUT.py.in: refactor with an error-logging __send() [#3509]
  • scripts/python/module/PyNUT.py.in: __read_until(): handle the case of un-received chunk [#3509]
  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: scripts/python/app/NUT-Monitor-py3qt{5,6}.in: mark the templates as nominally executable

2.77. 2026-07-17 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/mge-hid.c, NEWS.adoc: bump DRIVER_VERSION and update news about CHRG status suppression [#3518, #3519]

2.78. 2026-07-15 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, scripts/python/app/NUT-Monitor-py2gtk2.in: scripts/python/app/NUT-Monitor-py2gtk2.in: propose a fix for reconnection modeled after PR #3523 for Python3 siblings [#3509] Co-Authored-By: Programer <developer.sorli@gmail.com>
  • NEWS.adoc: NUT-Monitor updated with automatic reconnection [#3509, #3523]
  • NEWS.adoc, drivers/blazer_usb.c, drivers/nutdrv_atcl_usb.c, drivers/nutdrv_qx.c, drivers/powercom-hid.c, drivers/richcomm_usb.c, drivers/riello_usb.c, drivers/tripplite_usb.c: drivers/*.c, NEWS.adoc: play safer with temporary buffers, pre-zero some [#3529]
  • clients/upsmon.c: cast assignments into temporary argv[] [#3499]
  • tests/NIT/upssched.conf.in: fix path to upssched-cmd sample [#3499, #1711]
  • tests/NIT/nit.sh: we no longer support NOTIFYCMD with shell snippets in one token [#3499]
  • clients/upsmon.c: upsmon_cleanup(): NULLify freed allocations; free cert-related data [#3499]
  • NEWS.adoc, clients/upsmon.c, clients/upssched.c: clients/upssched.c: add an atexit(clean_exit) handler similar to that in upsmon [#3499]
  • NEWS.adoc, UPGRADING.adoc, clients/upsmon.c, clients/upssched.c, conf/upsmon.conf.sample.in, conf/upssched.conf.sample.in, docs/man/upsmon.conf.txt, docs/man/upssched.conf.txt: upsmon, upssched: extend SHUTDOWNCMD, NOTIFYMSG and CMDSCRIPT to handle command-line args as separately quoted tokens in the directive [#3499]

2.79. 2026-07-14 Jim Klimov <jimklimov+nut@gmail.com>

  • common/wincompat.c, drivers/dstate.c, clients/upssched.c: CreateNamedPipe(): add PIPE_REJECT_REMOTE_CLIENTS flag to only work with local host clients [#3527]
  • common/wincompat.c: syslog() shim: protect against re-entry if we upslog()/upsdebug() from this method [#3527]
  • common/wincompat.c: syslog() shim: do not flood stderr with repetitive SKIP: can't open existing event log NAMED_PIPE messages [#3527]
  • clients/upssched.c: help(): fix reference to man page
  • clients/upssched.c, NEWS.adoc: fix notify_msg from CLI when other options are present [#3525, #3105]
  • tests/NIT/nit.sh: do not die() in certificate prerequisite checks if we can follow up, logging the error in a way that seems too fatal to warnings parsers [#1711]
  • ci_build.sh, tests/NIT/nit.sh: simplify cut_filter() [#3526, #3109]
  • Do not add optional dependency on cut * Consider starting AND ending characters of input hash string for more randomness
  • NEWS.adoc, ci_build.sh, tests/NIT/nit.sh: ci_build.sh, tests/NIT/nit.sh: use shorter hash strings for cache dirs [#3526, #3109]

2.80. 2026-07-13 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upssched.c, common/wincompat.c, NEWS.adoc: use WIN32 security attributes for listening pipes [#3525] Replicate what was done for dstate.c in PR #3479. Co-authored-by: mbv06 <mbv06.dev@gmail.com>
  • common/wincompat.c, include/wincompat.h, drivers/dstate.c: move init_pipe_security() to be reusable [#3479, #3525] Co-authored-by: mbv06 <mbv06.dev@gmail.com>
  • include/wincompat.h: move include guard to be after heading comments
  • configure.ac: if CPPUNIT seems present but won’t build, suggest if C+\+ version could be the problem
  • scripts/perl/UPS/Nut.pm.in, scripts/perl/test_nutclient.pl, scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in: fix parsing of CERTIDENT NAME/PASS from nutauth.conf files [#3329]
  • clients/authconf.c, clients/nutclient.cpp, scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: consistently log seen unsupported keywords in nutauth.conf files [#3329] …this can well be a typo in parser code, help catch those.
  • tests/NIT/nit.sh: revise generation of PEM files with NSS binary builds [#1733, #3329] Now we need the PEM files always (when we can make them) for PERL and Python test suites. Generation of JKS (e.g. for jNut) remains conditional - if we would maintain the test certificate cache.
  • tests/NIT/nit.sh: fall back in nutauth-openssl.conf to use rootca.pem if no CERTHASH.0 links were populated [#1711, #3329]
  • tests/NIT/nit.sh: actively not-enable "CERTFILE" in nutauth.conf if upsmon.pem is missing [#1711, #3329]
  • tests/NIT/nit.sh: reshuffle determination of DO_USE_NIT_TESTCERT_CACHE and log different outcomes [#1711, #3329]
  • tests/NIT/nit.sh: at least report that upsmon.pem is missing and tests may fail, if we did not get one fallbacked for NSS builds [#1711]
  • tests/NIT/nit.sh: annotate failure messages for easier search in make-oriented logs [#1711]
  • ci_build.sh: annotate default-all-errors failure messages for easier search in make-oriented logs [#1711]

2.81. 2026-07-12 Jim Klimov <jimklimov+nut@gmail.com>

  • configure.ac: check for systemctl earlier, so it feeds into optional warning about lack of systemdsystemunitdir

2.82. 2026-07-09 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/Makefile.am, clients/upsclient.c, clients/upsclient.h: clients/upsclient.{c,h}: keep UPSCONN_t size same for different SSL backends [#3331]

2.83. 2026-07-05 Programer <developer.sorli@gmail.com>

  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: NUT-Monitor: Add automatic reconnection mechanism for Qt5 and Qt6 - Add automatic reconnection when connection to NUT server is lost - Implement proper teardown of old PyNUTClient instances - Use specific exceptions (socket.error, ConnectionError, EOFError, OSError, PyNUT.PyNUTError) - Prevent multiple simultaneous reconnection attempts - Add was_online flag to track connection state changes

    • Tested and confirmed working on both Qt5 and Qt6 Closes: #3509

2.84. 2026-07-03 Stephen Horvath <s.horvath@outlook.com.au>

  • docs/nut.dict: Add voltronic-qs QI command
  • NEWS.adoc, data/driver.list.in: NEWS.adoc, driver.list.in: Update for voltronic-qs & PSDR800
  • drivers/nutdrv_qx_voltronic-qs.c: nutdrv_qx: Voltronic-QS: Add QI command This command adds battery charge, runtime, input frequency, output current, and input transfer low/high values to the Voltronic-QS driver. Tested on a PowerShield PSDR800.

2.85. 2026-07-01 strasharo <3337997+strasharo@users.noreply.github.com>

  • drivers/mge-hid.c: mge-hid: suppress CHRG on CC-mode devices when battery is fully charged UPS.PowerSummary.PresentStatus.Charging stays asserted in float/ maintenance mode on constant-charge (CC-mode) devices such as the Eaton 5E series. These devices do not expose the ABM HID path (UPS.BatterySystem.Charger.ABMEnable), so advanced_battery_monitoring stays ABM_UNKNOWN. eaton_abm_check_chrg_fun() then maps any Charging=1 report directly to CHRG with no distinction between active charging and float mode. The result is ups.status stuck at OL CHRG indefinitely, even when battery.charge is 100. Fix: when ABM is UNKNOWN or DISABLED and the UPS asserts Charging=1, check battery.charge before publishing CHRG. If the battery is at 100% the charger is in float/maintenance mode, not actively charging, so return !chrg instead. Tested on Eaton 5E 650i (vendor 0463, product ffff, subdriver MGE HID 1.58, firmware 01.04.0016) where ups.status now correctly shows OL at full charge instead of OL CHRG.

2.86. 2026-06-30 Jim Klimov <jimklimov@gmail.com>

  • tests/NIT/nit.sh: setenv_ssl_perl(): in case of darwin OS, neuter CERTVERIFY also in nutauth.conf [#1711]

2.87. 2026-06-30 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c, clients/authconf.h: clients/authconf.h: do not require nut_stdint.h in public API [#3329]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: fix AuthConf::authconf_list to be actually a list, not vector [#3329] Memory-safer to extend and manipulate.
  • clients/nutclient.cpp: SSLConfig::addCertHost(): avoid duplicates [#3329]
  • tests/NIT/nit.sh: setenv_ssl_perl(): fix up paths on Windows in a new nutauth-openssl-perl-win.conf [#3329, #1711]
  • tools/gitlog2changelog.py.in: fix unicode TypeError with python 2.7
  • scripts/python/module/PyNUT.py.in: AI added "import getpass" needlessly [#3329]
  • scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in: scripts/python/module/PyNUT.py.in: do not strip brackets from section names after creation (fix IPv6 address handling) [#3229, #1711]
  • scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in: avoid Python 3 formatting strings [#3229, #1711] There is no reason for module to not work for older OS installations.
  • tests/NIT/nit.sh: setenv_ssl_*(): pass NUT_SSL=true for SSL builds even if skipping other settings due to nutauth.conf [#3329, #1711] May neuter later in the method (e.g. perl lacking SSL module).
  • NEWS.adoc, clients/upsmon.c, clients/upsmon.h, common/nutconf.cpp, common/nutwriter.cpp, include/nutconf.hpp: clients/upsmon.{c,h}, nutconf.cpp, nutwriter.cpp, NEWS.adoc: introduce NOTIFY_SUSPEND_TIMEJUMP_UNEXPECTED [#3405]

2.88. 2026-06-29 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/test_nutclient.py.in, scripts/perl/test_nutclient.pl, tests/cpputest-client.cpp: only set "NUT_SSL=True" from AuthConf NUT_FORCESSL if no value is passed as envvar [#3329, #1711]
  • tests/cpputest-client.cpp: setUp(): fix env_NUT_CERTHOST_ADDR and env_NUT_CERTHOST_NAME [#3329, #1711]
  • tests/cpputest-client.cpp: setUp(): fix env_NUT_CERTSTORE_PATH vs env_NUT_CAPATH (nss vs openssl backend) [#3329, #1711]
  • tests/cpputest-client.cpp: setupClientSSL(): trace incoming envvars and configs being applied [#3329, #1711]
  • tests/cpputest-client.cpp: setUp(): fix env_NUT_CERTIDENT_NAME vs env_NUT_KEYFILE [#3329, #1711]
  • tests/cpputest-client.cpp: revise certfile vs certpath handling [#3329, #1711]
  • scripts/python/module/test_nutclient.py.in: no longer need detailed AuthConf debug in tests [#3329]
  • scripts/python/module/PyNUT.py.in: fix readAuthConfFile() to strip spaces from KEY [#3329]
  • scripts/python/module/PyNUT.py.in: fix readAuthConfFile() to create new section with stripped brackets [#3329]
  • scripts/python/module/PyNUT.py.in: fix readAuthConfFile() to use open() not os.open() [#3329]
  • scripts/python/module/PyNUT.py.in: revise logging in AuthConf getter, reader, merger [#3329]
  • scripts/python/module/PyNUT.py.in: add PyNUT.AuthConf.str() method [#3329, #1711]
  • scripts/perl/test_nutclient.pl: revise certfile vs certpath handling
  • tests/NIT/nit.sh: refine NUT_SSL=false vs NUT_IGNORE_AUTHCONF [#1711, #3329]
  • tests/NIT/Makefile.am: check-NIT-devel: we now depend on more clients [#1711]

2.89. 2026-06-28 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/PyNUT.py.in: track AuthConf section name value without the brackets [#1711, #3329]
  • scripts/perl/test_nutclient.pl: tone down AuthConf parser debugging now [#1711, #3329]
  • tests/NIT/nit.sh: populate upsmon.pem for OpenSSL-only clients when generating NSS certs [#1711, #3311]
  • scripts/perl/UPS/Nut.pm.in: fix key casing in readAuthConfFile() [#3329]
  • scripts/perl/UPS/Nut.pm.in: more logging and comments to readAuthConfFile() [#3329]
  • tests/NIT/nit.sh: generatecfg_nutauth(): produce a nutauth-openssl.conf for Perl and Python tests to work even with NSS C builds [#3329, #1711]
  • scripts/perl/UPS/Nut.pm.in: rectify merge() logging [#3329]
  • tests/NIT/nit.sh: generate nutauth.conf with both "localhost" and "127.0.0.1" entries for different queries [#3329, #1711]
  • scripts/perl/UPS/Nut.pm.in, scripts/perl/test_nutclient.pl, scripts/python/module/test_nutclient.py.in: scripts/perl/UPS/Nut.pm.in, scripts/perl/test_nutclient.pl: rectify findAuthConf()/getAuthConf() logic and logging [#3329]
  • docs/Makefile.am: ChangeLog.adoc-parsed: convert markdown links to asciidoc (grok dependabot commit comments) [dependabot/dependabot-core#15228]
  • NEWS.adoc: move ragtech entry to post-2.8.5 release

2.90. 2026-06-26 juslex <66561713+juslex@users.noreply.github.com>

  • scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in, tests/nut-driver-enumerator-test—ups.conf, tests/nut-driver-enumerator-test.sh: nut-driver-enumerator: classify ragtech as serial media The ragtech driver talks to a USB CDC-ACM device node (/dev/ttyACM*) through the serial layer. Classifying it as "serial" media lets the auto-generated nut-driver@<ups> service depend on systemd-udevd / nut-udev-settle, so it waits for the device node to be published by udev before starting instead of racing the enumeration at boot. Requested by @jimklimov in issue #3500. Add a [ragtech-serial] section to the enumerator self-test fixture and the matching entries in the affected expected outputs (device-name listing, config dump, and MEDIA/checksum listing). Full self-test passes (104 cases).

2.91. 2026-06-26 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/test_nutclient.py.in: fix interpretation of ac.certfile [#3329]
  • tests/cpputest-client.cpp: debug progress through authconf parsing [#3329, #1711]
  • scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in: scripts/python/module/*: debug progress through authconf parsing
  • scripts/perl/UPS/Nut.pm.in, scripts/perl/test_nutclient.pl: scripts/perl/*: debug progress through authconf parsing
  • scripts/python/module/PyNUT.py.in: AuthConf: fix upper-/any- case expectations [#3329]
  • scripts/perl/UPS/Nut.pm.in: readAuthConfFile(): fix upper-/any- case expectations [#3329]
  • tests/NIT/nit.sh: report current SSLBACKEND in nutauth.conf [#1711]
  • clients/nutclient.cpp: handle_authconf_args(): AuthConf::merge(): pepper with debug [#3329]
  • clients/nutclient.cpp: handle_authconf_args(): set user name early (if known from section), so we can ignore it in set_authconf_val() [#3329]
  • clients/nutclient.cpp: set_authconf_val(): fix upper-/any- case expectations [#3329]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{h,cpp}: introduce methods to dump AuthConf data for debug [#3329]
  • clients/authconf.c: update comment [#3329]
  • scripts/python/module/test_nutclient.py.in, scripts/python/module/PyNUT.py.in: reshuffle debugging to see into AuthConf handling [#3329, #1711]
  • scripts/python/module/PyNUT.py.in: debug-log more details about SSL setup if it is enabled [#3329, #1711]
  • clients/authconf.h, clients/nutclient.h, scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: C, C+\+, Perl, Python AuthConf implementations: update comment about "certfile" field [#3329]
  • conf/upsmon.conf.sample.in, docs/man/upsmon.conf.txt: fix CERTFILE description ("client" not "server" here) [#3329]
  • scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in: scripts/python/module/PyNUT.py.in: rename AuthConf "pass_" field to "password" [#3329]
  • scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: document meanings of AuthConf fields [#3329]
  • scripts/python/module/PyNUT.py.in: fix back mis-merged edits [#3329]
  • scripts/perl/test_nutclient.pl: reshuffle debugging to see into AuthConf handling [#3329, #1711]
  • scripts/perl/UPS/Nut.pm.in: UPS::Nut→new(): support args hash with an AUTHCONF key to parse it and merge with other args [#3329]
  • scripts/perl/UPS/Nut.pm.in: revise debug logging of AuthConf progress [#3329]
  • clients/nutclient.cpp: parse_authconf_file(): upsdebugx() which file we are looking at [#3329]
  • scripts/perl/test_nutclient.pl, scripts/python/module/PyNUT.py.in, scripts/python/module/test_nutclient.py.in, tests/cpputest-client.cpp: Fix C+\+, Perl, Python test runners to consider nutauth.conf [#3329] WIP
  • scripts/perl/UPS/Nut.pm.in: refactor error/debug messaging from readAuthConfFile() [#3329]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{h,cpp}: add AuthConf operator= [#3329]

2.92. 2026-06-25 Thanh Nguyen <imscopilot@outlook.com>

  • data/driver.list.in: Add ARES AR630 to HCL

2.93. 2026-06-24 dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • .github/workflows/01-make-dist-worker.yml, .github/workflows/05-codeql.yml: build(deps): bump actions/cache from 5 to 6 Bumps actions/cache from 5 to 6. - Release notes - Changelog

    • Commits --- updated-dependencies: - dependency-name: actions/cache dependency-version: 6 dependency-type: direct:production update-type: version-update:semver-major …
  • .github/workflows/01-make-dist-worker.yml, .github/workflows/05-codeql.yml: build(deps): bump actions/checkout from 6 to 7 Bumps actions/checkout from 6 to 7. - Release notes - Changelog

    • Commits --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7 dependency-type: direct:production update-type: version-update:semver-major …

2.94. 2026-06-24 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/cpputest-client.cpp, scripts/perl/test_nutclient.pl, scripts/python/module/test_nutclient.py.in: use nututh.conf files if possible [#3329, #1711]
  • tests/NIT/nit.sh: setenv_ssl_*(): skip work by default if nutauth.conf exists [#1711, #3329]
  • scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: revise splitting of host:port strings where host looks like numeric IPv6 [#3503]
  • clients/nutclient.cpp: AuthConf::merge(): gracefully handle section name that starts with "@" [#3329]
  • clients/nutclient.cpp, scripts/perl/UPS/Nut.pm.in, scripts/python/module/PyNUT.py.in: revise normalization of host strings that look like numeric IPv6 [#3503]
  • clients/nutclient.cpp: update splitaddr() against libupsclient variant
  • scripts/python/module/test_nutclient.py.in, tests/NIT/Makefile.am: scripts/python/module/test_nutclient.py: add testsuite_splitaddr() [#3329, #1711]
  • scripts/python/module/PyNUT.py.in: allow to construct PyNUTClient without connecting right away; remember requested TRACKING option to apply whenever we do connect() [#3329, #1711]
  • configure.ac, scripts/Makefile.am, scripts/perl/.gitignore, scripts/perl/UPS/{Nut.pm ⇒ Nut.pm.in}, tests/NIT/Makefile.am, tests/NIT/nit.sh: Convert UPS/Nut.pm into a .in template to handle @CONFPATH@ correctly [#1711, #3329]
  • clients/authconf.c: parse bracketed IPv6 addresses correctly [#3329, #3503]
  • tests/test_authconf.c: add tests to parse IPv6 addresses (brackets and colons!) [#3329, #3503]
  • tests/test_authconf.c: test that (comment) text after a section name is ignored [#3329]
  • tests/test_authconf.c: auto-account expected_sections (to check parsed num_sections) [#3329]
  • tests/NIT/nit.sh: generatecfg_nutauth(): only handle case WITH_SSL_CLIENT_CERTIDENT ... if WITH_SSL_CLIENT != none [#1711]
  • tests/NIT/nit.sh: move check for usability of certutil or openssl into prepare_NIT_certs() where we would inevitably use them [#1711]
  • tests/NIT/nit.sh: fix indentation [#1711]

2.95. 2026-06-23 juslex <66561713+juslex@users.noreply.github.com>

  • NEWS.adoc, docs/man/ragtech.txt, docs/nut.dict, drivers/ragtech.c: ragtech: fix CDC-ACM startup (raw mode \+ initial-poll retry), battery scaling, va override Startup robustness (issue #3500, Easy Pro 3200 VA GT on a Raspberry Pi) ---------------------------------------------------------------------- The reporter’s unit failed every boot with "no reply to initial status poll", even though it speaks the family-10 protocol correctly (its 30-byte main range decodes cleanly against the OEM Supervise 8 readings). The root cause, pinned down from an strace \+ the probe script, was the serial line discipline, not the protocol:
  • A freshly enumerated CDC-ACM tty comes up in canonical mode (ICANON). In that mode the kernel buffers incoming bytes until it sees a newline. The firmware’s replies are raw binary and never contain one, so the bytes were held in the kernel and the first poll always timed out. The driver had been relying on the port already being raw — which happened to be true on the author’s host only because its systemd unit ran "stty … raw" in an ExecStartPre, masking the bug. Without that external step (e.g. a plain "upsdrvctl start"), the driver never worked. upsdrv_initups() now sets raw 8N1 mode itself: tcgetattr/clear ICANON et al./tcsetattr, leaving the baud untouched (CDC-ACM ignores it at the wire), with CLOCAL set and HUPCL cleared so neither the reconfigure nor the eventual close toggles DTR, and DTR/RTS are forced low afterwards. The previous "NEVER call tcsetattr" comment was unfounded: NUT’s own ser_set_speed() configures raw mode via tcsetattr and is used by \~50 serial drivers, the reporter observed no DTR pulse / shutdown, and this driver’s shutdown path is a register write, not a DTR edge. * upsdrv_initinfo() now retries the wake-up handshake and first poll a few times before calling fatalx(). Right after a cold boot the device may have only just enumerated and the firmware can need a moment to answer. Retrying lets the driver come up cleanly under the stock NUT systemd units (which start it as soon as the device node appears) instead of depending on a fatal exit plus a Restart=on-failure cycle to recover, which is noisy and only retries on a 5 s cadence. Combined with the raw mode above, no external stty, sleep, or .device binding is needed. * The firmware answers the handshake with a byte or two (e.g. 0xCA). Some models are half-duplex and drop a command sent while that reply is still in flight; the handshake reply is now drained before the first poll (which only works because raw mode actually delivers those bytes). Other fixes ----------- * Compute battery.charge as raw / 2.55 so a full battery reports exactly 100 % instead of 100.2 % (drops the previous clamp). * Add a "va" ups.conf option to override the apparent-power rating. The model id byte is shared between product lines (a 3200 VA GT reports the same id as an Easy 2200 TI), so the driver cannot always pick the right VA from its table; the override corrects ups.power.nominal, ups.realpower.nominal and the computed ups.load. Verified: family-10 Easy 2000 TI on hardware (raw mode, handshake drain, battery 100.0, va=3200 override) and the 3200 VA GT by the reporter (full telemetry, matching Supervise 8). Driver bumped to 0.10, still DRV_EXPERIMENTAL.

2.96. 2026-06-23 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.c: upscli_free_host_port_cert_data(): only defined for SSL-capable builds [#3329]
  • clients/upsclient.c: upscli_free_host_port_cert_data(): consider const-ness of freed strings [#3329]
  • clients/upsclient.c: upscli_free_host_port_cert(): consider builds without SSL [#3329]
  • drivers/dummy-ups.c: refactor with upscli_authconf_update_conn_flags() [#3329]
  • clients/upsc.c, clients/upsclient.c, clients/upsclient.h, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsrw.c, clients/upsset.c, clients/upsstats.c, tools/nut-scanner/scan_nut.c: tools/nut-scanner/scan_nut.c, clients/*.c: refactor with upscli_authconf_update_conn_flags() [#3329]
  • clients/upsc.c, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsrw.c, clients/upsset.c, clients/upsstats.c: clients/*.c: only consider authconf default certverify and flags_ssl if positive [#3329]
  • tests/NIT/nit.sh: on MSYS2, copy (not symlink) root CA cert hash-named files right away [#1711]
  • tests/NIT/nit.sh: when transplanting prepared certs to/from cache, fall back to dereferencing symlinks if cp -prf failed [#1711]
  • tests/NIT/nit.sh: TESTCERT_PATH_SEP: there was too much of a good thing for WIN32 runs [#1711]
  • tests/NIT/nit.sh: use TESTCERT_PATH_SEP more diligently (e.g. on WIN32 runs) [#3331, #1711]
  • tests/NIT/nit.sh: die() with a timestamp too

2.97. 2026-06-23 Boris Shor <boris@bshor.com>

  • NEWS.adoc, data/driver.list.in, docs/nut.dict, drivers/idowell-hid.c: idowell-hid: claim GoldenMate LiFePO4 (06da:ffff) without hijacking other Phoenixtec devices GoldenMate 1000VA/800W LiFePO4 packs reuse the shared Phoenixtec vendor ID 0x06da:0xffff with the same -BMS- firmware and HID descriptor as the existing iDowell 0x075d:0x0300 device, so they fall back to the generic liebert-hid handler (bogus battery.runtime, etc). The 0x06da VID is shared: mge-hid claims it for AEG PROTECT NAS and liebert-hid is its default sink. Probe order is mge → idowell → liebert, so idowell can intercept GoldenMate before liebert provided it does not grab the other 0x06da devices. - Add a named PHOENIXTEC_VENDORID define and use it for the 0x06da:0xffff device-table entry (feeds the udev/nut-scanner parser). - Gate idowell_claim() on the device’s -BMS-/Smart-Battery strings, like mge_claim() does for AEG, returning 0 for non-GoldenMate 0x06da so they fall through to liebert-hid / mge-hid. - Bump IDOWELL_HID_VERSION to 0.21 and add a NEWS.adoc entry. - List both GoldenMate variants in data/driver.list.in. Fixes: https://github.com/networkupstools/nut/issues/3501

2.98. 2026-06-22 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, UPGRADING.adoc, clients/upsmon.c, clients/upssched.c, conf/upsmon.conf.sample.in, conf/upssched.conf.sample.in, docs/man/upsmon.conf.txt, docs/man/upsmon.txt, docs/man/upssched.conf.txt, docs/man/upssched.txt, docs/nut.dict: clients/upsmon.c, clients/upssched.c, docs: handle NOTIFYCMD and CMDSCRIPT values strictly as paths [#3499]
  • docs/configure.txt: properly pass RPATH for linker not compiler in LDFLAGS [#3495]
  • NEWS.adoc: update about recipe and code portability PRs [#3489, #3495, #3498]
  • ci_build.sh: properly pass RPATH for linker not compiler in LDFLAGS [#3495]
  • NEWS.adoc, clients/Makefile.am, clients/upsimage.c, clients/upsimagearg.h: clients/upsimage.c, clients/upsimagearg.h: adjust scaling for small (single-digit) ranges [#3469] Avoid lack of output when the range of a variable is small (e.g. 0-10) and division by zero occurred instead.
  • NEWS.adoc, clients/upsc.c, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsmon.c, clients/upsrw.c, clients/upssched.c, clients/upsset.c, clients/upsstats.c: clients/*.c, NEWS.adoc: fflush() stderr and stdout before clean-up and exit, just in case [#3454]
  • clients/upsclient.c: wherever we fail upscli_init*(), call upscli_cleanup() right away [#3454]
  • clients/upsclient.c: pre-initialize SSL_CTX as NULL to be sure [#3454]

2.99. 2026-06-21 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/.gitignore: GitIgnore tests/test_authconf if built [#3329]
  • NEWS.adoc, docs/man/dummy-ups.txt, drivers/dummy-ups.c: dummy-ups: add NUT authconf support [#3329]

2.100. 2026-06-21 Huang Rui <vowstar@gmail.com>

  • NEWS.adoc, drivers/xppc-mib.c: feat(snmp-ups): map more xppc readings for Phoenixtec UPS cards - add battery.runtime, battery.voltage and input.frequency readings - add output.voltage.nominal and ups.firmware.aux readings - bump XPPC_MIB_VERSION to 0.41

2.101. 2026-06-21 Doug Nazar <nazard@nazar.ca>

  • server/upsd.c: AIX 64 uses a long for fd in pollfd
  • ci_build.sh: Tell compiler to pass -R option to linker instead of a remark

2.102. 2026-06-20 penghong123 <2835950872@qq.com>

  • data/driver.list.in: Add some new UPS models

2.103. 2026-06-20 Doug Nazar <nazard@nazar.ca>

  • include/timehead.h: Provide function declaration when needed
  • drivers/powervar_cx_ser.c: AIX doesn’t provide defines for those speeds.
  • clients/upsc.c, clients/upscmd.c, clients/upslog.c, clients/upsrw.c: Ignore unused parameters
  • Makefile.am: Switch to using -t to specify date as -d not always availble
  • configure.ac: Fix %zu format check
  • configure.ac: Use lsgroup on AIX to check for valid groups
  • m4/nut_compiler_family.m4: Check enable version of warnings to see if valid GCC will silently accept warning disable options, but then if there is an issue, warn about the option. "cc1: note: unrecognized command-line option -Wno-unknown-warning-option may have been intended to silence earlier diagnostics"
  • scripts/python/module/Makefile.am: Don’t try to copy permissions When the source files are on a different filesystem it’s possible for the copy to fail. Seen on FreeBSD, when trying to copy from an NFS mount it would fail trying to copy the ACLs.
  • tools/Makefile.am: Use full path for generated files When using relative paths Make will use VPATH to find the file in the source directory. If out of date, it will then try to update that file instead and fail on a read-only source directory.
  • include/common.h, tools/nut-scanner/scan_eaton_serial.c: Add check for older IBM compilers. Newer compilers are based on Clang and errors on GCC.

2.104. 2026-06-19 Jim Klimov <jimklimov+nut@gmail.com>

  • tools/nut-scanner/scan_nut.c: follow up on NUT authconf support when scanning "old nut" servers [#3329]
  • clients/upsclient.c, clients/upsclient.h: clients/upsclient.{c,h}: add pthreads support for first_host_cert-based list manipulation, and methods to remove items from list [#3329]

2.105. 2026-06-17 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.h: update comments for methods about default_connect_timeout [#3329]
  • tools/nut-scanner/scan_nut.c: nutscan_scan_ip_range_nut_authconf(): detect and load NUT auth conf file like other clients do [#3329]
  • tools/nut-scanner/scan_nut.c: nutscan_scan_ip_range_nut_authconf(): detect desired timeout like other NUT clients do [#3329]

2.106. 2026-06-16 Jim Klimov <jimklimov+nut@gmail.com>

  • tools/nut-scanner/scan_nut.c: detect if authconf-related methods are present in libupsclient build we try to use [#3329]
  • docs/man/nutscan_scan_nut.txt, tools/nut-scanner/Makefile.am, tools/nut-scanner/nut-scan.h, tools/nut-scanner/scan_nut.c: tools/nut-scanner/*, docs/man/nutscan_scan_nut.txt: introduce nutscan_scan_ip_range_nut_authconf() and nutscan_scan_nut_authconf() with nutscan_nut_authconf_t argument [#3329] Make old methods wrappers of the new ones, so existing libnutscan clients continue working as they were (no breaking ABI change, just new methods).
  • scripts/obs/debian.rules, scripts/obs/nut.spec: update comments about SO_MAJOR_LIB* version hassle
  • docs/nut.dict: sort the dictionary after PR #3447
  • NEWS.adoc: mention PR #3448
  • NEWS.adoc: mention PR #3456
  • docs/Makefile.am: fix ASPELL_ENV_LC_ALL for platforms that care
  • NEWS.adoc: refer to PR #3485

2.107. 2026-06-15 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/ci-farm-do-setup.adoc, docs/nut.dict: add notes about ZFS and 4KB disk sectors

2.108. 2026-06-14 mbv06 <mbv06.dev@gmail.com>

  • NEWS.adoc, data/driver.list.in, drivers/nutdrv_qx.c: nutdrv_qx: Fix armac subdriver failing on 7-byte interrupt reads Certain UPS models (e.g. SVC VP-1250-LCD) return a 7-byte report instead of the standard 6 bytes. This relaxes the strict equality check in the interrupt read loop and increases the requested read size to 8 bytes to safely accommodate these devices without dropping trailing bytes or timing out.

2.109. 2026-06-12 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: update optional traceable toggles

2.110. 2026-06-11 mbv06 <mbv06.dev@gmail.com>

  • NEWS.adoc: docs: note Windows state pipe security update

2.111. 2026-06-11 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/richcomm_usb.c, NEWS.adoc: bump DRIVER_VERSION and document the improvements [#1768, #3477]

2.112. 2026-06-10 dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • .github/workflows/01-make-dist.yml: build(deps): bump bluwy/substitute-string-action from 3 to 4 Bumps bluwy/substitute-string-action from 3 to 4. - Release notes - Commits ---- updated-dependencies: - dependency-name: bluwy/substitute-string-action dependency-version: 4 dependency-type: direct:production update-type: version-update:semver-major ----
  • .github/workflows/01-make-dist-worker.yml: build(deps): bump actions/upload-artifact from 4 to 7 Bumps actions/upload-artifact from 4 to 7. * Release notes * Commits ---- updated-dependencies: * dependency-name: actions/upload-artifact dependency-version: 7 dependency-type: direct:production update-type: version-update:semver-major ----

2.113. 2026-06-09 mbv06 <mbv06.dev@gmail.com>

  • docs/man/richcomm_usb.txt, drivers/richcomm_usb.c, tools/nut-scanner/scan_usb.c: Fix richcomm USB matching options
  • drivers/richcomm_usb.c: drivers: preserve libusb context across USB reopens (richcomm only)

2.114. 2026-06-08 mbv06 <mbv06.dev@gmail.com>

  • drivers/dstate.c: drivers: fix Windows state pipe permissions

2.115. 2026-06-08 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.h, clients/nutclient.cpp, clients/nutclient.h, scripts/perl/UPS/Nut.pm, scripts/python/module/PyNUT.py.in: clients/authconf.h, clients/nutclient.{h,cpp}, scripts/perl/UPS/Nut.pm, scripts/python/module/PyNUT.py.in: revise ability to parse nutauth.conf files [#3329]
  • scripts/perl/UPS/Nut.pm: fix old trailing whitespaces

2.116. 2026-06-07 Jim Klimov <jimklimov+nut@gmail.com>

  • include/nutconf.hpp, common/nutconf.cpp: add ability to parse nutauth.conf files [#3329]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.h, scripts/perl/UPS/Nut.pm, scripts/python/module/PyNUT.py.in: clients/nutclient*.{h,cpp}, scripts/perl/UPS/Nut.pm, scripts/python/module/PyNUT.py.in: add ability to parse nutauth.conf files [#3329]

2.117. 2026-06-06 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.c: upscli_authenticate(): reduce buf sizes for result to fit UPSCLI_NETBUF_LEN [#3329]
  • clients/upsimage.c, clients/upsset.c, clients/upsstats.c: clients/ups*.c: comment in CGI clients about auto-login (not wise for that use case) [#3329, #3411]
  • clients/upsc.c: support upscli_authenticate_authconf() [#3411, #3329]
  • clients/upsclient.c, clients/upsclient.h, clients/upscmd.c, clients/upsmon.c, clients/upsrw.c, docs/man/Makefile.am, docs/man/upsc.txt, docs/man/upscli_authenticate.txt, docs/man/upscmd.txt, docs/man/upslog.txt, docs/man/upsrw.txt: clients/upsclient.{c,h}, docs/man, upscmd.c, upsrw.c, upsmon.c: refactor with upscli_authenticate() to unite different tricks [#3329] Introduce upscli_authenticate_authconf() for completeness.
  • docs/Makefile.am: fix SOURCE_CHECK_UNICODE for out-of-tree builds

2.118. 2026-06-05 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/Makefile.am, configure.ac: configure.ac: revise LIBSSL_LIBS vs "-pthread" option (from NSS occasionally) for C+\+ builds [#3453] Address error: argument unused during compilation: -pthread
  • Makefile.am, SECURITY.md, docs/Makefile.am: SECURITY.md: introduce a policy that can be seen on GitHub Describe the current situation with support of NUT versions and the parties involved, and how to responsibly disclose vulnerabilities.

2.119. 2026-06-03 Jim Klimov <jimklimov+nut@gmail.com>

  • .github/workflows/01-make-dist-worker.yml, .github/workflows/01-make-dist.yml, .github/workflows/05-codeql.yml, .github/workflows/08-PyNUTClient.yml: .github/workflows/*.yml: bump actions versions Originally proposed by dependabot, but we cannot put its Markdown commit comments into ChangeLog.pdf, so reposting.
  • .github/dependabot.yml: introduce scanning for obsolete GHA versions
  • .github/workflows/01-make-dist.yml: update comments about the Checks message [#1400]
  • .github/workflows/01-make-dist.yml: when dispatching the workflow for a branch iteration, use its name (ref) not commit [#1400]
  • .github/workflows/01-make-dist.yml: delete old artifacts OTHER than the one just created [#1400] Before the recent refactoring, the logic was to first build the tarball, then if it succeeded - clean up the storage from obsolete ones, and quickly publish the new one. Now that a separate workflow does the uploads, we can at best ensure deletion of obsolete tarballs after hopefully having the new one up there (had enough space and all that). Also take care to not delete the newly made artifact ;)
  • NEWS.adoc: update for FreeBSD and Jenkins bumps
  • NEWS.adoc: Rearranged the GHA job to prepare Dist and Docs Tarballs [#1400, #3460]
  • .github/workflows/01-make-dist.yml: launch "GHA-01: Tarballs Build Worker" definition from the target branch (if a PR) [#1400]
  • configure.ac: pass -Wno-unused-command-line-argument also to CLANG LDFLAGS
  • .github/workflows/01-make-dist.yml: DRY: define artifact_name once [#1400]

2.120. 2026-06-02 Jim Klimov <jimklimov+nut@gmail.com>

  • .github/workflows/01-make-dist-worker.yml, .github/workflows/01-make-dist.yml: .github/workflows/01-make-dist{,-worker}.yml: retell the story of two workflows [#1400]
  • .github/workflows/01-make-dist.yml: reduce the permissions needed for artifact manipulation [#1400]

2.121. 2026-06-01 Jim Klimov <jimklimov+nut@gmail.com>

  • Jenkinsfile-dynamatrix: use new countStagesPerNode feature from the library in notifyHandlerAlert
  • Jenkinsfile-dynamatrix: introduce dynacfgPipeline.notifyHandlerAlert (to report looping RESTART dynamatrix cells)

2.122. 2026-05-31 Jim Klimov <jimklimov+nut@gmail.com>

  • configure.ac: ignore recent clang warning about unused-command-line-argument

2.123. 2026-05-31 Doug Nazar <nazard@nazar.ca>

  • common/common.c, drivers/upsdrvquery.c, include/common.h, server/sstate.c: Use non-blocking connect() for AF_UNIX sockets to ensure we don’t stall If a driver has become unresponsive, connect() will block after the listen() backlog has been exhausted, causing the main upsd to also become unresponsive.

2.124. 2026-05-28 Jim Klimov <jimklimov+nut@gmail.com>

  • tools/check-source-nonascii.pl: fix syntax for hash length
  • ci_build.sh: fix normalize_path_perl() syntax for hash length; avoid newlines

2.125. 2026-05-27 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/config-prereqs.txt: document JDK21\+ as the requirement for NUT CI build farm agents since spring 2026
  • ci_build.sh: normalize_path() before reporting it at start of actual work about BUILD_TYPE
  • ci_build.sh: configure_nut(): report the PATH value ultimately used (maybe normalized)
  • ci_build.sh: propose shell vs perl (faster?) implementations of normalize_path()
  • drivers/bicker_ser.c, drivers/nut-libfreeipmi.c, drivers/phoenixcontact_modbus.c, drivers/socomec_jbus.c, drivers/usbhid-ups.c: drivers/*.c: replace Unicode in comments with ASCII equivalents
  • drivers/socomec_jbus.c: clean up trailing whitespace
  • docs/documentation.txt, docs/man/nut.txt: replace Unicode long dashes with acsii --
  • drivers/libwinhid.h: replace Unicode long dashes with acsii -- and arrow with =>
  • docs/developers.txt, docs/nut.dict: docs/developers.txt: add a chapter on ASCII vs Unicode
  • Makefile.am, configure.ac, docs/Makefile.am, tools/Makefile.am, tools/check-source-nonascii.pl: configure.ac, Makefile.am, docs/Makefile.am, tools/check-source-nonascii.pl: add unicode-in-sources detection to spellcheck On some platforms, at least the file type parsing fails for text with some but not other wide characters (so it is "data" and not some sort of "text"). This test was added for C sources as well, but keep in mind that the final authority is the actually used compilers - which are free to complain or not.
  • .github/pull_request_template.md: update with a section on Unicode characters
  • configure.ac: report the tested compiler location just before AC_PROG_CC etc. which might fail
  • configure.ac: unset CCACHE_DIR if provided but empty Notably, ccache-3.7.12 in FreeBSD 15 breaks CI build with that complaint.
  • drivers/ragtech.c: pre-declare variables for portability [#3447]
  • drivers/ragtech.c: replace Unicode long dashes with acsii -- [#3447]
  • docs/man/ragtech.txt: replace Unicode long dashes with acsii -- [#3447]

2.126. 2026-05-26 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/config-prereqs.txt, docs/nut.dict: revise with FreeBSD 15 lessons learned
  • configure.ac: if developer enables configure debug, only wipe DOCTESTDIR after confirming we can build docs we want
  • drivers/ragtech.c: fix compiler warnings about %u vs. conditionally picked numbers [#3447] …which are signed int by default

2.127. 2026-05-25 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, UPGRADING.adoc, docs/man/upsd.conf.txt, server/netssl.c: server/netssl.c: ssl_init(): abort with fatalx() and better explanations when SSL setup was requested but failed [#3331]
  • tests/NIT/nit.sh: check_NIT_certs_NSS(): fix the check for *.txt and *.db file patterns [#3331, #1711]
  • tests/NIT/nit.sh: wrap long openssl lines; fix a missed validity-time spec [#1711]
  • tests/NIT/nit.sh: check_NIT_certs_NSS(): verify old run-time toolkit vs. newer (cached) DB files [#3331, #1711] Auto-repair the trusted NSS DB set(s) of files if we can. Primarily relevant for build agents where same cache is used by NSS toolkits a decade apart.
  • docs/nut.dict: add opcodes
  • configure.ac: add -Wno-thread-safety-negative to clang flags (seen on FreeBSD builds)

2.128. 2026-05-24 Pedro Cunha <pedroagracio+nut@gmail.com>

  • drivers/libusb1.c: use explicit context; drop per-close libusb_exit() [#598] The original code called libusb_init(NULL) on every nut_libusb_open() and libusb_exit(NULL) on every nut_libusb_close(), bumping and decrementing the default context’s refcount. That violates the documented contract for libusb_exit, which "should be called after closing all open devices and before your application terminates", and on certain firmware it wedges indefinitely: when libusb_exit hits the 1→0 transition it tears the context down, which waits on libusb’s internal sync primitives for outstanding URBs to drain. URBs orphaned by libusb_reset_device or by an unexpected device disconnect never drain, so the wait never returns. The deadlock is reachable from any reconnect path. It was first recognized by 4f84b7f92 ("don’t libusb_exit() when closing a previously opened device"), which dropped the call in nut_libusb_open()'s rematch loop. The fallthrough reconnect path in qx_command and the rest of the driver lifecycle still hit it. With the companion change in nutdrv_qx.c (a4813f868) that escalates persistent LIBUSB_ERROR_OVERFLOW to libusb_reset_device, the deadlock window opens on every escalation and was hit reproducibly on the 0665:5161 Cypress USB-serial bridge family (Salicru SPS, Ippon, ViewPower, Voltronic Power UPSes; see #598, #993, #2453). Switch to an explicit libusb context owned end-to-end: - Initialize once on the first nut_libusb_open() and register a matching one-shot atexit handler. - Remove libusb_exit() from nut_libusb_close(); the close path now only calls libusb_close(), which is non-blocking. - Replace libusb_get_device_list(NULL, …) with the explicit context so the default context is never touched.

    • Bound the atexit libusb_exit() with a SIGALRM-based 2s timeout (POSIX) so a deadlock-prone teardown at shutdown can’t wedge supervisor stop sequences (e.g. systemctl stop). Sub-millisecond on a healthy context. Bump USB_DRIVER_VERSION from 0.53 to 0.54. Validated on a Salicru SPS 1500 ONE BL (0665:5161) by authorize-toggle stress reproduction (8 cycles, clean reconnect each time, same driver PID throughout) and by a natural OVERFLOW event captured in soak (counter-gated logic at 1/3, next poll succeeded, no escalation; driver PID stable for 22h\+).

2.129. 2026-05-22 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c, clients/authconf.h, docs/man/upscli_dump_authconf_item.txt: clients/authconf.{c,h}, docs/man/upscli_dump_authconf_item.txt: do not specify "FILE *restrict stream", some platforms think "restrict" is the variable name [#3329]
  • lib/libupsclient-config.in: this should consider LIBSSL_LDFLAGS_RPATH [#3331]
  • lib/libnutclient.pc.in: this now Requires: @LIBSSL_REQUIRES@ [#3331]

2.130. 2026-05-21 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/Makefile.am: test_authconf: consider LIBSSL flags [#3329, #1711] Builds with NSS tend to fail on some platforms due to not locating libnss3.so (which is hidden from common searches in an nss/mps subdirectory)

2.131. 2026-05-20 Pedro Cunha <pedroagracio+nut@gmail.com>

  • drivers/nutdrv_qx.c: qx_command(): reset USB device after persistent LIBUSB_ERROR_OVERFLOW [#598] LIBUSB_ERROR_OVERFLOW has been handled as a benign, retry-on-next-poll condition (grouped with LIBUSB_ERROR_TIMEOUT and the default case) ever since the original blazer import. For most devices a one-off oversized interrupt-IN frame is indeed transient. But some Cypress USB-serial bridge firmware (VID:PID 0665:5161; Salicru SPS, Ippon, ViewPower and various Voltronic Power UPSes) wedges the endpoint once it overruns: every subsequent interrupt read returns OVERFLOW and the driver spins in the stale-data loop until an external USB-level reset. This is the chronic hang reported in issue #598. Distinguish the two cases with a small consecutive-overflow counter: the first QX_USB_OVERFLOW_RESET_TRIES-1 overflows are retried on the next poll (so genuine transients cost nothing but a skipped cycle), and a sustained run escalates to usb_reset() \+ reconnect, reusing the recovery path already used for PIPE/ETIME/IO errors. Any clean read resets the counter. Captured on a Salicru SPS 1500 ONE BL: an isolated overflow recovers on the next poll, while a sustained lockup (dozens of consecutive overflows within seconds) is cleared only by the device reset, matching the behaviour of an external usb_resetter --reset-device.

2.132. 2026-05-20 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsstats.c: ups_connect(): avoid potential NULL dereference in debug/failure printouts

2.133. 2026-05-19 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, clients/upsc.c, clients/upscmd.c, clients/upsimage.c, clients/upslog.c, clients/upsrw.c, clients/upsset.c, clients/upsstats.c, docs/man/upsc.txt, docs/man/upscmd.txt, docs/man/upsimage.cgi.txt, docs/man/upslog.txt, docs/man/upsrw.txt, docs/man/upsset.cgi.txt, docs/man/upsset.conf.txt, docs/man/upsstats.cgi.txt: clients/{upscmd,upsrw,upslog,upsstats,upsset,upsimage}.c, docs/man/*.txt: add support for nutauth.conf [#3329]
  • clients/upsclient.c: upscli_cleanup(): call upscli_free_authconf_list() [#3329]
  • clients/upsclient.c, docs/man/upscli_add_host_cert.txt: refactor upscli_find_host_port_cert() with a "verbose" option, and extend upscli_add_host_port_cert() with check for existing entries [#3329]
  • clients/authconf.c, drivers/libusb0.c, include/Makefile.am, include/strcasestr-static.h: include/strcasestr-static.h, clients/authconf.c, etc.: promote code from drivers/libusb0.c to be a bit more shared [#3329] Follows up from commits b91e34edc79 and f951dce8deb
  • clients/upsclient.c: upscli_find_host_cert(), upscli_add_host_cert(): avoid "s_host" varname which confuses WIN32, illumos and other builds [#3329]
  • clients/authconf.c, docs/man/nutauth.conf.txt, tests/NIT/nit.sh: set_authconf_val(): support USERNAME as alias of USER [#3329] Follow the NUT Networked Protocol keywords a bit more closely.
  • tests/NIT/nit.sh: do not export NUT_AUTHCONF_FILE to NIT.env [#3329, #1711] Depending on context, whether "none" or a real path may be useful or toxic to custom developer test works.
  • clients/upsclient.c: AuthCertificate(), upscli_sslinit(): refactor NSS server certname validation [#3331] It is NOT about just host name (URL) matching per our spec.
  • clients/upsclient.c: revise logging from AuthCertificate*() methods [#3329]
  • tests/NIT/nit.sh: generatecfg_upsmon_add_SSL(): consider CERTHOST with non-default NUT_PORT now [#3329]
  • conf/upsmon.conf.sample.in, docs/man/upsmon.conf.txt: clarify CERTHOST with non-default NUT_PORT [#3329]
  • clients/upsclient.c: upscli_add_host_cert(): fix reporting for plain legacy "hostname" input [#3329]
  • clients/upsclient.c: BadCertHandler(): cast printing of port [#3329]
  • clients/upsclient.c: upscli_sslinit(): wrap long lines in NSS part [#3329]
  • tests/NIT/Makefile.am: check-NIT-devel: minimize nut_version.h regeneration attempts [#1711]
  • tests/NIT/nit.sh: fix passing server cert trust to NSS DB [#3331, #1711]
  • tests/NIT/nit.sh: fix passing server cert usage to NSS DB [#3331, #1711]
  • tests/NIT/nit.sh: fix passing root CA cert trust to NSS DB [#3331, #1711]

2.134. 2026-05-18 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/apc_common.c, drivers/apc_common.h: add Axel Gembe copyright to apc_common as the code is at the very least inspired by the original code from apc-modbus

2.135. 2026-05-18 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.c: upscli_find_host_port_cert(): debug-trace whether this succeeded [#3329]
  • clients/upsclient.c: upscli_add_host_cert(): revise splitting apart the "hostname" which may come from authconf section name [#3329]
  • clients/upsclient.c: upscli_init2(): quiesce reaction to NUT_QUIET_INIT_SSL=false [#1711]
  • clients/upsclient.c: upscli_init_authconf(): if the "ac" refers to a CERTHOST, call upscli_add_host_cert() on it [#3329]
  • clients/upsclient.c: upscli_init_authconf(): debug-trace the authconf pointer [#3329, #1711]
  • clients/authconf.c, clients/authconf.h, docs/man/upscli_dump_authconf_item.txt, tests/test_authconf.c: clients/authconf.{c,h}, docs/man/upscli_dump_authconf_item.txt, tests/test_authconf.c: extend upscli_dump_authconf_{list,item}() with an option to show/hide passwords [#3329]
  • NEWS.adoc, UPGRADING.adoc, clients/upsc.c, docs/man/upsc.txt: clients/upsc.c, docs: add support for "-A /path/to/nutauth.conf" [#3329, #3411]
  • clients/upsclient.c: document better the upscli_init*() and upscli_sslinit() methods [#3329]
  • clients/authconf.c: handle_authconf_args(): revise logging of parsed lines, especially malformed and sensitive ones [#3329]
  • tests/test_authconf.c: consult presence of NUT_AUTHCONF_FILE envvar to parse and print out that file [#3329, #1711]
  • tests/NIT/nit.sh: add support for nutauth.conf file testing, add a read-only user [#3329, #1711, #3411] Call new generatecfg_nutauth() after we do generatecfg_upsdusers_trivial() (and "coincidentally" in one case generatecfg_upsd_add_SSL() as well).
  • clients/upsclient.c: upscli_add_host_cert(), upscli_find_host_cert(): refactor with shared get_port_from_string() logic [#3329]
  • drivers/apcmicrolink.c: drop pragma to ignore a warning unknown to old clang [#3406]

2.136. 2026-05-17 juslex <66561713+juslex@users.noreply.github.com>

  • NEWS.adoc, data/driver.list.in, docs/man/Makefile.am, docs/man/ragtech.txt, docs/nut.dict, drivers/Makefile.am, drivers/ragtech.c, include/wincompat.h: ragtech: new driver for Ragtech UPSes (USB CDC-ACM, family 10) Targets Brazilian-built Ragtech "Easy Pro" / NEP / TORO / INNERGIE / OneUP devices that present themselves as USB CDC-ACM (VID 0x04D8, PID 0x000A, Microchip PIC firmware). Validated end-to-end against an Easy 2000 TI (reg 0x9A model id = 16) read out of a working OneUP Nitro 2000. Protocol — three opcodes observed in OEM traffic: 0x01 ADDR_HI ADDR_LO VALUE CKSUM write byte 0x02 ADDR_HI ADDR_LO MASK CKSUM AND mask (atomic bit-clear) 0x04 ADDR_HI ADDR_LO COUNT CKSUM read range CKSUM = (ADDR_HI \+ ADDR_LO + VALUE) & 0xFF The CDC-ACM channel ignores baud at the wire but DTR/RTS are interpreted by some Ragtech families as a remote shutdown signal — the driver forces both low after open and does NOT call ser_set_speed() to avoid the tcsetattr() DTR pulse that some Linux tty drivers perform. The full 30-byte main range (0x80..0x9D) plus V_IOUTCALIB (0xF3) and the oscillator calibration pair (0x202/0x203) are read; output frequency is interpolated per devices.xml formula rather than hard-coded. Twenty models from the family-10 device table populate ups.model, ups.realpower.nominal, output.voltage scaling, output.current scaling and battery voltage scaling. input.voltage.nominal switches between 115V and 220V dynamically based on the measured input. Instcmds implemented: shutdown.stayoff  — aa 02 00 80 fe (clear AUTOSTART) \+ aa 01 00 98

    1. Byte-for-byte identical to the OEM supsvc sequence. Validated: cuts output and stays off until manual power-on, in both AC and battery modes. shutdown.return  — falls back to shutdown.stayoff with a warning. The atomic OR opcode that would set F_AUTOSTART has not been captured yet; read-modify-write with 0x01 sends the bytes but does not trip the firmware’s shutdown state machine. shutdown.stop  — aa 01 00 98 00 (write V_SHUTDOWNTIMER = 0). Confirmed to abort an armed countdown. test.battery.start.deep —  bit writes on regs 0x90 / 0x95 (fullDischarge in devices.xml). Flagged as unverified for the same reason as shutdown.return. Read primitive uses ser_get_buf() in a manual loop rather than ser_get_buf_len() (which discards partial reads on timeout — CDC fragments replies into 1-8 byte chunks). ser_flush_io() is used before each TX to clear the kernel CDC buffer (ser_flush_in() only does a select\+read loop that misses queued bytes under O_NONBLOCK). Reverse engineering primarily from the OEM devices.xml table for register layout / scaling / flags / actions, and from strace of the OEM supsvc binary capturing both read polls and the LED/shutdown write sequences. Cross-checked against UPS_ESP32_tinySrv (https://github.com/antunesls/UPS_ESP32_tinySrv).

2.137. 2026-05-15 Lukas Schmid <lukas.schmid@netcube.li>

  • docs/man/apcmicrolink.txt, docs/nut.dict, drivers/apcmicrolink.c: update docs and spellcheck dictionary

2.138. 2026-05-15 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c, docs/man/upscli_read_authconf_file.txt: add support for NUT_AUTHCONF_FILE and/or NUT_AUTHCONF_PATH envvars to locate exactly one nutauth.conf candidate or bail out [#3329]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: add rudimentary support for SSLConfig_CERTHOST with a port number [#3329]
  • clients/authconf.c, clients/upsclient.c, clients/upsclient.h, docs/man/Makefile.am, docs/man/upscli_add_host_cert.txt: clients/upsclient.{c,h}, clients/authconf.c, docs: introduce upscli_add_host_port_cert() et al [#3329]
  • clients/authconf.c, tests/test_authconf.c: handle_authconf_args(): when we finish parsing a section, upscli_add_host_cert() if applicable [#3329]
  • clients/authconf.c, clients/authconf.h, docs/man/nutauth.conf.txt, docs/man/upscli_create_authconf_item.txt: clients/authconf.{c,h}, docs/man/upscli_create_authconf_item.txt, docs/man/nutauth.conf.txt: add CERTHOST field support [#3329]

2.139. 2026-05-14 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/Makefile.am: add missing apc_common.h to distributed headers
  • drivers/apcmicrolink-maps.c, drivers/apcmicrolink-maps.h, drivers/apcmicrolink.c: extract descriptor helpers and fix map includes

2.140. 2026-05-14 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c: upscli_get_authconf_item(): add ifdef-ed away very optional debug messages [#3329]
  • tests/test_authconf.c: print TAP-style progress trackingl add tests for upscli_get_authconf_item() modification of the list [#3329]
  • clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/upscli_find_authconf_item.txt, tests/test_authconf.c: clients/authconf.{c,h}: introduce upscli_get_authconf_item() for auto-merged items [#3329]
  • docs/man/Makefile.am: fix after reshuffle nutauth.conf related methods vs population of man pages [#3329]
  • clients/authconf.c, docs/man/upscli_create_authconf_item.txt: revise upscli_merge_authconf_item() function vs. cloning of empty strings [#3329]
  • clients/authconf.c, docs/man/upscli_create_authconf_item.txt: revise upscli_clone_authconf_item() and upscli_merge_authconf_item() functions vs. cloning of USER name [#3329]
  • clients/authconf.c: refactor with separation of upscli_add_authconf_item(name) and upscli_add_authconf(node) [#3329]
  • clients/authconf.c: upscli_find_authconf_item(): refine return of global_defaults and zearch in empty list [#3329]

2.141. 2026-05-13 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/apcmicrolink.c, drivers/apcmicrolink.h: fix switch warnings and replace flag bit-fields

2.142. 2026-05-13 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/upscli_create_authconf_item.txt, docs/man/upscli_get_authconf_list.txt: clients/authconf.{c,h}, docs: introduce upscli_clone_authconf_item() and upscli_merge_authconf_item() functions [#3329]
  • clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/nutauth.conf.txt, …uthconf.txt ⇒ upscli_create_authconf_item.txt}, …_authconf.txt ⇒ upscli_dump_authconf_item.txt}, …_authconf.txt ⇒ upscli_find_authconf_item.txt}, docs/man/upscli_free_authconf_list.txt, docs/man/upscli_get_authconf_list.txt, docs/man/upscli_init.txt, …_authconf.txt ⇒ upscli_read_authconf_file.txt}, tests/test_authconf.c: clients/authconf.{c,h}, tests/test_authconf.c, docs/man/auth, docs/man/Makefile.am: rename file/list/item/section methods consistently with their purpose [#3329]
  • clients/authconf.c: upscli_create_authconf(): handle failed calloc() by returning NULL; fail fatally in upscli_add_authconf() if needed [#3329]
  • docs/man/Makefile.am, docs/man/upscli_create_authconf.txt, …_authconf_list.txt ⇒ upscli_dump_authconf.txt}, docs/man/upscli_find_authconf.txt, docs/man/upscli_free_authconf_list.txt, docs/man/upscli_get_authconf_list.txt: docs/man/auth.txt, docs/man/Makefile.am: reshuffle nutauth.conf related methods vs population of man pages [#3329]
  • clients/authconf.c, docs/man/nutauth.conf.txt: revise section title parsing, INCLUDE handling, naming of [_global_defaults], code comments etc. [#3329]
  • clients/authconf.c, clients/authconf.h: clients/authconf.{c,h}: refactor static upscli_add_authconf() with a public upscli_create_authconf() [#3329]
  • clients/authconf.c: fix static analysis warnings [#3329]
  • tests/test_authconf.c: add a Non-exact match test case [#3329]
  • clients/authconf.c: refactor upscli_find_authconf() with upscli_normalize_auth_section_parts() [#3329]
  • NEWS.adoc, drivers/nutdrv_qx_megatec.c: drivers/nutdrv_qx_megatec.c: add QX_FLAG_ABSENT to "ups.firmware" so devices with all-spaces in that field can still be recognized [#3436] Co-authored-by: Jim Klimov <jimklimov\+nut@gmail.com> Co-authored-by: Siravijbb <siravijbb@gmail.com>

2.143. 2026-05-12 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/apc_modbus.c: remove unused function

2.144. 2026-05-12 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/upscli_find_authconf.txt: clients/authconf.{c,h}, docs: further refactor with upscli_normalize_auth_section_parts() which we can share and expose [#3329]
  • clients/authconf.c: handle_authconf_args(): revise section title line parsing [#3329]
  • clients/authconf.c: upscli_splitauth(): reject empty non-NULL sect_name [#3329]
  • clients/authconf.c: upscli_splitauth(): if "port" is a non-numeric string, try to resolve it in the system naming database [#3329]
  • scripts/obs/debian.nut-client.install, scripts/obs/debian.nut-client.manpages, scripts/obs/nut.spec: know about nutauth.conf samples [#3329]
  • conf/nutauth.conf.sample.in, configure.ac, conf/Makefile.am, conf/.gitignore: add example config for nutauth.conf [#3329]
  • tests/test_authconf.c: add a dev-test for discovery of user/site nutauth configs [#3329]
  • tests/test_authconf.c: honour NUT_DEBUG_LEVEL [#3329]
  • clients/authconf.c, clients/authconf.h, docs/man/upscli_read_authconf.txt: clients/authconf.{c,h}, docs/man/upscli_read_authconf.txt: add searching for default nutauth.conf in user home or NUT_CONFPATH locations [#3329]
  • clients/upsclient.c, clients/upsclient.h, docs/man/Makefile.am, docs/man/upscli_init.txt: clients/upsclient.{c,h}, docs/man/upscli_init.txt: introduce upscli_init_authconf() [#3329]
  • clients/authconf.c: parse_authconf_file(): call check_perms(filename) [#3329, #3359]
  • common/common.c, include/common.h, server/upsd.c, server/upsd.h, server/user.h: common/common.c et al: relocate check_perms() from upsd to common code base [#3359]
  • clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/upscli_find_authconf.txt, docs/nut.dict: clients/authconf.{c,h}, docs/man/upscli_find_authconf.txt: refactor with upscli_split_auth_section() [#3329]
  • clients/authconf.c, clients/authconf.h, docs/man/upscli_dump_authconf_list.txt, tests/test_authconf.c: clients/authconf.{c,h}, tests, docs: extend upscli_dump_authconf{,_list} methods with "for_debug" option [#3329]
  • tests/test_authconf.c: drop "Mocks for functions usually provided by libcommon" [#3329]
  • common/common.c: cosmetic fix
  • docs/Makefile.am: add ASPELL_NUT_COMMON_ARGS to preclude mangling of nut.dict on some platforms when checking interactively

2.145. 2026-05-12 Doug Nazar <nazard@nazar.ca>

  • common/common.c, drivers/main.c, include/common.h: drivers/main.c: Split backgrounding driver into two stages This is needed in particular with libusb on linux as it will create a thread to handle either netlink or udev messages. If this is created before we background, the driver will hang during exit trying to join the non-existant thread in the new process. Shows this warning while stopping the driver. Stopping /run/nut/usbhid-ups-xxxx.pid failed, retrying harder: Success

2.146. 2026-05-11 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/apcmicrolink.c: fix commands and writes sometimes not working and allow configuration of command source
  • drivers/Makefile.am, drivers/apc_common.c, drivers/apc_common.h, drivers/apc_modbus.c, drivers/apc_modbus.h, drivers/apcmicrolink-maps.c, drivers/apcmicrolink-maps.h, drivers/apcmicrolink.c: Variable cleanup and commons creation between apc-modbus and microlink

2.147. 2026-05-11 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc, clients/Makefile.am, clients/authconf.c, clients/authconf.h, docs/man/Makefile.am, docs/man/nutauth.conf.txt, docs/man/upscli_dump_authconf_list.txt, docs/man/upscli_find_authconf.txt, docs/man/upscli_get_authconf_list.txt, docs/man/upscli_read_authconf.txt, docs/nut.dict, tests/Makefile.am, tests/test_authconf.c: Introduce NUT "authconf" file support [#3329] First PoC from AI, slightly modified in review, following the spec requested in the GitHub issue. Stepping stone for further work.
  • server/upsd.c: update_sysmaxconn(): default the sysmaxconn value to "MAX - RESERVE_FD_COUNT_UPSD" on platforms where it might matter [#3365]
  • server/upsd.c: update_sysmaxconn(): check that detected sysmaxconn_hard is big enough to be useful considering the RESERVE_FD_COUNT_UPSD set aside AND a few actual connections [#3365]
  • server/upsd.c: update_sysmaxconn(): revise warning message about hard limit [#3365]
  • server/upsd.c: be sure to re-detect sysmaxconn after (re-)loading config [#3365]
  • NEWS.adoc, UPGRADING.adoc, docs/nut.dict, server/upsd.c, server/upsd.h: server/upsd.{c,h}, NEWS.adoc, UPGRADING.adoc: consider getrlimit() permissons vs. MAXCONN request [#3365]
  • NEWS.adoc, server/conf.c, server/upsd.c, server/upsd.h: server/conf.c, server/upsd.{c,h}, NEWS.adoc: if we hit "Too many open files" during configuration reload, close oldest client connection and retry [#3365]
  • server/upsd.h: update © heading and whitespace styling
  • docs/nut.dict: update for dllldd [#3420]
  • NEWS.adoc: update about scripts/Windows/dllldd.sh helper script refinements [#3420]
  • docs/man/upscli_add_host_cert.txt: update that this call works also with OpenSSL (since NUT v2.8.6) [#3331]
  • docs/download.txt, docs/nut.dict: Upload NUT-for-Windows-x86_64-RELEASE-2.8.5-1-fixNSS.7z with NSS dependency libraries missing in original NUT v2.8.5 release [#3420]
  • appveyor.yml: allow ln to create hard links, and tell 7-zip to store them as such, not as independent files (subject to 7zip format du jour)
  • scripts/Windows/dllldd.sh: Revise use of TEMPFILE_REC to avoid grep if it is empty [#3420]
  • Makefile.am: install-win-bundle-thirdparty: revise progress message markup for easier troubleshooting [#3420]
  • scripts/Windows/dllldd.sh: dllldd_with_strings(): filter away OUT_TOOLS hits if known [#3420]
  • scripts/Windows/dllldd.sh: dllldd_with_strings(): quick bail-out with TEMPFILE_REC [#3420]
  • scripts/Windows/dllldd.sh: revise cleanup of TEMPFILE_REC; create it earlier in dllldddir() [#3420]
  • scripts/Windows/dllldd.sh: dllldd(): use TEMPFILE_REC if defined by caller [#3420]
  • scripts/Windows/dllldd.sh: dllldd_with_tools(), dllldd(): optimize looped iteration of search paths we prepared [#3420] Avoid calls to tr ':' '\n' that can be done once.
  • scripts/Windows/dllldd.sh: relocate TEMPFILE_REC declaration to before first use-mention [#3420]
  • scripts/Windows/dllldd.sh: filter_away_system_DLLs(): add some more OS-provided common hits to exclusion list [#3420]
  • scripts/Windows/dllldd.sh: filter_away_system_DLLs(): add DWrite.dll [#3420] Some OS releases have Microsoft DirectWrite for high-quality text rendering, anti-aliasing, and font management… not something we really care about in NUT, and part of OS footprint we do not drill into.
  • scripts/Windows/dllldd.sh: dllldd_with_tools(), dllldd(): optimize SEARCH_INPUT_PATH discovery [#3420] Instead of a loop with dirname calls (spawning those processes takes a few seconds in MSYS2 on Windows), use one sed process.
  • scripts/Windows/dllldd.sh: dllldd_with_strings(): ignore also "lib%s.dll" hits [#3420]
  • scripts/Windows/dllldd.sh: accept also outputs from "objdump" variants that return "NEEDED libname" [#3420]
  • scripts/Windows/dllldd.sh: when given a CLI argume t, check if it is a directory (to run dllldddir) before diving in with dlllddrec [#3420]
  • scripts/Windows/dllldd.sh: accept also outputs from "ldd" variants that only return path (no parenthesis with hex) [#3420]
  • scripts/Windows/dllldd.sh: allow to parameterize DLLEXT_REGEX [#3420] At least, allow to test/iterate the script with non-Windows builds too.
  • scripts/Windows/dllldd.sh: dllldd_with_strings(): filter away some bogus strings outputs and findings [#3420]
  • scripts/Windows/dllldd.sh: more filter_away_system_DLLs() [#3420]

2.148. 2026-05-10 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/Windows/dllldd.sh: dllldd_with_tools(): define and clean-up TEMPFILE_REC in the outer-most call if we stack them [#3420] This should help directory crawlers etc. ignore paths already walked by some previous recursion in this scripted execution.
  • scripts/Windows/dllldd.sh: dllldd_with_tools(): do not bail out if we found some but not all DLLs via objdump; feed only the names we did not resolve to ldd [#3420]
  • scripts/Windows/dllldd.sh: consider also SEARCH_INPUT_PATH based on where the input EXE/DLL files are [#3420] This allows to find libnutprivate*.dll or libupsclient*.dll files we built and installed to EXE bindir, which were otherwise ignored by earlier path discovery tools and/or logic.
  • scripts/Windows/dllldd.sh: filter_away_NUT_DLLs() when we query both dllldd_with_tools() and dllldd_with_strings() [#3420]
  • scripts/Windows/dllldd.sh: refactor with discover_COMPILER_PATHS() to not do this in a loop [#3420]

2.149. 2026-05-09 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/nut.dict: Update nut.dict [#3430]

2.150. 2026-05-08 ostrich <570911+ostrich@users.noreply.github.com>

  • scripts/python/app/NUT-Monitor-py3qt5.in, scripts/python/app/NUT-Monitor-py3qt6.in: Fix NUT-Monitor tray tooltip text

2.151. 2026-05-08 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: update about NUT-Monitor tooltips [#3430]

2.152. 2026-05-07 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsmon.c, NEWS.adoc: do not abort if we did not succeed upscli_init() but did not require SSL in the first place [#3420]
  • scripts/Windows/dllldd.sh: refactor to find DLLs with strings too; track filenames we already investigated [#3420] DRY the loop and avoid cycles.

2.153. 2026-05-06 Jim Klimov <jimklimov+nut@gmail.com>

  • m4/nut_check_libnetsnmp.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libavahi.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • ci_build.sh, scripts/Windows/build-mingw-nut.sh: get_CI_CACHE_NUT_HASHDIR_CFG_OPT(): consider also MAKE and (CONFIG_)SHELL as circumstances that impact the configuration hash [#3108]

2.154. 2026-05-05 Jim Klimov <jimklimov+nut@gmail.com>

  • m4/nut_check_libfreeipmi.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libgd.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libglib.m4, configure.ac: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libgpiod.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libmodbus.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libneon.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libltdl.m4: do not drag around nut_have_libltdl in cached section, use nut_cv_have_libltdl right away [#3108]
  • m4/nut_check_libopenssl.m4, m4/nut_check_libnss.m4, configure.ac: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libpowerman.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libusb.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_os.m4: fix comments starting a line with a hash to use "dnl" first

2.155. 2026-05-04 Jim Klimov <jimklimov+nut@gmail.com>

  • m4/nut_check_libregex.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libsystemd.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_libwrap.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_pkgconfig.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • m4/nut_check_os.m4: set OS_NAME and PKG_TARGET after passing AC_CACHE_CHECK [#3108]
  • m4/nut_check_libltdl.m4: do not reset nut_have_libltdl=yes when checking for header from cached data [#3108]
  • m4/nut_check_libltdl.m4: add a summary print-out for cached re-runs [#3108]
  • common/common.c: upsnotify(): fix "unused" warnings in different build combos
  • ci_build.sh, ci_build.adoc: when we DO_USE_NUTCI_CACHE_DEBUG, stash also the help text from configure script [#3108]
  • m4/nut_check_pkgconfig.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libwrap.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libusb.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libsystemd.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libregex.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libpowerman.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libopenssl.m4, m4/nut_check_libpowerman.m4: m4/nut_check_libopenssl.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libnss.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libnetsnmp.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libneon.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libmodbus.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libgpiod.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libglib.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libgd.m4: fix indentations, use AS_IF and AS_CASE more [#3108]
  • m4/nut_check_libfreeipmi.m4: fix indentations, use AS_IF [#3108]
  • m4/nut_check_libavahi.m4: fix indentations, use AS_IF [#3108]
  • m4/nut_check_cppcheck.m4: fix indentations, use AS_IF and AC_CACHE_VAL [#3108]
  • m4/nut_check_bool.m4: fix indentations [#3108]
  • m4/nut_check_aspell.m4: fix indentations, use AS_IF [#3108]
  • m4/nut_check_asciidoc.m4: fix indentations, use AS_IF [#3108]
  • m4/nut_check_libltdl.m4: fix indentations, use AS_IF for outer check [#3108]
  • m4/nut_check_os.m4: fix typo in heading
  • m4/nut_check_libltdl.m4: revise with AC_CACHE_VAL() for each option to be remembered [#3108]
  • Makefile.am: when we touch -d "-10 seconds", hide the errors (such request is known to be not too portable)
  • ci_build.sh, scripts/Windows/build-mingw-nut.sh: facilitate re-runs with changed .m4/.am/.ac sources and enabled autoconf cache: handle precious CCACHE_ args/vars [#3108]
  • ci_build.adoc: clarify some points about caching [#3108]

2.156. 2026-05-02 Jim Klimov <jimklimov+nut@gmail.com>

  • ci_build.sh, ci_build.adoc: introduce DO_USE_AUTOCONF_CACHE_DEBUG=each mode [#3108]

2.157. 2026-05-01 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: move entry for #3302 into v2.8.6 release

2.158. 2026-04-30 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/config-prereqs.txt: do not pull valgrind into default Debian/Ubuntu footprint, it is not ubiquitous Revise other suggestions a bit too

2.159. 2026-04-29 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/cpputest-client.cpp: revise reporting of UPSCLI_SSL_CAPS* [#3331, #1711]
  • clients/nutclient.cpp, clients/nutclient.h, clients/upsclient.c, clients/upsclient.h, docs/man/upscli_ssl_caps.txt: upsclient, nutclient, docs: introduce UPSCLI_SSL_CAPS_CERTHOST* [#3331]
  • tests/NIT/nit.sh: consider if we can not do CERTHOST validation [#3331]
  • clients/upsclient.c: report if we can not do CERTHOST(name) validation [#3331]
  • Makefile.am: .libs-dev-PATH: fix typo

2.160. 2026-04-28 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/Makefile.am, common/Makefile.am, scripts/obs/debian.rules, scripts/obs/nut.spec, tools/nut-scanner/Makefile.am: /Makefile.am, scripts/obs/: bump libnutclient ABI level [#1290, #3331]
  • clients/nutclient.cpp: drop obsolete FIXME comments [#3331]
  • clients/nutclient.cpp: with openssl builds, do not require non-trivial certhost_name IF we do not have methods to check it (openssl too old) [#3331]
  • tests/NIT/nit.sh: refactor by use of check_NIT_certs_NSS() [#1711]
  • tests/NIT/nit.sh: introduce check_NIT_certs_NSS() [#1711]
  • tests/cpputest-client.cpp: enable testing for CERTHOST with OpenSSL builds, update argument order for NSS builds [#3331, #1711]
  • clients/Makefile.am, clients/nutclient.cpp, clients/nutclient.h, docs/man/libnutclient_tcp.txt: clients/nutclient.{cpp,h}, docs, Makefile.am: fix SSLConfig_NSS related argument order to place certhost after certident, to match logical proximity and OpenSSL equivalents; reset ABI age (not backwards compatibleto recent v2.8.5 release) [#3331]
  • tests/NIT/nit.sh: refactor by use of check_NIT_certs_NSS() [#1711, #3390, #3108]
  • tests/NIT/nit.sh: introduce check_NIT_certs_NSS() [#1711]
  • tests/NIT/nit.sh: revise sanity-check, not all NSS versions had a *.txt file [#3331, #1711]
  • tests/NIT/nit.sh: consider WITH_SSL_CLIENT_CERTIDENT=none to (re)set certificate nickname verification [#3331, #1711]
  • tests/NIT/nit.sh: reword SSL support messages [#1711]
  • tests/NIT/nit.sh: revise sanity-check, not all NSS versions had a *.txt file [#3331, #1711]

2.161. 2026-04-27 Jim Klimov <jimklimov@gmail.com>

  • clients/nutclient.cpp: fix NULL⇒nullptr [#3331]
  • tests/NIT/nit.sh: isTestablePython(): fix detection of interpreter shebang via /usr/bin/env [#1711]
  • tests/NIT/nit.sh: isTestablePython(): fix detection of interpreter shebang via /usr/bin/env [#1711]
  • tests/NIT/nit.sh: refactor and improve portability with somehash_filter()/somehash_files() method detection [#3390]
  • ci_build.sh: refactor and improve portability with somehash_filter()/somehash_files() method detection [#3108]

2.162. 2026-04-27 Jim Klimov <jimklimov+nut@gmail.com>

  • .circleci/config.yml: allow various cache contents for same branch/job iterations [#3390]
  • tests/NIT/nit.sh: introduce check_NIT_certs() to ensure availability of all expected certs for NSS or OpenSSL scenarios [#3390]
  • Makefile.am: .libs-dev-PATH: adjust for MacOS [#1711]
  • tests/cpputest-client.cpp: report also NUT_PORT [#1711, #3331]
  • tests/cpputest-client.cpp: fix reporting of incoming envvars [#1711, #3331]
  • clients/upsclient.c, clients/nutclient.cpp: accept systems where openssl does not define X509_V_ERR_HOSTNAME_MISMATCH [#3331]
  • clients/upsclient.c, clients/nutclient.cpp: use pragmas to hide unreachable code warnings when built against openssl without X509 checking methods we use [#3331]
  • ci_build.sh, tests/NIT/nit.sh: introduce DO_USE_NUTCI_CACHE_DEBUG to stash optional items not needed in production use of the cache, but rather to track its regressions on re-runs etc. [#3108]
  • Makefile.am: use a more portable date spec in touch for start of Epoch
  • configure.ac: properly use AC_CACHE_VAL for BSDKVMPROCLIBS [#3108]
  • m4/nut_check_libltdl.m4: re-detect ltdl.h details if we needed to retry, to not lose track [#3108]
  • tests/NIT/nit.sh: fix NUT_CAPATH for PERL SSL tests on Windows (MSYS2) yet some more [#1711]
  • tests/NIT/nit.sh: fix NUT_CAPATH for PERL SSL tests on Windows (MSYS2) with forward slashes too [#1711]
  • tests/NIT/nit.sh: fix NUT_CAPATH for PERL SSL tests on Windows (MSYS2) with forward slashes too [#1711]
  • tests/NIT/nit.sh: fix portability for recursive "cp" [#3390]

2.163. 2026-04-26 Jim Klimov <jimklimov+nut@gmail.com>

  • m4/nut_check_libnetsnmp.m4: re-detect libnetsnmp details in mingw builds to not lose track [#3108]
  • scripts/Windows/build-mingw-nut.sh: CI_CACHE_NUT_HASHDIR_CFG and configure_nut(): stash a copy of first-run config.log and config.h as examples of resulting configuration [#3108]
  • scripts/Windows/build-mingw-nut.sh: add © into the heading
  • ci_build.sh: get_CI_CACHE_NUT_HASHDIR_CFG_OPT(), configure_nut(): stash a copy of first-run config.log and config.h as examples of resulting configuration [#3108]

2.164. 2026-04-25 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/download.txt: fix NOTE block style
  • ci_build.sh, Makefile.am: revise passing CI_CACHE_NUT_HASHDIR_CFG_OPT into distcheck runs [#3108]
  • ci_build.sh: consider_cleanup_shortcut(): do not fail if there is no out-of-tree CI_BUILDDIR/Makefile.in - normally it should not be there (but do check for one in SCRIPTDIR)
  • ci_build.sh: refactor get_CI_CACHE_NUT_HASHDIR_CFG_OPT() into a method, handle distcheck builds too [#3108]
  • NEWS.adoc: update about NIT caching of test certificates [#3390]
  • tests/NIT/README.adoc, docs/nut.dict: document caching [#3108, #3390, #1711]
  • appveyor.yml, .circleci/config.yml: generalize to DO_USE_NUTCI_CACHE, not just DO_USE_AUTOCONF_CACHE [#3108, #3390]
  • ci_build.adoc, docs/nut.dict: document caching [#3108]
  • ci_build.sh, tests/NIT/nit.sh: generalize DO_USE_NUTCI_CACHE so AUTOCONF and NIT_TESTCERT toggles may be separate; drop old logic for config.cache in build area; rename CLEAN vars to …_BEFORE [#1711, #3390]
  • tests/NIT/nit.sh: handle DO_CLEAN_AUTOCONF_CACHE [#3390]
  • tests/NIT/nit.sh: fix preparation of PEM and JKS client key/cert from OpenSSL [#1711, #3390] Move code that landed into a wrong case.
  • tests/NIT/nit.sh: refactor preparation of PEM and JKS server key/cert from OpenSSL [#1711, #3390] Handle a situation with new openssl defaults and old(er) java keytool.
  • tests/NIT/Makefile.am, tests/NIT/nit.sh: name the default WITH_SSL_TESTS=best-effort behavior for clarity [#1711]
  • ci_build.sh, scripts/Windows/build-mingw-nut.sh: ci_build.sh: name CI_CACHE_NUT_HASHDIR with AUTOCONF prefix [#3108]
  • tests/NIT/nit.sh: prepare_NIT_certs(): rename TESTCERT_VARS.sh to .env, generate one [#3390]

2.165. 2026-04-24 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/NIT/nit.sh: fix NUT_CAPATH for PERL SSL tests on Windows (MSYS2) [#1711]
  • tests/NIT/nit.sh: prepare_NIT_certs(): for caching, also generate NSS DB from OpenSSL PEM if we can [#3390]
  • tests/NIT/nit.sh: prepare_NIT_certs(): drop obsolete comment [#3390]
  • tests/NIT/nit.sh: prepare_NIT_certs(): extend validity of generated test certificates to 20 years [#3390]
  • tests/NIT/nit.sh: prepare_NIT_certs(): add generation of JKS (and more PEM) from NSS, but only if we plan to cache the result [#3390]
  • tests/NIT/nit.sh: prepare_NIT_certs(): wrap long command lines [#3390]
  • tests/NIT/nit.sh: refactor mock cert population logic as prepare_NIT_certs(), and allow caller-provided or automatically cached mocks [#3390]
  • appveyor.yml: tweak notification triggers again [#1400]
  • tests/NIT/nit.sh: fix NUT_CAPATH for PERL SSL tests on Windows (MSYS2) [#1711]
  • NEWS.adoc, clients/nutclient.cpp, clients/nutclient.h, clients/upsclient.c, conf/upsmon.conf.sample.in, docs/man/libnutclient_tcp.txt, docs/man/upscli_add_host_cert.txt: clients/{nut,ups}client, docs: make sure CERTHOST values for FORCESSL/CERTVERIFY are used (C+\+), allow inheriting global setting via -1 [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: in TcpClient, track the applied SSLConfig* vs. build(/run)time ability to use NSS (and/)or OpenSSL via UPSCLI_SSL_CAPS* and consider that in getter preferences [#3331]
  • clients/nutclient.cpp, clients/nutclient.h, docs/man/libnutclient_commands.txt, docs/man/libnutclient_tcp.txt: clients/nutclient.{cpp,h}, docs/man/libnutclient_tcp.txt: add optional CERTHOST verification to OpenSSL code path [#3331]

2.166. 2026-04-23 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/nutclient.cpp: some builds manage to get into WITH_SSL_CXX without WITH_OPENSSL|\|WITH_NSS defined [#3331]
  • m4/nut_check_libopenssl.m4, clients/upsclient.c, clients/nutclient.cpp, server/netssl.c: openssl_cert_verify_san_name(): support OpenSSL versions before ASN1_STRING_get0_data() and potentially related helpers [#3331]
  • ci_build.sh: comment about the life-cycle of config.cache file [#3108]
  • ci_build.sh: align defaults for DO_USE_AUTOCONF_CACHE and DO_CLEAN_AUTOCONF_CACHE with existence of CI_CACHE_NUT_BASEDIR [#3108]
  • scripts/Windows/build-mingw-nut.sh: add support for CI_TRACE envvar toggle, like in ci_build.sh
  • ci_build.sh, scripts/Windows/build-mingw-nut.sh: pass CI_CACHE_NUT_HASHDIR into Windows builds if we want to and can [#3108]
  • NEWS.adoc, appveyor.yml: NEWS.adoc: ability to optionally re-use a config.cache file from older runs moved to NUT v2.8.6 [#3108]
  • appveyor.yml, .circleci/config.yml: cache the out-of-tree locations for config.cache [#3108]
  • ci_build.sh: support reusable config.cache per "uname \+ script config opts" hash [#3108] Now even several build environments sharing a source directory with a generated configure script should be able to safely use their individual caches.
  • ci_build.sh: in developer mode (no BUILD_TYPE) utilize make all-quick for less noise
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: refactor SSLConfig_CERTIDENT_NSS to not construct with a separate certstore_pass [#3331] NSS only has one pass phrase (shared for all private key database entries)
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: refactor TcpClient and Socket to pass around SSLConfig(-derived) instances instead of private fields [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: SSLConfig: add getCertHostBy*() methods [#3331]
  • clients/nutclient.cpp: clients/nutclient.{cpp,h}: SSLConfig_*::apply(): prepare and hide debug printouts [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: SSLConfig_CERTIDENT fix polymorphic-friendly reference to _certstore [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclientmem.{cpp,h}: use _empty_str in constructors, just to be sure we have a valid reference to an immortal std::string where we expect one [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: SSLConfig*: introduce clone() methods to own data in our sets of CERTHOST/CERTSTORE/CERTIDENT and delete it properly [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: SSLConfig: in the sets of CERTHOST/CERTSTORE/CERTIDENT data, retain polymorphic-friendly pointers instead of mutilated auto-casted objects [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: SSLConfig::addCertHost(): constrain to adding non-trivial entries only [#3331]

2.167. 2026-04-22 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.{cpp,h}: move SSLConfig* method implementations from header to CPP file [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.h: refactor SSLConfig_NSS class to use new storage classes [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.h: refactor SSLConfig_OpenSSL class to use new storage classes [#3331]
  • clients/nutclient.h: extend base SSLConfig class to keep track of optional CERTSTORE, CERTIDENT and/or CERTHOST info [#3331]
  • clients/nutclient.cpp, clients/nutclient.h: clients/nutclient.h: introduce SSLConfig* classes to keep CERTSTORE locations and credentials, and CERTIDENT and CERTHOST info [#3331]
  • clients/nutclient.h: streamline code style for SSLConfig* classes [#3331]
  • clients/nutclient.cpp: Socket() constructor should not rely on openssl_cert_verify_data struct to be pre-zeroed [#3331] OBS builds, and later valgrind, confirmed an issue with random decisions and free()'s based on random data in that struct. Apparently C differs from C+\+ in that aspect?..

2.168. 2026-04-21 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/usbhid-ups.c, NEWS.adoc: reconnect_ups(): report success more visibly [#3423] Also fix setting "quiet" driver status when we actualy remain in reconnecting state (or rather will soon re-enter it because situation is not fixed)
  • clients/nutclient.cpp, clients/upsclient.c, server/netssl.c: openssl_cert_verify_san_name(): revise loop based on return from *printf() [#3331]
  • clients/nutclient.cpp, server/netssl.c: fix CI warnings [#3331]
  • UPGRADING.adoc, clients/nutclient.cpp, clients/nutclient.h, docs/man/Makefile.am, docs/man/libnutclient_tcp.txt, tests/NIT/nit.sh, tests/cpputest-client.cpp: clients/nutclient.{cpp,h}, tests/cpputest-client.cpp, nit.sh, docs: a CERTHOST address was missing (and subject/nickname is not the host name/url that NSS checks for) [#3331, #1711]
  • clients/nutclient.cpp, clients/nutclient.h, docs/man/libnutclient_tcp.txt: clients/nutclient.{cpp,h}: rename "forcessl" to match upsclient.c, and "tryssl" to not stand out [#3331]
  • tests/cpputest-client.cpp: honour NUT_DEBUG_LEVEL by enabling (Socket Connect) debug toggle [#1711]
  • tests/NIT/nit.sh: support passing individual NUT_DEBUG_LEVEL_{PERL,PYTHON,CPPNIT,NUTSCAN} [#1711]
  • server/netssl.c, clients/upsclient.c, clients/nutclient.cpp: align messages from NSS AuthCertificate() callbacks [#3331]
  • clients/nutclient.cpp: Socket::startTLS(): if NSS handshake fails, add ssl_url to the exception message [#3331]
  • clients/nutclient.cpp: setSSLConfig_*(): debug-log the code path actually taken [#1711]
  • clients/nutclient.cpp: Socket: document the SSL fields better [#3331]
  • clients/nutclient.cpp: TcpClient::connect(): now we can config run-time for both NSS and OpenSSL, most options are shared and built-in abilities drive the final choice [#3331]
  • tests/cpputest-client.cpp: setupClientSSL(): rearrange reporting of enabled SSL options [#1711]

2.169. 2026-04-20 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/NIT/nit.sh: setenv_ssl_cppnit(): provide NUT_CERTFILE and NUT_KEYFILE values for OpenSSL tests [#1711, #3331]
  • clients/nutclient.cpp: transplant openssl_cert_verify_callback() et al from upsclient.{c,h} [#3331]
  • clients/nutclient.cpp: Socket::startTLS(): some reported CN values (at least from openssl parser) may include escaped slashes as a separator [#3331]
  • clients/nutclient.cpp: fix syntax for NSS builds [#3331, #1711]
  • configure.ac: fix the checks for enabling SSL support in C+\+ builds [#1711, #3331]
  • server/netssl.c: ssl_init(): some reported CN values (at least from openssl parser) may include escaped slashes as a separator [#3331]
  • server/netssl.c, server/nut_ctype.h: transplant openssl_cert_verify_callback() et al from upsclient.{c,h} [#3331]
  • clients/upsclient.c, clients/upsclient.h: clients/upsclient.{c,h}: revise code/comment markup for recent changes [#3331]
  • tests/NIT/nit.sh: ensure SAN aliases (DNS and IP) that should NOT match the current host [#1711]
  • clients/upsclient.c: upscli_init2(): some reported CN values (at least from openssl parser) may include escaped slashes as a separator [#3331]
  • clients/upsclient.c: openssl_cert_verify_san_name(): use X509_check_host()/X509_check_ip_asc() where available [#3331]
  • clients/upsclient.c: openssl_cert_verify_callback(): consistently report depth along with CN (counterpart, CA…) [#3331]
  • clients/upsclient.c: openssl_cert_verify_callback(): do not call and log openssl_cert_verify_san_name() when already OK per caller [#3331]
  • clients/upssched.c: hush down byte-by-byte read tracing (away from common debug level 6)
  • clients/upsclient.c, clients/upsclient.h: clients/upsclient.c: convert openssl_cert_print_san_name() into openssl_cert_verify_san_name() [#3331] Also persist openssl_cert_verify_data_t in UPSCONN_t so it is always available to callbacks when needed for this ups instance.
  • clients/upsclient.c: openssl_cert_print_san_name(): handle IPv4/IPv6 addresses in SAN [#3331]
  • clients/upsclient.c: rename example "mydata" into "openssl_cert_verify_data" (types, vars) and example methods to print/verify certs [#3331]
  • clients/upsclient.c, configure.ac: import openssl doc examples for certificate reporting/inspection into NUT styling and logging [#3331]
  • clients/upsclient.c: fix markup about switch() so it is consistent in the file [#3331]
  • clients/nutclient.cpp, clients/upsclient.c, server/netssl.c: server/netssl.c,clients/upsclient.c, clients/nutclient.cpp: when we verify subject_CN[], consider comma as a separator too [#3331]
  • clients/nutclient.cpp: fix a missed static_cast [#3331]

2.170. 2026-04-19 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/nutclient.cpp: be sure to include openssl/x509v3.h for methods we use [#3331]

2.171. 2026-04-18 Tom Eldon <tom@tomeldon.com>

  • drivers/apcmicrolink.c, drivers/apcmicrolink.h: drivers/apcmicrolink.c: minor handshake fix, allow flexible frame size Change to allow flexible frame size (upto 256 bytes) by waiting for page0, as some devices with payload size != 35 were incompatible. Allow variable length serial number string during handshake (as some devices use 14 characters).

2.172. 2026-04-18 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/apc_modbus.c: addvar() the modbus_retries option [#3414]

2.173. 2026-04-16 Jim Klimov <jimklimov+nut@gmail.com>

  • tests/NIT/nit.sh: setenv_ssl_common(): revise NUT_CAPATH conditions [#1711]
  • tests/NIT/nit.sh: fix check of python FORCESSL is testable [#1711, #1349]

2.174. 2026-04-15 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/PyNUT.py.in: fix sleep() [#1349, #1711]
  • clients/nutclient.cpp, clients/nutclient.h, clients/upsclient.c, clients/upsclient.h, docs/man/libnutclient_tcp.txt, docs/man/upscli_ssl_caps.txt, server/netssl.c, tests/NIT/nit.sh: Further differentiate reported CERTIDENT_NAME/_PASS capability [#1711]
  • tests/NIT/nit.sh: refactor with setenv_ssl_common(), and in setenv_ssl_python() verify that we can run SSL tests (else do not force them) [#1349, #1711]
  • scripts/python/module/PyNUT.py.in: detect inability to use SSL module (older interpreter etc.) [#1349, #1711]
  • tests/NIT/nit.sh: align generation of CERTIDENT into upsd.conf/upsmon.conf with ability announced by the binaries [#1711]
  • scripts/perl/test_nutclient.pl: ANSI colors are a nice to have, do not crash if absent [#1711]
  • scripts/perl/UPS/Nut.pm: StartTLS(): use a better portable "or" for "undef" printing [#1711]
  • scripts/perl/test_nutclient.pl: warnings are a nice to have, do not crash if absent [#1711]
  • NEWS.adoc, clients/nutclient.cpp, clients/nutclient.h, clients/upsclient.c, clients/upsclient.h, docs/man/libnutclient_tcp.txt, docs/man/upscli_ssl_caps.txt: clients/upsclient.c, clients/nutclient.cpp, headers, docs: propagate UPSCLI_SSL_CAPS_CERTIDENT as a flag [#3331]
  • server/netssl.c, clients/upsclient.c: *ssl_caps_descr(): mention OpenSSL CERTIDENT (in)ability [#3331]
  • scripts/perl/UPS/Nut.pm: warnings are a nice to have, do not crash if absent [#1711]
  • clients/upsclient.c, clients/nutclient.cpp, server/netssl.c, m4/nut_check_libopenssl.m4: for OpenSSL password callback, check actual presence of methods, not version numbers [#3331]
  • clients/upsclient.c: nss_password_callback(): reconcile markup with server/netssl.c [#3331]
  • server/netssl.c: for now openssl_password_callback() should only be declared/implemented when OPENSSL_VERSION_NUMBER >= 1.1.0 [#3331]
  • tests/NIT/nit.sh: account SKIPPED test cases (when isTestable* says we can not) [#1711]
  • tests/NIT/nit.sh: be portable, use TABCHAR instead of "\t" [#1711]
  • docs/nut.dict: update for older systems
  • configure.ac: work around ancient autotools without PACKAGE_URL
  • Makefile.am: hijack PARMAKES_OPT to also set V=0 by default

2.175. 2026-04-14 Lukas Schmid <lukas.schmid@netcube.li>

  • drivers/apcmicrolink-maps.c, drivers/apcmicrolink-maps.h, drivers/apcmicrolink.c: drivers/apcmicrolink.c: try to use naming from apc_modbus for some commands and values
  • drivers/apcmicrolink.c: fix microlink_page0_state_t init and reset

2.176. 2026-04-14 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.c: for now openssl_password_callback() should only be declared/implemented when OPENSSL_VERSION_NUMBER >= 1.1.0 [#3331]
  • conf/upsmon.conf.sample.in, docs/man/upsmon.conf.txt: clarify that CERTIDENT support requires OpenSSL 1.1.0\+ to fully work at the moment [#3331]
  • drivers/nutdrv_qx.c, NEWS.adoc: qx_is_usb_device_supported(): only claim SUPPORTED if subdriver_command was assigned [#3410]

2.177. 2026-04-14 Axel Gembe <axel@gembe.net>

  • NEWS.adoc, drivers/apc_modbus.c: apc_modbus: increase default TCP response timeout to 2000 ms The default 500ms timeout value is marginal for Modbus TCP, even on a fast local network. In tests with an APC UPS, we see timeouts because of this: ``` Apr 14 19:10:44 ares apc_modbus[2002143]: _apc_modbus_read_registers: Read of 0:27 failed: Connection timed out (192.168.0.102:502) Apr 14 19:10:44 ares apc_modbus[2002143]: _apc_modbus_read_registers: Read of 0:27 failed: Invalid data (192.168.0.102:502) Apr 14 19:10:44 ares apc_modbus[2002143]: _apc_modbus_close: Closing connection Apr 14 19:10:47 ares apc_modbus[2002143]: Opened modbus successfully ``` Packet capture: ``` No. Time Source Destination Protocol Length Info 3636 2026-04-14 19:10:43.842072 192.168.0.40 192.168.0.102 Modbus/TCP 66 Query: Trans: 12622; Unit: 1, Func: 3: Read Holding Registers 3637 2026-04-14 19:10:44.018369 192.168.0.102 192.168.0.40 TCP 60 502 → 40392 [ACK] Seq=70441 Ack=11905 Win=3660 Len=0 3638 2026-04-14 19:10:44.377723 192.168.0.40 192.168.0.102 Modbus/TCP 66 Query: Trans: 12623; Unit: 1, Func: 3: Read Holding Registers 3639 2026-04-14 19:10:44.578103 192.168.0.102 192.168.0.40 TCP 60 502 → 40392 [ACK] Seq=70441 Ack=11917 Win=3648 Len=0 3640 2026-04-14 19:10:44.579449 192.168.0.102 192.168.0.40 Modbus/TCP 117 Response: Trans: 12622; Unit: 1, Func: 3: Read Holding Registers 3641 2026-04-14 19:10:44.579482 192.168.0.40 192.168.0.102 TCP 54 40392 → 502 [ACK] Seq=11917 Ack=70504 Win=63 Len=0 3642 2026-04-14 19:10:44.579604 192.168.0.40 192.168.0.102 TCP 54 40392 → 502 [FIN, ACK] Seq=11917 Ack=70504 Win=63 Len=0 ``` 3636 is the first read, 3638 is the retry after 500ms without response, both are ACKed. 3640 is the actual response to transaction 12622, which arrives 700ms after the initial packet. At this point we expect a response to transaction 12623, which is why we fail with "invalid data". To fix this we increase the default TCP response timeout to 2000 ms, which seems to be more reasonable for a device that already can take 700 ms to respond under ideal conditions. I chose a bit higher value for users who might connect the device over a higher latency network.
  • NEWS.adoc, docs/nut.dict, drivers/apc_modbus.c, drivers/apc_modbus.h: apc_modbus: decode RunTimeCalibrationStatus_BF RunTimeCalibrationStatus_BF is explained in MPAO-98KJ7F_R1_EN Appendix B. This adds defines for the bitfield and decodes it into experimental.ups.calibration.result using a string join converter.
  • NEWS.adoc, docs/nut.dict, drivers/apc_modbus.c, drivers/apc_modbus.h: apc_modbus: decode PowerSystemError_BF as alarms PowerSystemError_BF is explained in MPAO-98KJ7F_R1_EN Appendix B. This adds defines for the bitfield and decodes it as alarms in the driver.
  • NEWS.adoc, docs/nut.dict, drivers/apc_modbus.c, drivers/apc_modbus.h: apc_modbus: decode BatterySystemError_BF as alarms BatterySystemError_BF is explained in MPAO-98KJ7F_R1_EN Appendix B. We were already using the bitfield for the RB status, this adds defines for the bitfield and decodes it as alarms in the driver.
  • NEWS.adoc, docs/man/apc_modbus.txt, drivers/apc_modbus.c: apc_modbus: simplify error handling with read retries Replace the complex _apc_modbus_handle_error function with a simpler retry mechanism built into _apc_modbus_read_registers. The new approach: - Retries register reads on ETIMEDOUT errors up to modbus_retries times (configurable, default 3) - On non-timeout errors or after retry exhaustion, closes the connection for reconnection on the next update cycle - Removes the platform-specific (WIN32/POSIX) timeout detection and the flush-based recovery that didn’t work anyway (flush is already done in _apc_modbus_reopen upon reconnection) Also adds a modbus_retries driver option to configure the number of read retry attempts, and improves logging for connection open/close events. This change was inspired by a patch by @marcan to do the same and from testing the behaviour of apcupsd, noticing that on my USB unit it times out on read and only succeeds on the first retry.
  • NEWS.adoc, drivers/apc_modbus.c: apc_modbus: always zero terminate string join _apc_modbus_string_join did not zero terminate the result in every case, like when all values are NULL.
  • drivers/apc_modbus.c: apc_modbus: bump driver version to 0.20

2.178. 2026-04-13 Lukas Schmid <lukas.schmid@netcube.li>

  • docs/nut.dict: Add more words to dict
  • drivers/apcmicrolink.c: fix most static compile issues
  • docs/nut.dict: add apcmicrolink and RMI

2.179. 2026-04-13 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: introduce apcmicrolink driver [#3406]
  • drivers/Makefile.am: EXTRA_DIST apcmicrolink-maps.h apcmicrolink.h [#3406]
  • docs/man/Makefile.am: provide apsmicrolink.html also [#3406]
  • tests/NIT/nit.sh: generate upsmon.pem [#3331, #1711]
  • NEWS.adoc, clients/nutclient.cpp, clients/upsclient.c, conf/upsd.conf.sample, conf/upsmon.conf.sample.in, docs/man/upsd.conf.txt, docs/man/upsmon.conf.txt, server/netssl.c: upsd, libupsclient, libnutclient, upsmon: in OpenSSL builds, support CERTPATH pointing to a big PEM file as well as a directory [#3331]
  • clients/nutclient.cpp, clients/nutclient.h, tests/cpputest-client.cpp: clients/nutclient.{h,cpp}, tests/cpputest-client.cpp: extend setSSLConfig_OpenSSL() etc. to support CERTIDENT name (validation of client' own cert) [#3331]
  • clients/upsclient.c: fix CERTIDENT (OpenSSL) to check for CN=… part too [#3331]
  • server/netssl.c: fix CERTIDENT (OpenSSL) to check for CN=… part too [#3331]
  • tests/NIT/nit.sh: generate upsmon.pem [#3331, #1711]
  • tests/NIT/nit.sh: setenv_ssl_cppnit(): fix check for TESTCERT_PATH_ROOTCA [#1711]
  • tests/NIT/nit.sh: unlock OpenSSL features that are now on par with NSS [#3331, #1711]
  • scripts/python/module/PyNUT.py.in, scripts/perl/UPS/Nut.pm: handle CERTIDENT to validate loaded client certificate name [#3331] Make sure the file we got is the file we wanted. Belts and suspenders rule!
  • scripts/python/module/PyNUT.py.in, scripts/perl/UPS/Nut.pm: handle CERTHOST (dict or list of dict/tuple) to validate connections better [#3331]
  • scripts/perl/UPS/Nut.pm: StartTLS(): alias variables named like in other NUT code into SSL_* if not provided directly [#1711]
  • clients/nutclient.cpp, clients/upsclient.c, conf/upsd.conf.sample, docs/man/upsd.conf.txt, server/netssl.c: clients/nutclient.cpp, clients/upsclient.c, server/netssl.c: reconcile variants of openssl_password_callback() [#3331] Notably, in C code (upsclient, netssl) constrain with OpenSSL >= 1.1 support, and do use SSL_CTX_set_default_passwd_cb_userdata() to pass the configured (or generally otherwise provided) certpasswd string.
  • NEWS.adoc, clients/upsmon.c, conf/upsmon.conf.sample.in, docs/man/upsmon.conf.txt: clients/upsmon.c, NEWS.adoc, conf/, docs/man/: introduce support of CERTFILE to upsmon [#3331]
  • clients/upsclient.c, clients/upsclient.h, docs/man/Makefile.am, docs/man/upscli_init.txt: clients/upsclient.{c,h}, docs/man/upscli_init.txt: implement client CERTIDENT correctly (via CERTFILE); introduce upscli_init2() [#3331]
  • NEWS.adoc, conf/upsd.conf.sample, docs/man/upsd.conf.txt, server/conf.c, server/netssl.c, server/netssl.h: server/*, NEWS.adoc, conf/upsd.conf.sample, docs/man/upsd.conf.txt: implement server-side CERTPATH for OpenSSL [#3331] Optionally use for validation of clients with OpenSSL collection of CA PEM files.
  • conf/upsd.conf.sample, docs/man/upsd.conf.txt, server/conf.c, server/netssl.c: server/*, conf/upsd.conf.sample, docs/man/upsd.conf.txt: implement server-side CERTIDENT for OpenSSL [#3331]
  • NEWS.adoc, conf/upsd.conf.sample, conf/upsmon.conf.sample.in, docs/man/upsd.conf.txt, docs/man/upsmon.conf.txt: NEWS.adoc, docs/man/, conf/: update docs about newly supported features with OpenSSL [#3331]
  • server/conf.c: reshuffle options processing and update comments now that CERTREQUEST is for both backends and more may follow [#3331]
  • clients/upsclient.c: upscli_sslinit(): consider CERTHOST→certverify setting to NOT abort the connection if verification was not required (0) [#3331]
  • clients/upsclient.c: upscli_sslinit(): consider OPENSSL_VERSION_NUMBER constraint in logging, possibly rejecting the StartTLS attempt with OpenSSL too old (can not fulfil required security, avoid sad mishaps) [#3331]
  • conf/upsd.conf.sample, docs/man/upsd.conf.txt: heads-up about MAXCONN and ulimit [#3365]
  • NEWS.adoc: remove duplicated paragraph (silent merge conflict)
  • clients/upsmon.c, clients/upsrw.c, common/common.c, drivers/adelsystem_cbi.c, drivers/generic_modbus.c, drivers/main.c, drivers/powerman-pdu.c, drivers/snmp-ups.c, drivers/tripplite_usb.c, tools/nut-scanner/nutscan-serial.c: .c: fix use of non-const char with strstr() and strchr() return values [#823]
  • docs/man/Makefile.am: fix possible non-delivery of NUTSCAN_UPSLOG_SET_DEBUG_LEVEL_DEPS pages [#3378, #3379]
  • docs/man/upscli_init.txt: update wording and markup for pre-existing features [#3331]
  • NEWS.adoc: announce the adjusted markup format

2.180. 2026-04-12 Jim Klimov <jimklimov@gmail.com>

  • scripts/perl/UPS/Nut.pm: warn if CERTVERIFY and custom CA options are enabled on Darwin platform [#3404]
  • scripts/perl/UPS/Nut.pm: align closer with can_ssl [#1711]
  • scripts/perl/UPS/Nut.pm, scripts/perl/test_nutclient.pl, tests/NIT/nit.sh: scripts/perl/*, tests/NIT/nit.sh: streamline passing NUT_DEBUG_SSL_PERL (or defaulting to no debug) [#1711]
  • tests/NIT/nit.sh: neuter CERTVERIFY on darwin for now [#3404]
  • tests/NIT/nit.sh: centralize definition of PERL_OPTS [#1711]
  • scripts/perl/UPS/Nut.pm: allow SSL cert validation for IP addresses as host names [#1711]
  • scripts/perl/UPS/Nut.pm: pass only defined args to IO::Socket::SSL→start_SSL() [#1711]
  • scripts/perl/UPS/Nut.pm: support debugging messages of IO::Socket::SSL [#1711]
  • tests/NIT/nit.sh: report the non-default NIT_CASE being handled [#1711]

2.181. 2026-04-12 Jim Klimov <jimklimov+nut@gmail.com>

  • clients/upsclient.c: upscli_sslinit(): OPENSSL_VERSION_NUMBER constraint applies to whole block for X509_VERIFY_PARAM [#3331]
  • server/netssl.c: extend OpenSSL builds feature set to support CERTREQUEST validation of clients [#3331]
  • clients/upsclient.c: extend OpenSSL builds feature set to support pinning server certificates [#3331]
  • clients/upsclient.c: extend OpenSSL builds feature set to support a client certificate\+key, optionally with password [#3331]
  • scripts/perl/UPS/Nut.pm, scripts/perl/test_nutclient.pl: scripts/perl/UPS/Nut.pm: now we can pass SSL debug level as a number [#1711]
  • scripts/perl/UPS/Nut.pm: fix to loading IO::Socket::SSL via eval with quotes not braces [#1711] It is then evaluated at run-time as code reaches that line (or not - conditionals), not at interpretation time where it fails too early. Also use "1;" in the end as a tidy practice.
  • clients/nutclient.cpp: work around lack of threads in some mingw versions [#3402]

2.182. 2026-04-11 Lukas Schmid <lukas.schmid@netcube.li>

  • data/driver.list.in, docs/man/Makefile.am, docs/man/apcmicrolink.txt, drivers/Makefile.am, drivers/apcmicrolink-maps.c, drivers/apcmicrolink-maps.h, drivers/apcmicrolink.c, drivers/apcmicrolink.h: drivers: add experimental APC Microlink serial driver Implement a new apcmicrolink driver for APC Smart-UPS units that expose the Microlink serial protocol. The driver establishes a Microlink session over the serial link, reads the device descriptor blob, and uses that runtime descriptor map to publish standard NUT variables, writable settings, status bits, alarms, and supported instant commands. Add labeled Microlink command masks for battery tests, calibration, UPS control, and outlet-group power control; wire APC-style load and shutdown commands through the static command map; publish input.transfer.reason from UPS status change cause; and stop folding simple-signaling status bits into ups.status. Also hook upsdrv_shutdown() up to the Microlink shutdown request, document the driver, and clean up initial upstream review issues such as missing file headers, whitespace, and format-string warnings.

2.183. 2026-04-11 Jim Klimov <jimklimov+nut@gmail.com>

  • m4/ax_c_pragmas.m4, clients/nutclient.cpp: introduce HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_DEPRECATED_DECLARATIONS{,_BESIDEFUNC} to implement methods for deprecated "master" operation [#840, #3402]

2.184. 2026-04-10 Jim Klimov <jimklimov+nut@gmail.com>

  • NEWS.adoc: announce updates to client binding libraries [#3402]
  • scripts/perl/test_nutclient.pl: avoid "Can’t call method "Error" on an undefined value" messages in failed tests; clearly say that $nut is no more [#1711]
  • scripts/perl/UPS/Nut.pm: when IO::Socket::SSL is not available, report the error with "FEATURE-NOT-SUPPORTED" in text [#1711]
  • tests/NIT/nit.sh: setenv_ssl_perl(): check if IO::Socket::SSL is available, neuter NUT_FORCESSL based on that [#1711]
  • scripts/perl/test_nutclient.pl: support USESSL=undef if no value provided in env [#1711]
  • scripts/perl/UPS/Nut.pm: support USESSL=undef, check that we can_ssl and follow up with that [#1711]
  • scripts/perl/UPS/Nut.pm: _initialize(): include previously reported error in SSL setup fail messages [#1711]
  • scripts/perl/UPS/Nut.pm: apply indentation consistenly (3 styles were intermixed)
  • scripts/perl/UPS/Nut.pm, scripts/perl/test_nutclient.pl: support NUT_SSL envvar toggle to disable attempts at SSL altogether [#1711]
  • scripts/perl/UPS/Nut.pm, scripts/perl/test_nutclient.pl: SSL_ca_path should be defined in args by caller, not by a hack in production code [#1711]
  • scripts/perl/UPS/Nut.pm: STARTTLS: debug-print %arg list that is passed to the SSL library [#1711]
  • scripts/perl/UPS/Nut.pm: use CAPATH to provide SSL_ca_path, not SSL_ca_file [#1348, #1711]
  • scripts/python/module/PyNUT.py.in: retry SSL handshake if it timed out, a few times [#3401]
  • server/netssl.c: net_starttls(): report which SSL backend is in place (if any) [#3331]
  • scripts/python/module/PyNUT.py.in: isValidProtocolVersion(): stringify and strip the "result" before matching in regex [#1349]
  • clients/Makefile.am: bump libupsclient ABI version
  • tests/NIT/nit.sh: add a note about jNut using this script [#1711, #1350]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.cpp, clients/nutclientmem.h, tests/cpputest-client.cpp: clients/nutclient{,mem}.{h,cpp}, tests/cpputest-client.cpp: remember if we enabled TRACKING so it is really done once, confirm with isTrackingModeEnabled() [#656]
  • scripts/perl/test_nutclient.pl: fix DeviceLogin⇒ListClient for "admin" without a role [#1711]
  • scripts/perl/UPS/Nut.pm: _send(): reset the error buffer before networking away [#1711, #1348]
  • scripts/perl/UPS/Nut.pm: we can only authenticate once per connection, skip other attempts [#1711, #1348]
  • docs/download.txt: publish jNut-1.1 [#1350]
  • NEWS.adoc: announce updates to client binding libraries [#3402]
  • appveyor.yml: post only one (custom) notification per build [#1400]
  • docs/man/Makefile.am: hush "cat" errors in LIBNUTCLIENT_TCP_DEPS_SUB recipe

2.185. 2026-04-09 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/perl/test_nutclient.pl: fix reporting of @failed methods [#1711]
  • scripts/perl/UPS/Nut.pm: GetTrackingResult(): fix parsing results of GET TRACKING [#1348]
  • tests/NIT/nit.sh: when we ask for specific NIT_CASE values (cppnit, python, perl, nutscanner) the lack of prerequisites should be a FAILURE [#1711]
  • tests/NIT/nit.sh: introduce tests for PERL [#1348, #1711]
  • message, so we can parse it [#1348, #1711]
  • scripts/perl/test_nutclient.pl: fix access to possibly undefined envvars [#1348, #1711]
  • scripts/perl/UPS/Nut.pm: fix error messages when Authenticate() and Login() fail [#1348, #1711]
  • scripts/perl/UPS/Nut.pm, scripts/perl/test_nutclient.pl: scripts/perl/test_nutclient.pl: fix test case setup, update comments [#1711, #1348]
  • scripts/perl/UPS/Nut.pm: fix string work with TRACKING logic
  • scripts/perl/UPS/Nut.pm: change commands that address an UPS to optionally accept the UPS name as argument, only using
  • scripts/python/module/test_nutclient.py.in: fix typo in debug traces
  • assignments into debug() calls [#1348, #1711]
  • COPYING, scripts/HP-UX/nut.psf.in, scripts/Makefile.am, scripts/obs/debian.libups-nut-perl.install, scripts/perl/{ ⇒ UPS}/Nut.pm, scripts/perl/test_nutclient.pl: scripts/perl/test_nutclient.pl: introduce tests, move Nut.pm to UPS subdir [#1348, #1711]
  • scripts/perl/Nut.pm: call StartTLS() from _initialize(), and use isValidProtocolVersion() method to actively ping a connection after STARTTLS claimed success (but handshake might be broken in fact) [#3387, #1348]
  • clients/nutclient.cpp, clients/upsclient.c, scripts/python/module/PyNUT.py.in: Client libraries (C, C+\+, Python): use isValidProtocolVersion() method to actively ping a connection after STARTTLS claimed success (but handshake might be broken in fact) [#3387]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.cpp, clients/nutclientmem.h, clients/upsclient.c, clients/upsclient.h, scripts/perl/Nut.pm, scripts/python/module/PyNUT.py.in: Client libraries (C, C+\+, Python, PERL): introduce an isValidProtocolVersion() method to actively ping a connection [#3387]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.cpp, clients/nutclientmem.h: clients/nutclient{,mem}.{h,cpp}: becomeSecondary() was overkill, there is no such verb in NUT protocol (LOGIN already reaches that role level)
  • scripts/python/module/PyNUT.py.in: refactor FSD() with an explicit becomePrimary() method
  • scripts/perl/Nut.pm: becomeSecondary() was overkill, there is no such verb in NUT protocol (LOGIN already reaches that role level)
  • scripts/perl/Nut.pm: add warnings toggle
  • scripts/perl/Nut.pm: fix typos in comments
  • scripts/perl/Nut.pm: rename primary()⇒becomePrimary, add becomeSecondary() for completeness [#1348]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.cpp, clients/nutclientmem.h: clients/nutclient{,mem}.{h,cpp}: rename primary()⇒becomePrimary, add becomeSecondary() for completeness [#656]
  • tests/cpputest-client.cpp: add test with SET VAR TRACKING [#1711, #656]
  • scripts/python/module/test_nutclient.py.in: add test with SET VAR TRACKING [#1711, #1349]
  • scripts/perl/Nut.pm: fix typos in comments
  • docs/download.txt: refer to new release of jNut-1.0 (and the following snapshot evolution)
  • tests/NIT/nit.sh: do not unset DEBUG_SLEEP when we generate configs [#1711] We might actually want more of those, with test case passwords revealed, etc.

2.186. 2026-04-08 Jim Klimov <jimklimov+nut@gmail.com>

  • scripts/python/module/PyNUT.py.in: TrackingID class: include timestamp tracking and age reporting [#1349]
  • scripts/perl/Nut.pm: TrackingID class: include timestamp tracking and age reporting [#1348]
  • clients/nutclient.cpp, clients/nutclient.h, clients/nutclientmem.cpp, clients/nutclientmem.h: clients/nutclient{,mem}.{h,cpp}, tests/*.cpp: implement waiting for TRACKING result if requested [#656]
  • clients/nutclient.h, clients/Makefile.am: Implement TrackingID as a separate class, including timestamp tracking and age reporting [#656]
  • scripts/python/module/PyNUT.py.in: Implement TrackingID as a separate class [#1349]
  • scripts/python/module/PyNUT.py.in: fix syntax [#1349]
  • scripts/perl/Nut.pm: Implement TrackingID as a separate class [#1348]
  • scripts/perl/Nut.pm: fix syntax [#1348]
  • scripts/python/module/PyNUT.py.in: revise AI creations, complete the TRACKING\+waiting dialog support [#1349]
  • scripts/perl/Nut.pm: revise AI creations, complete the TRACKING\+waiting dialog support [#1348]
  • scripts/perl/Nut.pm, scripts/python/module/PyNUT.py.in: AI PoC: implement the remainder of NUT Networked protocol (as of NUT v2.8.5) for Python and Perl bindings
  • scripts/perl/Nut.pm: clean up trailing white-space
  • tests/NIT/nit.sh: simplify with NIT_CASE=generatecfg_sandbox support [#1711]
  • tests/NIT/nit.sh: allow injecting WITH_SSL_CLIENT, WITH_SSL_SERVER, WITH_SSL_SERVER_CLIVAL (e.g. to test against older packaged builds) [#1711]
  • .github/workflows/05-codeql.yml: drop duplicate init stage
  • configure.ac: hush clang-21 "-Wc11-extensions" warnings about system headers
  • docs/download.txt: fix NOTE markup for N4W 7Zip size

2.187. 2026-04-07 Jim Klimov <jimklimov+nut@gmail.com>

  • docs/maintainer-guide.txt: revise instructions for post-release steps (website, etc.)
  • docs/download.txt: newly refer to nut-X.Y.Z-docs.tar.gz archives
  • docs/docinfo.xml.in: fix release date of v2.8.5 and move its tag to be above v2.8.4
  • NEWS.adoc, UPGRADING.adoc, docs/docinfo.xml.in: Revert "NEWS.adoc, UPGRADING.adoc, docs/docinfo.xml.in: finalize text before NUT v2.8.5 release" This reverts commit 784219a3dc935e53a1273063c591bc6dd949590b: finally released NUT v2.8.5, preparing for v2.8.6 cycle now.
  • tools/gitlog2version.sh: for development, set fallback NUT_VERSION_DEFAULT=2.8.5.1

2.188. 2026-04-06 Jim Klimov <jimklimov+nut@gmail.com>

  • common/wincompat.c: syslog(), send_to_named_pipe(): debug-log when we failed to open existing NAMED_PIPE and so move on [#3302]
  • common/wincompat.c: log errors after methods faults via upslog_with_errno() not plain upslogx() [#3302]
  • common/wincompat.c: log progress through pipe*() methods [#3302, #3368]
  • common/wincompat.c: fix send_to_named_pipe() to measure strlen(data) once [#3302]
  • common/wincompat.c: fix getpass() to measure strlen(wincompat_password) once [#3302]
  • common/wincompat.c: fix filter_path() to measure strlen(source) once [#3302]
  • common/wincompat.c: tcflush(): fix bitwise OR to boolean OR

2.189. 2026-03-31 Jim Klimov <jimklimov+nut@gmail.com>

  • server/upsd.c: sendback(): adjust debug traces for write() vs ssl_write() [#3379, #3331]
  • server/netssl.c: net_starttls(): send_err_extra(NUT_ERR_ACCESS_DENIED) for faults in NSS setup [#3379, #3331]
  • server/upsd.c, server/upsd.h: server/upsd.{c,h}: introduce send_err_extra() [#3331]
  • drivers/dstate.c, NEWS.adoc: revise closing of connections and returning error codes; document the methods [#3302]

2.190. 2026-03-30 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/dstate.c: debug-log the progress around starting and maybe failing CreateNamedPipe() [#3302]
  • drivers/dstate.c: sock_disconnect(): resync code with wincompat::pipe_disconnect() implementation [#3302]
  • drivers/dstate.c: sock_disconnect(): try to flush the buffer [#3368]

2.191. 2026-03-25 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/dstate.c: err on the safe side and set conn=NULL after sock_disconnect() [#3368]

2.192. 2026-03-24 Jim Klimov <jimklimov+nut@gmail.com>

  • drivers/upsdrvctl.c: refactor with forkexec_parent_analyze() and a re-check before retry - also for WIN32 [#3302] Also revised WaitForSingleObject() result checking - there has to be a chance to succeed ;)
  • drivers/upsdrvctl.c: refactor with forkexec_parent_analyze() and a re-check before retry - POSIX part [#3302]

2.193. 2025-10-02 Jim Klimov <jimklimov+nut@gmail.com>

  • configure.ac: WIN32 GetAdaptersInfo(): be careful about $NETLIBS_GETADDRS usage
  • ci_build.sh: retain config.cache if DO_CLEAN_AUTOCONF_CACHE=no also in dev-test builds [#3108]
  • ci_build.sh: configure_nut(): log if config.cache is available and if we want/do use it [#3108]
  • ci_build.sh: do not default to DO_USE_AUTOCONF_CACHE=yes/DO_CLEAN_AUTOCONF_CACHE=no [#3108] Callers like specific CI recipes should be free to enable certain behaviors though.
  • .circleci/config.yml: default to DO_CLEAN_AUTOCONF_CACHE:no DO_USE_AUTOCONF_CACHE:yes and carry over the config.cache between builds [#3108]
  • appveyor.yml: default to DO_CLEAN_AUTOCONF_CACHE:no DO_USE_AUTOCONF_CACHE:yes and carry over the config.cache between builds [#3108]
  • ci_build.sh, scripts/Windows/build-mingw-nut.sh, NEWS.adoc: introduce options to USE_AUTOCONF_CACHE and/or CLEAN_AUTOCONF_CACHE [#3108]