diff --git a/NEWS.adoc b/NEWS.adoc index 6d88f0fd75..047e4801c3 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -286,6 +286,9 @@ https://github.com/networkupstools/nut/milestone/12 to bumping the debug verbosity of `upsdrvctl` tool. [#3276] * If we fail to stop a driver by signal (e.g. PID file was not saved), retry to `INSTCMD driver.exit` it by socket protocol. [#3277] + * The NUT Integration Testing suite now also involves `upsdrvctl` to + run one driver instance indirectly, helping make check-NIT` catch + portability issues with different builds of the tool. [#2800, #3292] - `upslog` tool updates: * Updated `help()` and failure messages to suggest `-m '*,-'` for logging diff --git a/configure.ac b/configure.ac index e7bc787c86..ddee6dd565 100644 --- a/configure.ac +++ b/configure.ac @@ -6708,10 +6708,13 @@ AS_CASE([${target_os}], AC_MSG_NOTICE([FWIW, assuming ABS_TOP_SRCDIR="$ABS_TOP_SRCDIR" and ABS_TOP_BUILDDIR="$ABS_TOP_BUILDDIR"]) ]) -dnl Use these at best for tests (e.g. nutconf), not production code: +dnl Use these at best for tests (e.g. nutconf, NIT), not production code: AC_DEFINE_UNQUOTED([ABS_TOP_SRCDIR], ["${ABS_TOP_SRCDIR}"], [NUT source directory when the build was configured]) AC_DEFINE_UNQUOTED([ABS_TOP_BUILDDIR], ["${ABS_TOP_BUILDDIR}"], [NUT build directory when the build was configured]) +AC_SUBST(ABS_TOP_SRCDIR) +AC_SUBST(ABS_TOP_BUILDDIR) + dnl --------------------------------------------------------------------- AC_MSG_CHECKING([whether to install External API integration script: Enphase Monitor]) nut_with_extapi_enphase="no" diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index a6ba76b459..475d4a6d49 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -943,7 +943,7 @@ static void forkexec(char *const argv[], const ups_t *ups) BOOL ret; DWORD res; DWORD exit_code = 0; - char commandline[SMALLBUF]; + char commandline[LARGEBUF]; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; int i = 1; @@ -951,15 +951,30 @@ static void forkexec(char *const argv[], const ups_t *ups) memset(&StartupInfo, 0, sizeof(STARTUPINFO)); /* the command line is made of the driver name followed by args */ - snprintf(commandline, sizeof(commandline), "%s", ups->driver); + if (strstr(argv[0], ups->driver)) { + /* We already know whom to call (got a pointer to needle in the haystack) */ + snprintf(commandline, sizeof(commandline), "%s", argv[0]); + } else { + /* Hope for the PATH based resolution to work, perhaps the + * driver program is located nearby (depends on configure + * options). Note that for builds tested in the workspace + * this may be misleading ("nearby" is under ".libs/" and + * fails to run directly without the tweaks of libtool + * wrapper provided in the directory just above). + */ + snprintf(commandline, sizeof(commandline), "%s%s", ups->driver, EXEEXT); + } + while (argv[i] != NULL) { snprintfcat(commandline, sizeof(commandline), " %s", argv[i]); i++; } + upsdebugx(1, "%s[WIN32]: CreateProcess(argv0='%s' cmdline='%s')...", + __func__, argv[0], commandline); ret = CreateProcess( - argv[0], - commandline, + argv[0], /* Application/Module name, often the program to run */ + commandline, /* Full command line including the program to run and its args */ NULL, NULL, FALSE, @@ -1237,7 +1252,10 @@ static void start_driver(const ups_t *ups) #ifndef WIN32 snprintf(dfn, sizeof(dfn), "%s/%s", driverpath, ups->driver); #else /* WIN32 */ - snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); + if (driverpath && *driverpath == '/') + snprintf(dfn, sizeof(dfn), "%s/%s.exe", driverpath, ups->driver); + else /* Assume windows-style path with backslashes */ + snprintf(dfn, sizeof(dfn), "%s\\%s.exe", driverpath, ups->driver); #endif /* WIN32 */ ret = stat(dfn, &fs); diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am index 8332bac658..0d1aea3f2c 100644 --- a/tests/NIT/Makefile.am +++ b/tests/NIT/Makefile.am @@ -13,10 +13,16 @@ EXTRA_DIST = nit.sh README.adoc if WITH_CHECK_NIT check: check-NIT -else +else !WITH_CHECK_NIT check: @echo "NO-OP: $@ in `pwd` is inactive by default. Run 'configure --enable-check-NIT' or 'make check-NIT' explicitly" >&2 -endif +endif !WITH_CHECK_NIT + +# Paths possibly different from what automake provides (POSIXish) +# when building on/for e.g. Windows. Matters where system() is used, +# such as NIT suite using/testing upsdrvctl: +ABS_TOP_SRCDIR = @ABS_TOP_SRCDIR@ +ABS_TOP_BUILDDIR = @ABS_TOP_BUILDDIR@ # Run in builddir, use script from srcdir # Avoid running "$<" - not all make implementations handle that @@ -25,6 +31,7 @@ check-NIT: $(abs_srcdir)/nit.sh BUILTIN_RUN_AS_USER='$(RUN_AS_USER)' BUILTIN_RUN_AS_GROUP='$(RUN_AS_GROUP)' \ abs_srcdir='$(abs_srcdir)' abs_builddir='$(abs_builddir)' \ abs_top_srcdir='$(abs_top_srcdir)' abs_top_builddir='$(abs_top_builddir)' \ + ABS_TOP_SRCDIR='$(ABS_TOP_SRCDIR)' ABS_TOP_BUILDDIR='$(ABS_TOP_BUILDDIR)' \ EXEEXT='$(EXEEXT)' \ "$(abs_srcdir)/nit.sh" @@ -48,6 +55,7 @@ check-NIT-sandbox: $(abs_srcdir)/nit.sh BUILTIN_RUN_AS_USER='$(RUN_AS_USER)' BUILTIN_RUN_AS_GROUP='$(RUN_AS_GROUP)' \ abs_srcdir='$(abs_srcdir)' abs_builddir='$(abs_builddir)' \ abs_top_srcdir='$(abs_top_srcdir)' abs_top_builddir='$(abs_top_builddir)' \ + ABS_TOP_SRCDIR='$(ABS_TOP_SRCDIR)' ABS_TOP_BUILDDIR='$(ABS_TOP_BUILDDIR)' \ EXEEXT='$(EXEEXT)' \ "$(abs_srcdir)/nit.sh" diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 0db5378c85..43df6e47fc 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -53,7 +53,7 @@ # different value like "nobody" or "nogroup" would be defaulted for test. # # Copyright -# 2022-2025 Jim Klimov +# 2022-2026 Jim Klimov # # License: GPLv2+ @@ -427,7 +427,7 @@ else fi log_info "Locating NUT programs to test:" -for PROG in upsd upsc dummy-ups upsmon upslog upssched ; do +for PROG in upsd upsc dummy-ups upsdrvctl upsmon upslog upssched ; do (command -v ${PROG}) || (command -v ${PROG}${EXEEXT-}) || die "Useless setup: ${PROG} not found in PATH: ${PATH}" done @@ -1000,9 +1000,24 @@ generatecfg_upsmon_secondary() { generatecfg_ups_trivial() { # Populate the configs for the run ( echo 'maxretry = 3' > "$NUT_CONFPATH/ups.conf" || exit - if [ x"${TOP_BUILDDIR}" != x ]; then - echo "driverpath = \"${TOP_BUILDDIR}/drivers\"" >> "$NUT_CONFPATH/ups.conf" || exit + if [ x"${ABS_TOP_BUILDDIR}" != x ]; then + # NOTE: Windows backslashes are pre-escaped in the configure-generated value + case "${ABS_TOP_BUILDDIR}" in + ?":\\"*) PATHSEP='\\' ;; + *) PATHSEP="/" ;; + esac + echo "driverpath = \"${ABS_TOP_BUILDDIR}${PATHSEP}drivers\"" >> "$NUT_CONFPATH/ups.conf" || exit + else + # NOTE: Escaping presumed needed below, so for PATHSEP too + if [ x"${TOP_BUILDDIR}" != x ]; then + case "${TOP_BUILDDIR}" in + ?":\\"*) PATHSEP='\' ;; + *) PATHSEP="/" ;; + esac + echo "driverpath = \"${TOP_BUILDDIR}${PATHSEP}drivers\"" | sed 's,\\,\\\\,g' >> "$NUT_CONFPATH/ups.conf" || exit + fi fi + unset PATHSEP if [ -n "${NUT_DEBUG_MIN-}" ] ; then echo "debug_min = ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/ups.conf" || exit fi @@ -1359,8 +1374,9 @@ sandbox_start_drivers() { if [ -n "${NUT_DEBUG_LEVEL_DRIVERS-}" ]; then NUT_DEBUG_LEVEL="${NUT_DEBUG_LEVEL_DRIVERS}" fi - #execcmd upsdrvctl ${ARG_FG} ${ARG_USER} start dummy & - execcmd dummy-ups -a dummy ${ARG_USER} ${ARG_FG} & + # Run one driver instance indirectly, to test the upsdrvctl tool too: + execcmd upsdrvctl ${ARG_FG} ${ARG_USER} start dummy & + #execcmd dummy-ups -a dummy ${ARG_USER} ${ARG_FG} & PID_DUMMYUPS="$!" log_debug "Tried to start dummy-ups driver for 'dummy' as PID $PID_DUMMYUPS"