diff --git a/NEWS.adoc b/NEWS.adoc index 711aab6d55..6ff7edbfc6 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -49,14 +49,31 @@ https://github.com/networkupstools/nut/milestone/10 * dstate machinery: a segmentation fault (null pointer dereference) was possible with `INSTCMD` processing of commands without parameters nor `TRACKING` identifier. [#2155] + * USB bus number detection for libusb-1.0 builds was overly zealous and + wrongly considered zero values as an error. [#2198] * `upsmon` recognition of `CAL` state could linger after the calibration activity was completed by the hardware, which led to mis-processing of shutdown triggers. Also, notification was added to report "finished calibration". [issue #2168, PR #2169] + * `upsmon` support for `POLLFAIL_LOG_THROTTLE_MAX` did not neuter the + applied setting when live-reloading configuration, so commenting it + away in `upsmon.conf` did not have the effect of resetting the logging + frequency to default. It also did not reset the counters to certainly + follow the new configuration for existing faults. [#2207] + * `upsmon` support for `POLLFAIL_LOG_THROTTLE_MAX` had an off-by-one error + (e.g. reporting "Data stale" or "Driver not connected" every 30 sec with + `POLLFAIL_LOG_THROTTLE_MAX 5` and `POLLFREQ 5` settings). [#2207] * Drivers running with non-default user account (e.g. with `user=root` in their configuration) failed to apply group ownership and permissions to their Unix socket file for interaction with the local data server. [#2185, #2096] + * Dispatcher script `scripts/python/app/NUT-Monitor` referenced `py3qt3` + instead of the correct `py3qt5`. It also tries to check both `py2gtk2` + and `py3qt5` implementations verbosely, even if one is not installed. + [#2199, #2201] + * Set the `DesktopFileName` in `scripts/python/app/NUT-Monitor-py3qt5`, + this binds the application with the desktop file and allow the Desktop + implementation to display the proper icon and application name. [#2205] - nut-usbinfo.pl, nut-scanner and libnutscan: * USB VendorID:ProductID support list files generated by the script for diff --git a/clients/upsmon.c b/clients/upsmon.c index 01aa2604f7..827d2f1d0e 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -3,6 +3,7 @@ Copyright (C) 1998 Russell Kroll 2012 Arnaud Quette + 2020-2023 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -60,8 +61,9 @@ static unsigned int pollfreq = 5, pollfreqalert = 5; * will only be repeated every so many POLLFREQ loops. * If pollfail_log_throttle_max == 0, such error messages will * only be reported once when that situation starts, and ends. - * By default it is logged every pollfreq (which can abuse syslog - * and its storage). + * By default (or for negative values) it is logged every pollfreq + * loop cycle (which can abuse syslog and its storage), same as + * if "max = 1". * To support this, each utype_t (UPS) structure tracks individual * pollfail_log_throttle_count and pollfail_log_throttle_state */ @@ -216,7 +218,8 @@ typedef struct async_notify_s { int flags; char *ntype; char *upsname; - char *date; } async_notify_t; + char *date; +} async_notify_t; static unsigned __stdcall async_notify(LPVOID param) { @@ -1867,6 +1870,27 @@ static void loadconfig(void) * (or commented away) the debug_min * setting, detect that */ nut_debug_level_global = -1; + + if (pollfail_log_throttle_max >= 0) { + utype_t *ups; + + upslogx(LOG_INFO, + "Forgetting POLLFAIL_LOG_THROTTLE_MAX=%d and " + "resetting UPS error-state counters before " + "a configuration reload", + pollfail_log_throttle_max); + pollfail_log_throttle_max = -1; + + /* forget poll-failure logging throttling, so that we + * rediscover the error-states and the counts involved + */ + ups = firstups; + while (ups) { + ups->pollfail_log_throttle_count = -1; + ups->pollfail_log_throttle_state = UPSCLI_ERR_NONE; + ups = ups->next; + } + } } while (pconf_file_next(&ctx)) { @@ -1900,7 +1924,7 @@ static void loadconfig(void) if (reload_flag == 1) { if (nut_debug_level_global > -1) { upslogx(LOG_INFO, - "Applying debug_min=%d from upsmon.conf", + "Applying DEBUG_MIN %d from upsmon.conf", nut_debug_level_global); nut_debug_level = nut_debug_level_global; } else { @@ -1914,7 +1938,7 @@ static void loadconfig(void) if (pollfail_log_throttle_max >= 0) { upslogx(LOG_INFO, - "Applying pollfail_log_throttle_max=%d from upsmon.conf", + "Applying POLLFAIL_LOG_THROTTLE_MAX %d from upsmon.conf", pollfail_log_throttle_max); } } @@ -2259,9 +2283,11 @@ static void pollups(utype_t *ups) * failure state */ pollfail_log = 0; } else { - /* Only log once for start, every MAX iterations, - * and end of the same failure state */ - if (ups->pollfail_log_throttle_count++ >= pollfail_log_throttle_max) { + /* here (pollfail_log_throttle_max > 0) : + * only log once for start, every MAX iterations, + * and end of the same failure state + */ + if (ups->pollfail_log_throttle_count++ >= (pollfail_log_throttle_max - 1)) { /* ping... */ pollfail_log = 1; ups->pollfail_log_throttle_count = 0; @@ -2282,9 +2308,10 @@ static void pollups(utype_t *ups) upslogx(LOG_ERR, "Poll UPS [%s] failure state code " "changed from %d to %d; " "report below will only be repeated to syslog " - "every %d polling loop cycles:", + "every %d polling loop cycles (%d sec):", ups->sys, ups->pollfail_log_throttle_state, - upserror, pollfail_log_throttle_max); + upserror, pollfail_log_throttle_max, + pollfail_log_throttle_max * pollfreq); } ups->pollfail_log_throttle_state = upserror; diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index d555fec796..8634f5d540 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -415,8 +415,11 @@ NOCOMMWARNTIME 300 # extended timeframe, you can use this throttle to reduce the stress on # syslog traffic and storage, by posting these messages only once in every # several loop cycles, and when the error condition has changed or cleared. -# A negative value means standard behavior, and a zero value means to never -# repeat the message (log only on start and end/change of the failure state). +# A negative value means standard behavior (log on every loop, same as when +# max=1), and a zero value means to never repeat the message (log only on +# start and end/change of the failure state). +# Note that this throttle only applies to one latest-active error state per +# monitored device. # #POLLFAIL_LOG_THROTTLE_MAX 100 diff --git a/data/driver.list.in b/data/driver.list.in index 1bb1bee82d..9c74f758bd 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -607,7 +607,7 @@ "Legrand" "ups" "2" "Keor Line RT" "Serial" "nutdrv_qx" "Legrand" "ups" "2" "Keor Line RT" "USB" "nutdrv_qx" "Legrand" "ups" "2" "Keor LP" "Serial" "nutdrv_qx" -"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" # NOTE: As of 2023 reports, the "NEW Keor Multiplug" is not supported (also by older vendor SW) "Legrand" "ups" "2" "Keor S" "Serial" "nutdrv_qx" "Legrand" "ups" "2" "Keor S" "USB" "nutdrv_qx" "Legrand" "ups" "3" "Keor PDU" "USB" "usbhid-ups" diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 6f559092be..0a1a49b660 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -171,9 +171,13 @@ system log as configured. If your devices are expected to be AWOL for an extended timeframe, you can use this throttle to reduce the stress on syslog traffic and storage, by posting these messages only once in every several loop cycles, and when the error condition has changed or cleared. - -A negative value means standard behavior, and a zero value means to never -repeat the message (log only on start and end/change of the failure state). ++ +A negative value means standard behavior (log on every loop, effectively +same as when `max=1`), and a zero value means to never repeat the message +(log only on start and end/change of the failure state). ++ +Note that this throttle only applies to one latest-active error state per +monitored device. *NOTIFYCMD* 'command':: diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 227d9c34c7..ad751603ee 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -475,9 +475,12 @@ system log as configured. If your devices are expected to be AWOL for an extended timeframe, you can use POLLFAIL_LOG_THROTTLE_MAX to reduce the stress on syslog traffic and storage, by posting these messages only once in every several loop cycles, and when the error condition has changed or -cleared. A negative value means standard behavior, and a zero value means -to never repeat the message (log only on start and end/change of the failure -state). +cleared. A negative value means standard behavior (log on every loop, +effectively same as when `max=1`), and a zero value means to never repeat +the message (log only on start and end/change of the failure state). + +Note that this throttle only applies to one latest-active error state per +monitored device. RELOADING NUANCES ----------------- diff --git a/docs/nut.dict b/docs/nut.dict index 1389ae0c51..7653b5e3a0 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3420 utf-8 +personal_ws-1.1 en 3421 utf-8 AAC AAS ABI @@ -296,6 +296,7 @@ Ddtrace DeepTstFail Defensor DeltaUPSv +DesktopFileName DeviceID DeviceKit DeviceLogin diff --git a/m4/nut_check_aspell.m4 b/m4/nut_check_aspell.m4 index 4fb7feaa9f..dbccd27878 100644 --- a/m4/nut_check_aspell.m4 +++ b/m4/nut_check_aspell.m4 @@ -35,6 +35,13 @@ if test -z "${nut_have_aspell_seen}"; then ASPELL_VERSION_MINMAJ="`echo "${ASPELL_VERSION}" | sed 's,\.@<:@0-9@:>@@<:@0-9@:>@*$,,'`" + dnl FIXME: Some systems have more complicated layouts, e.g. + dnl /usr/lib/amd64/aspell-0.60/tex-filter.so + dnl /usr/lib/aspell-0.60/tex-filter.so + dnl which require matching of the `aspell` binary architecture + dnl with the module. We currently avoid the hassle thanks to a + dnl fallback to built-in paths below if this initial guesswork + dnl failed. This may need some more-direct addressing later. AC_MSG_CHECKING([for aspell "lib" filtering resources directory]) ASPELL_BINDIR="`dirname "$ASPELL"`" if test -d "${ASPELL_BINDIR}/../lib" ; then @@ -97,22 +104,58 @@ if test -z "${nut_have_aspell_seen}"; then AC_MSG_RESULT([${ASPELL_FILTER_TEX_PATH}]) fi - AM_CONDITIONAL([HAVE_ASPELL_FILTER_LIB_PATH], [test -d "$ASPELL_FILTER_LIB_PATH"]) - AC_SUBST(ASPELL_FILTER_LIB_PATH) - AM_CONDITIONAL([HAVE_ASPELL_FILTER_SHARE_PATH], [test -d "$ASPELL_FILTER_SHARE_PATH"]) - AC_SUBST(ASPELL_FILTER_SHARE_PATH) - AM_CONDITIONAL([HAVE_ASPELL_FILTER_TEX_PATH], [test -d "$ASPELL_FILTER_TEX_PATH"]) - AC_SUBST(ASPELL_FILTER_TEX_PATH) AC_MSG_CHECKING([if aspell version can do our documentation spell checks (minimum required ${ASPELL_MIN_VERSION})]) AX_COMPARE_VERSION([${ASPELL_VERSION}], [ge], [${ASPELL_MIN_VERSION}], [ AC_MSG_RESULT(yes) - nut_have_aspell="yes" + AC_MSG_CHECKING([if detected aspell configuration works]) + dnl Roughly following docs/Makefile.am setup for "make spellcheck": + ASPELL_NUT_TEXMODE_ARGS="-t" + AS_IF([test -n "$ASPELL_FILTER_TEX_PATH" -a -d "$ASPELL_FILTER_TEX_PATH"], [ASPELL_NUT_TEXMODE_ARGS="--filter-path='${ASPELL_FILTER_TEX_PATH}' ${ASPELL_NUT_TEXMODE_ARGS}"]) + ASPELL_NUT_COMMON_ARGS="-d en -a" + dnl Using "eval" to handle quotes, in case of funny paths + out0="`LANG=C; LC_ALL=C; export LANG; export LC_ALL; exec -- 2>&1; set -x; echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}`"; res0=$? + AS_IF([test x"$res0" != x0], [ + AC_MSG_NOTICE([FAILED CMD: ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}]) + AC_MSG_NOTICE([aspell result ($res0) and output: $out0]) + ]) + AS_CASE([$out0], + [*ELFCLASS*|*"wrong ELF class"*], [ + dnl Retry without the filter path, we must have caught a wrong one + dnl and *most* platforms do serve a trustworthy built-in after all: + AC_MSG_RESULT(no) + AC_MSG_CHECKING([if detected aspell configuration works with built-in paths (tweaked one finds wrong binary modules)]) + ASPELL_NUT_TEXMODE_ARGS="-t" + out0="`LANG=C; LC_ALL=C; export LANG; export LC_ALL; exec -- 2>&1; set -x; echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}`"; res0=$? + AS_IF([test x"$res0" = x0], [ASPELL_FILTER_TEX_PATH=""], [ + AC_MSG_NOTICE([FAILED CMD: ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS}]) + AC_MSG_NOTICE([aspell result ($res0) and output: $out0]) + ]) + ] + ) + out1="`echo test | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS} | grep test`"; res1=$? + out2="`echo qwer | eval ${ASPELL} ${ASPELL_NUT_TEXMODE_ARGS} ${ASPELL_NUT_COMMON_ARGS} | grep qwer`"; res2=$? + AS_IF([test x"$out1" = x -a x"$out2" != x], [ + AC_MSG_RESULT(yes) + nut_have_aspell="yes" + ], [ + AC_MSG_RESULT(no) + AC_MSG_NOTICE([aspell result ($res1) for 'test' (should be empty): $out1]) + AC_MSG_NOTICE([aspell result ($res2) for 'qwer' (should have suggestions): $out2]) + nut_have_aspell="no" + ]) ], [ AC_MSG_RESULT(no) nut_have_aspell="no" ]) + AM_CONDITIONAL([HAVE_ASPELL_FILTER_LIB_PATH], [test -d "$ASPELL_FILTER_LIB_PATH"]) + AC_SUBST(ASPELL_FILTER_LIB_PATH) + AM_CONDITIONAL([HAVE_ASPELL_FILTER_SHARE_PATH], [test -d "$ASPELL_FILTER_SHARE_PATH"]) + AC_SUBST(ASPELL_FILTER_SHARE_PATH) + AM_CONDITIONAL([HAVE_ASPELL_FILTER_TEX_PATH], [test -d "$ASPELL_FILTER_TEX_PATH"]) + AC_SUBST(ASPELL_FILTER_TEX_PATH) + dnl Notes: we also keep HAVE_ASPELL for implicit targets, such as dnl addition to "make check" target dnl ### AM_CONDITIONAL([HAVE_ASPELL], [test -n "$ASPELL"]) diff --git a/scripts/python/app/NUT-Monitor b/scripts/python/app/NUT-Monitor index cab270f574..11c5a07e3b 100755 --- a/scripts/python/app/NUT-Monitor +++ b/scripts/python/app/NUT-Monitor @@ -5,7 +5,7 @@ # a single simple call. # # Copyright (C): -# 2022 Jim Klimov +# 2022-2023 Jim Klimov # # License: GPLv2+ @@ -14,8 +14,8 @@ PREFER_PY2=true # Detect which variant of NUT-Monitor we can run on the local system: -PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,'`" || PYTHON_PY2GTK2="" -PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,'`" || PYTHON_PY3QT5="" +[ -s "$0"-py2gtk2 -a -x "$0"-py2gtk2 ] && PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,'`" || PYTHON_PY2GTK2="" +[ -s "$0"-py3qt5 -a -x "$0"-py3qt5 ] && PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,'`" || PYTHON_PY3QT5="" SCRIPTDIR="`dirname "$0"`" && SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" || SCRIPTDIR="./" if [ -n "$PYTHON_PY2GTK2" ] \ @@ -52,14 +52,14 @@ if [ -n "$PYTHON_PY2GTK2" ] && [ -n "$PYTHON_PY3QT5" ] ; then if $PREFER_PY2 ; then exec "$0"-py2gtk2 "$@" else - exec "$0"-py3qt3 "$@" + exec "$0"-py3qt5 "$@" fi else if [ -n "$PYTHON_PY2GTK2" ] ; then exec "$0"-py2gtk2 "$@" fi - if [ -n "$PYTHON_PY3QT3" ] ; then - exec "$0"-py3qt3 "$@" + if [ -n "$PYTHON_PY3QT5" ] ; then + exec "$0"-py3qt5 "$@" fi fi diff --git a/scripts/python/app/NUT-Monitor-py2gtk2.in b/scripts/python/app/NUT-Monitor-py2gtk2.in index 5c16aa3a77..652132f1c8 100755 --- a/scripts/python/app/NUT-Monitor-py2gtk2.in +++ b/scripts/python/app/NUT-Monitor-py2gtk2.in @@ -75,6 +75,9 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() + # FIXME: tell the system to not guess and definitively use + # the `nut-monitor-py2gtk2.desktop` for windowing resources: + # g_set_prgname('nut-monitor-py2gtk2') self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "ui/gui-1.3.glade" ) diff --git a/scripts/python/app/NUT-Monitor-py3qt5.in b/scripts/python/app/NUT-Monitor-py3qt5.in index d78f4160a4..a149ddaf31 100755 --- a/scripts/python/app/NUT-Monitor-py3qt5.in +++ b/scripts/python/app/NUT-Monitor-py3qt5.in @@ -29,6 +29,9 @@ # # 2022-02-20 Luke Dashjr - Version 2.0 # Port to Python 3 with PyQt5. +# +# 2023-11-27 Laurent Bigonville - Version 2.0.1 +# Set the DesktopFileName import PyQt5.uic @@ -80,6 +83,10 @@ class interface : self.__app = QApplication( argv ) + try: + self.__app.setDesktopFileName("nut-monitor-py3qt5") + except Exception as ex: + pass self.__ui_file = self.__find_res_file( 'ui', "window1.ui" ) diff --git a/server/conf.c b/server/conf.c index c752ed55ca..dd908ce4c8 100644 --- a/server/conf.c +++ b/server/conf.c @@ -415,7 +415,7 @@ void load_upsdconf(int reloading) if (reloading) { if (nut_debug_level_global > -1) { upslogx(LOG_INFO, - "Applying debug_min=%d from upsd.conf", + "Applying DEBUG_MIN %d from upsd.conf", nut_debug_level_global); nut_debug_level = nut_debug_level_global; } else {