From 6c1e8134d005181c9e33985e240b74479d503445 Mon Sep 17 00:00:00 2001 From: user01010111 Date: Tue, 1 Sep 2026 05:57:27 +0000 Subject: [PATCH 1/2] 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. Signed-off-by: user01010111 --- drivers/Makefile.am | 17 ++++++++ drivers/snmp-ups-setvar-test.c | 75 ++++++++++++++++++++++++++++++++++ drivers/snmp-ups.c | 28 ++++++++++--- 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 drivers/snmp-ups-setvar-test.c diff --git a/drivers/Makefile.am b/drivers/Makefile.am index cb537f8b29..38a9e81a9e 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -430,6 +430,23 @@ endif !WITH_OPENSSL snmp_ups_LDFLAGS += $(LIBSSL_LDFLAGS_RPATH) endif WITH_SSL +if WITH_SNMP +TESTS = snmp-ups-setvar-test +check_PROGRAMS = $(TESTS) +snmp_ups_setvar_test_SOURCES = snmp-ups-setvar-test.c $(snmp_ups_SOURCES) +snmp_ups_setvar_test_CFLAGS = $(snmp_ups_CFLAGS) +snmp_ups_setvar_test_LDADD = libdummy_mockdrv.la $(LIBNETSNMP_LIBS) -lm +snmp_ups_setvar_test_LDFLAGS = $(AM_LDFLAGS) +if WITH_SSL +if !WITH_OPENSSL + snmp_ups_setvar_test_CFLAGS += -UNETSNMP_USE_OPENSSL +endif !WITH_OPENSSL + snmp_ups_setvar_test_CFLAGS += $(LIBSSL_CFLAGS) + snmp_ups_setvar_test_LDADD += $(LIBSSL_LIBS) + snmp_ups_setvar_test_LDFLAGS += $(LIBSSL_LDFLAGS_RPATH) +endif WITH_SSL +endif WITH_SNMP + # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c netxml_ups_LDADD = $(LDADD_DRIVERS) $(LIBNEON_LIBS) diff --git a/drivers/snmp-ups-setvar-test.c b/drivers/snmp-ups-setvar-test.c new file mode 100644 index 0000000000..6e3982b1e2 --- /dev/null +++ b/drivers/snmp-ups-setvar-test.c @@ -0,0 +1,75 @@ +/* snmp-ups-setvar-test.c - focused tests for SNMP outlet settings + * + * Copyright (C) 2026 Network UPS Tools contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "main.h" +#include "dstate.h" +#include "snmp-ups.h" + +static int expect_result(const char *name, int actual, int expected) +{ + if (actual == expected) + return 0; + + fprintf(stderr, "%s: got %d, expected %d\n", name, actual, expected); + return 1; +} + +int main(void) +{ + static snmp_info_t test_snmp_info[] = { + snmp_info_default("outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, + 20, NULL, "All outlets", SU_FLAG_ABSENT | SU_FLAG_OK, NULL), + snmp_info_default("outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, + SU_INFOSIZE, "not-an-oid.%i", NULL, SU_OUTLET | SU_FLAG_OK, NULL), + snmp_info_default("outlet.unavailable", ST_FLAG_RW | ST_FLAG_STRING, + 20, NULL, "Unavailable", SU_FLAG_ABSENT, NULL), + snmp_info_default("outlet.load.off", 0, 1, + "not-an-oid", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL), + snmp_info_sentinel + }; + const char *value; + int failed = 0; + + snmp_info = test_snmp_info; + dstate_setinfo("outlet.count", "%d", 1); + dstate_setinfo("outlet.desc", "%s", "All outlets"); + + failed += expect_result("server-side outlet.desc", + su_setvar("outlet.desc", "All rack outlets"), STAT_SET_HANDLED); + value = dstate_getinfo("outlet.desc"); + if (!value || strcmp(value, "All rack outlets")) { + fprintf(stderr, "server-side outlet.desc: got '%s'\n", NUT_STRARG(value)); + failed++; + } + + failed += expect_result("numbered outlet template", + su_setvar("outlet.1.desc", "Rack outlet 1"), STAT_SET_FAILED); + failed += expect_result("unknown numbered outlet template", + su_setvar("outlet.1.missing", "unused"), STAT_SET_UNKNOWN); + failed += expect_result("unavailable exact outlet mapping", + su_setvar("outlet.unavailable", "unused"), STAT_SET_UNKNOWN); + failed += expect_result("exact outlet command", + su_instcmd("outlet.load.off", NULL), STAT_INSTCMD_FAILED); + + dstate_free(); + snmp_info = NULL; + + return failed ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index cc8943a317..b6d03c3f7d 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3957,6 +3957,7 @@ static int su_setOID(int mode, const char *varname, const char *val) /* normal (default), outlet, or outlet group variable */ snmp_info_flags_t vartype = 0; int daisychain_device_number = -1; + bool_t is_template_instance = FALSE; /* variable without the potential "device.X" prefix, to find the template */ char *tmp_varname = NULL; const char *val_practical = NULL; @@ -4042,9 +4043,9 @@ static int su_setOID(int mode, const char *varname, const char *val) return (mode==SU_MODE_INSTCMD ? (int)STAT_INSTCMD_INVALID : (int)STAT_SET_INVALID); } - /* Check if it is outlet / outlet.group, or standard variable */ - if (strncmp(tmp_varname, "outlet", 6)) { - su_info_p = su_find_info(tmp_varname); + /* Check for an exact match before treating outlet names as templates */ + su_info_p = su_find_info(tmp_varname); + if (su_info_p || strncmp(tmp_varname, "outlet", 6)) { /* what if e.g. "device.x.contact" is not found as a "contact"? */ if (!su_info_p && strcmp(tmp_varname, varname)) { upsdebugx(2, @@ -4117,9 +4118,14 @@ static int su_setOID(int mode, const char *varname, const char *val) upsdebugx(3, "%s: searching for template\"%s\"", __func__, item_varname); tmp_info_p = su_find_info(item_varname); free(item_varname); + if (!tmp_info_p) { + free(tmp_varname); + return (mode==SU_MODE_INSTCMD ? (int)STAT_INSTCMD_UNKNOWN : (int)STAT_SET_UNKNOWN); + } /* for an snmp_info_t instance */ su_info_p = instantiate_info(tmp_info_p, su_info_p); + is_template_instance = TRUE; /* check if default value is also a template */ if ((su_info_p->dfl != NULL) && @@ -4197,7 +4203,8 @@ static int su_setOID(int mode, const char *varname, const char *val) upsdebugx(2, "%s: info element unavailable %s", __func__, varname); /* Free template (outlet and outlet.group) */ - free_info(su_info_p); + if (is_template_instance) + free_info(su_info_p); if (tmp_varname != NULL) free(tmp_varname); @@ -4216,6 +4223,16 @@ static int su_setOID(int mode, const char *varname, const char *val) } val_practical = val ? val : su_info_p->dfl; + if (mode == SU_MODE_SETVAR + && (su_info_p->info_flags & ST_FLAG_RW) + && (su_info_p->flags & SU_FLAG_ABSENT) + && su_info_p->OID == NULL + ) { + dstate_setinfo(varname, "%s", val_practical); + retval = STAT_SET_HANDLED; + goto cleanup; + } + if (su_info_p->info_flags & ST_FLAG_STRING) { status = nut_snmp_set_str(su_info_p->OID, val_practical); } @@ -4279,8 +4296,9 @@ static int su_setOID(int mode, const char *varname, const char *val) } } +cleanup: /* Free template (outlet and outlet.group) */ - if (!strncmp(tmp_varname, "outlet", 6)) + if (is_template_instance) free_info(su_info_p); free(tmp_varname); From 48e68772290a8fabe4ac09bf615fbf8fa9c8e4a4 Mon Sep 17 00:00:00 2001 From: user01010111 Date: Tue, 1 Sep 2026 21:10:32 +1200 Subject: [PATCH 2/2] 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. Signed-off-by: user01010111 --- NEWS.adoc | 4 +++ drivers/Makefile.am | 17 ---------- tests/Makefile.am | 40 +++++++++++++++++++++++ {drivers => tests}/snmp-ups-setvar-test.c | 0 4 files changed, 44 insertions(+), 17 deletions(-) rename {drivers => tests}/snmp-ups-setvar-test.c (100%) diff --git a/NEWS.adoc b/NEWS.adoc index 8da073ce8e..cb92392e3a 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -277,6 +277,10 @@ https://github.com/networkupstools/nut/milestone/13 `ups.realpower`. [issue #3580, PR #3581] - `snmp-ups` driver updates: + * Fixed a NULL pointer dereference when setting exact outlet mappings + without a corresponding numbered outlet template, and restored handling + of exact server-side outlet variables and commands. [issue #3360, + PR #3610] * Extended the XPPC-MIB subdriver (enterprise 935) to expose `battery.runtime`, `battery.voltage`, `input.frequency`, `output.voltage.nominal` and `ups.firmware.aux` from Phoenixtec diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 38a9e81a9e..cb537f8b29 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -430,23 +430,6 @@ endif !WITH_OPENSSL snmp_ups_LDFLAGS += $(LIBSSL_LDFLAGS_RPATH) endif WITH_SSL -if WITH_SNMP -TESTS = snmp-ups-setvar-test -check_PROGRAMS = $(TESTS) -snmp_ups_setvar_test_SOURCES = snmp-ups-setvar-test.c $(snmp_ups_SOURCES) -snmp_ups_setvar_test_CFLAGS = $(snmp_ups_CFLAGS) -snmp_ups_setvar_test_LDADD = libdummy_mockdrv.la $(LIBNETSNMP_LIBS) -lm -snmp_ups_setvar_test_LDFLAGS = $(AM_LDFLAGS) -if WITH_SSL -if !WITH_OPENSSL - snmp_ups_setvar_test_CFLAGS += -UNETSNMP_USE_OPENSSL -endif !WITH_OPENSSL - snmp_ups_setvar_test_CFLAGS += $(LIBSSL_CFLAGS) - snmp_ups_setvar_test_LDADD += $(LIBSSL_LIBS) - snmp_ups_setvar_test_LDFLAGS += $(LIBSSL_LDFLAGS_RPATH) -endif WITH_SSL -endif WITH_SNMP - # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c netxml_ups_LDADD = $(LDADD_DRIVERS) $(LIBNEON_LIBS) diff --git a/tests/Makefile.am b/tests/Makefile.am index eef6e490bf..8063a76250 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -212,6 +212,46 @@ endif ENABLE_SHARED_PRIVATE_LIBS driver_methods_utest_LDADD += $(top_builddir)/drivers/libdummy_mockdrv.la driver_methods_utest_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/tests -DDRIVERS_MAIN_WITHOUT_MAIN=1 +if WITH_SNMP +SNMP_UPS_TEST_DRIVER_SOURCES = snmp-ups.c snmp-ups-helpers.c \ + apc-mib.c apc-pdu-mib.c apc-epdu-mib.c \ + baytech-mib.c baytech-rpc3nc-mib.c bestpower-mib.c \ + compaq-mib.c cyberpower-mib.c \ + delta_ups-mib.c \ + eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ + eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c eaton-pdu-nlogic-mib.c \ + eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ + eaton-ups-pwnm2-mib.c eaton-ups-pxg-mib.c \ + emerson-avocent-pdu-mib.c \ + hpe-pdu-mib.c hpe-pdu3-cis-mib.c huawei-mib.c \ + ietf-mib.c \ + mge-mib.c \ + netvision-mib.c \ + raritan-pdu-mib.c raritan-px2-mib.c \ + vertiv-mib.c voltronic-mib.c \ + xppc-mib.c + +LINKED_SOURCE_FILES += $(SNMP_UPS_TEST_DRIVER_SOURCES) + +$(SNMP_UPS_TEST_DRIVER_SOURCES): + test -s '$@' || ln -s -f "$(top_srcdir)/drivers/$@" '$@' + +TESTS += snmp-ups-setvar-test +snmp_ups_setvar_test_SOURCES = snmp-ups-setvar-test.c +nodist_snmp_ups_setvar_test_SOURCES = $(SNMP_UPS_TEST_DRIVER_SOURCES) +snmp_ups_setvar_test_CFLAGS = $(AM_CFLAGS) $(LIBNETSNMP_CFLAGS) +snmp_ups_setvar_test_LDADD = $(top_builddir)/drivers/libdummy_mockdrv.la $(LIBNETSNMP_LIBS) -lm +snmp_ups_setvar_test_LDFLAGS = $(AM_LDFLAGS) +if WITH_SSL +if !WITH_OPENSSL + snmp_ups_setvar_test_CFLAGS += -UNETSNMP_USE_OPENSSL +endif !WITH_OPENSSL + snmp_ups_setvar_test_CFLAGS += $(LIBSSL_CFLAGS) + snmp_ups_setvar_test_LDADD += $(LIBSSL_LIBS) + snmp_ups_setvar_test_LDFLAGS += $(LIBSSL_LDFLAGS_RPATH) +endif WITH_SSL +endif WITH_SNMP + ### Optional tests which can not be built everywhere # List of src files for CppUnit tests CPPUNITTESTSRC = example.cpp nutclienttest.cpp diff --git a/drivers/snmp-ups-setvar-test.c b/tests/snmp-ups-setvar-test.c similarity index 100% rename from drivers/snmp-ups-setvar-test.c rename to tests/snmp-ups-setvar-test.c