From 37b79c3998ecdde2995700f8e91802073781d030 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 3 Jan 2025 21:07:34 +0100 Subject: [PATCH 1/8] tests/NIT/nit.sh: stop_daemons(): first stop upsmon by its own command, then kill it As of now we `make check-NIT-sandbox-devel` and eventually `kill $UPSMON_PID` but this only stops the parent process, but the forked-off child keeps running. Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 2da6495092..b902ed6a11 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -34,7 +34,7 @@ # ksh, busybox sh...) # # Copyright -# 2022-2024 Jim Klimov +# 2022-2025 Jim Klimov # # License: GPLv2+ @@ -356,6 +356,11 @@ if [ "`id -u`" = 0 ]; then fi stop_daemons() { + if [ -n "$PID_UPSMON" ] ; then + log_info "Stopping test daemons: upsmon via command" + upsmon -c stop + fi + if [ -n "$PID_UPSD$PID_UPSMON$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then log_info "Stopping test daemons" kill -15 $PID_UPSD $PID_UPSMON $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 2>/dev/null || return 0 From 744fb8740de6c36b0e5737c2d4121a5a2eaf9a15 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 3 Jan 2025 21:46:48 +0100 Subject: [PATCH 2/8] clients/upslog.c: revise code markup Signed-off-by: Jim Klimov --- clients/upslog.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clients/upslog.c b/clients/upslog.c index 4b88cd9421..4ef9a19d07 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -78,7 +78,8 @@ static void reopen_log(void) { for (monhost_ups_current = monhost_ups_anchor; monhost_ups_current != NULL; - monhost_ups_current = monhost_ups_current->next) { + monhost_ups_current = monhost_ups_current->next + ) { if (monhost_ups_current->logfile == stdout) { upslogx(LOG_INFO, "logging to stdout"); return; @@ -594,7 +595,8 @@ int main(int argc, char **argv) for (monhost_ups_current = monhost_ups_anchor; monhost_ups_current != NULL; - monhost_ups_current = monhost_ups_current->next) { + monhost_ups_current = monhost_ups_current->next + ) { printf("logging status of %s to %s (%is intervals)\n", monhost_ups_current->monhost, monhost_ups_current->logfn, interval); if (upscli_splitname(monhost_ups_current->monhost, &(monhost_ups_current->upsname), &(monhost_ups_current->hostname), &(monhost_ups_current->port)) != 0) { @@ -668,7 +670,8 @@ int main(int argc, char **argv) for (monhost_ups_current = monhost_ups_anchor; monhost_ups_current != NULL; - monhost_ups_current = monhost_ups_current->next) { + monhost_ups_current = monhost_ups_current->next + ) { ups = monhost_ups_current->ups; /* XXX Not ideal */ upsname = monhost_ups_current->upsname; /* XXX Not ideal */ /* reconnect if necessary */ @@ -690,7 +693,8 @@ int main(int argc, char **argv) for (monhost_ups_current = monhost_ups_anchor; monhost_ups_current != NULL; - monhost_ups_current = monhost_ups_current->next) { + monhost_ups_current = monhost_ups_current->next + ) { if (monhost_ups_current->logfile != stdout) fclose(monhost_ups_current->logfile); From 9f8a4564cb4414467625d024489032deb470dd95 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 3 Jan 2025 21:39:26 +0100 Subject: [PATCH 3/8] clients/upslog.c: support limit of loop count Signed-off-by: Jim Klimov --- clients/upslog.c | 19 +++++++++++++++++-- docs/man/upslog.txt | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clients/upslog.c b/clients/upslog.c index 4ef9a19d07..73c1d38d89 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -39,12 +39,14 @@ #include "timehead.h" #include "nut_stdint.h" #include "upslog.h" +#include "str.h" #ifdef WIN32 #include "wincompat.h" #endif static int reopen_flag = 0, exit_flag = 0; + static size_t max_loops = 0; static char *upsname; static UPSCONN_t *ups; @@ -154,6 +156,7 @@ static void help(const char *prog) printf(" -f - Log format. See below for details.\n"); printf(" - Use -f \"\" so your shell doesn't break it up.\n"); printf(" -i - Time between updates, in seconds\n"); + printf(" -d - Exit after specified amount of updates\n"); printf(" -l - Log file name, or - for stdout (foreground by default)\n"); printf(" -F - stay foregrounded even if logging into a file\n"); printf(" -B - stay backgrounded even if logging to stdout\n"); @@ -435,7 +438,7 @@ static void run_flist(struct monhost_ups *monhost_ups_print) int main(int argc, char **argv) { int interval = 30, i, foreground = -1; - size_t monhost_len = 0; + size_t monhost_len = 0, loop_count = 0; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; @@ -447,7 +450,7 @@ int main(int argc, char **argv) print_banner_once(prog, 0); - while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:FBm:")) != -1) { + while ((i = getopt(argc, argv, "+hs:l:i:d:f:u:Vp:FBm:")) != -1) { switch(i) { case 'h': help(prog); @@ -502,6 +505,10 @@ int main(int argc, char **argv) interval = atoi(optarg); break; + case 'd': + str_to_ulong_strict(optarg, &max_loops, 10); + break; + case 'f': logformat = optarg; break; @@ -686,6 +693,14 @@ int main(int argc, char **argv) upscli_disconnect(ups); } } + + if (max_loops > 0) { + loop_count++; + if (loop_count >= max_loops || loop_count > (SIZE_MAX - 1)) { + upslogx(LOG_INFO, "%" PRIuSIZE " loops have elapsed", max_loops); + exit_flag = 1; + } + } } upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); diff --git a/docs/man/upslog.txt b/docs/man/upslog.txt index 478af0f9ac..e9bfcbb8cc 100644 --- a/docs/man/upslog.txt +++ b/docs/man/upslog.txt @@ -71,6 +71,11 @@ Wait this many seconds between polls. This defaults to 30 seconds. If you require tighter timing, you should write your own logger using the linkman:upsclient[3] library. +*-d* 'count':: + +Exit after specified amount of updates. Default is 0 for infinite loop +(until interrupted otherwise). + *-l* 'logfile':: Store the results in this file. From 9d7582d0973f23e39e5c01b7aaa40389cd91b1f2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 5 Jan 2025 13:14:31 +0100 Subject: [PATCH 4/8] scripts/systemd/nut-logger.service.in: revise systemd integration mishaps [#1803] Signed-off-by: Jim Klimov --- scripts/systemd/nut-logger.service.in | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/systemd/nut-logger.service.in b/scripts/systemd/nut-logger.service.in index 914566fc0d..13d27cfbbf 100644 --- a/scripts/systemd/nut-logger.service.in +++ b/scripts/systemd/nut-logger.service.in @@ -23,7 +23,8 @@ Wants=nut-server.service # Requires=network-online.target # After=network-online.target PartOf=nut.target -Before=nut.target ### not enabled by default, but ordered if enabled by user +### not enabled by default, but ordered if enabled by user: +Before=nut.target Documentation=man:upslog(8) Documentation=@NUT_WEBSITE_BASE@/docs/man/upslog.html @@ -40,10 +41,12 @@ EnvironmentFile=-@CONFPATH@/nut.conf EnvironmentFile=@CONFPATH@/upslog.conf SyslogIdentifier=%N ExecStartPre=-@SYSTEMD_TMPFILES_PROGRAM@ --create @systemdtmpfilesdir@/nut-common-tmpfiles.conf -ExecStartPre=/bin/test -n "${UPSLOG_ARGS-}" -ExecStart=@SBINDIR@/upslog @SYSTEMD_DAEMON_ARGS_UPSLOG@ $UPSLOG_ARGS +ExecStartPre=/bin/test -n "${UPSLOG_ARGS}" +ExecStart=@BINDIR@/upslog @SYSTEMD_DAEMON_ARGS_UPSLOG@ $UPSLOG_ARGS # NOTE: SIGHUP is supported to re-open the log file, -# which is the default systemd ReloadSignal +# which is the default systemd ReloadSignal (only +# sent by systemd 253 and newer) +ExecReload=/bin/kill -HUP $MAINPID PIDFile=@PIDPATH@/upslog.pid # With this program, the PID file always exists and "kill -TERM" in particular # can be used from command-line or some legacy scripts, it causes a clean and From 7162244cf48a6b2c78031b1e82d9db08b4ab0aa8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 5 Jan 2025 13:20:19 +0100 Subject: [PATCH 5/8] clients/Makefile.am: upslog is now an integrated service, use the full library [#1803] Signed-off-by: Jim Klimov --- clients/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/Makefile.am b/clients/Makefile.am index 5af70c54a3..1b59ce990f 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -79,6 +79,7 @@ upsc_SOURCES = upsc.c upsclient.h upscmd_SOURCES = upscmd.c upsclient.h upsrw_SOURCES = upsrw.c upsclient.h upslog_SOURCES = upslog.c upsclient.h upslog.h +upslog_LDADD = $(LDADD_FULL) upsmon_SOURCES = upsmon.c upsmon.h upsclient.h upsmon_LDADD = $(LDADD_FULL) if HAVE_WINDOWS_SOCKETS From ac94a39531989ccba2dee6b467c86ad7d96f9e05 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 5 Jan 2025 15:09:46 +0100 Subject: [PATCH 6/8] tests/NIT/nit.sh: testcase_sandbox_upsc_query_timer(): use upslog for a second opinion [#1803] Signed-off-by: Jim Klimov --- tests/NIT/nit.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index b902ed6a11..befec52803 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -1194,7 +1194,17 @@ testcase_sandbox_upsc_query_bogus() { testcase_sandbox_upsc_query_timer() { log_separator log_info "[testcase_sandbox_upsc_query_timer] Test that dummy-ups TIMER action changes the reported state" - # Driver is set up to flip ups.status every 5 sec, so check every 3 + + # Driver is set up to flip ups.status every 5 sec, so check every 3 sec + # with upsc, but we can follow with upslog more intensively. New process + # launches can lag a lot on very busy SUTs; hopefully still-running ones + # are more responsive in this regard. + log_info "Starting upslog daemon" + rm -f "${NUT_STATEPATH}/upslog-dummy.log" || true + # Start as foregrounded always, so we have a PID to kill easily: + upslog -F -i 1 -d 30 -m "dummy@localhost:${NUT_PORT},${NUT_STATEPATH}/upslog-dummy.log" & + PID_UPSLOG="$!" + # TODO: Any need to convert to runcmd()? OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT1" ; sleep 3 OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "[testcase_sandbox_upsc_query_timer] upsd does not respond on port ${NUT_PORT} ($?): $OUT2" @@ -1216,7 +1226,15 @@ testcase_sandbox_upsc_query_timer() { fi fi fi - if echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OL" ; then + + log_info "Stopping upslog daemon" + kill -15 $PID_UPSLOG 2>/dev/null || true + wait $PID_UPSLOG || true + + if (grep " [OB] " "${NUT_STATEPATH}/upslog-dummy.log" && grep " [OL] " "${NUT_STATEPATH}/upslog-dummy.log") \ + || (grep " \[OB\] " "${NUT_STATEPATH}/upslog-dummy.log" && grep " \[OL\] " "${NUT_STATEPATH}/upslog-dummy.log") \ + || (echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4$OUT5" | grep "OL") \ + ; then log_info "[testcase_sandbox_upsc_query_timer] PASSED: ups.status flips over time" PASSED="`expr $PASSED + 1`" else @@ -1224,6 +1242,8 @@ testcase_sandbox_upsc_query_timer() { FAILED="`expr $FAILED + 1`" FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_timer" fi + + #rm -f "${NUT_STATEPATH}/upslog-dummy.log" || true } isTestablePython() { From 528f8359514fb4865650a59aa04eac7ab0f31f7c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 5 Jan 2025 15:15:57 +0100 Subject: [PATCH 7/8] NEWS.adoc, UPGRADING.adoc: report that upslog is now also a service [#1803] Signed-off-by: Jim Klimov --- NEWS.adoc | 5 +++++ UPGRADING.adoc | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/NEWS.adoc b/NEWS.adoc index 7bec29669c..17bbb19206 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -309,6 +309,11 @@ https://github.com/networkupstools/nut/milestone/11 as "OTHER" notification type (whenever the set of such tokens appears or changes) or "NOTOTHER" when they disappear. [#415] + - upslog: + * Added support for limiting the loop count. Using in NIT (NUT Integration + Test) suite for double profit (checking the tool and fallback in NIT). + * Added systemd and SMF service integration. [#1803] + - More systemd integration: * Introduced a `nut-sleep.service` unit which stops `nut.target` when a system sleep was requested, and starts it when the sleep is finished. diff --git a/UPGRADING.adoc b/UPGRADING.adoc index 47c4a554b3..dfcfaca94a 100644 --- a/UPGRADING.adoc +++ b/UPGRADING.adoc @@ -70,6 +70,10 @@ Changes from 2.8.2 to 2.8.3 For fallback with older systemd, a `nut-sleep.service` is provided now. [#1070, #2596, #2597] +- Added systemd and SMF service integration for `upslog` as a `nut-logger` + service (disabled by default, needs a `upslog.conf` file to deliver the + `UPSLOG_ARGS=...` setting for actual monitoring and logging). [#1803] + - Handling of per-UPS `ALARM` state was introduced to `upsmon`, allowing it to optionally treat it as a factor in deciding that the device is in a "critical" state (polled more often, assumed dead if communications are From 1d8bf9253c93cce053238ef14a4df07e59b621d6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 5 Jan 2025 16:02:50 +0100 Subject: [PATCH 8/8] clients/upslog.c: fix max loops CLI arg processing [#2753] Signed-off-by: Jim Klimov --- clients/upslog.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/clients/upslog.c b/clients/upslog.c index 73c1d38d89..ac53e8d5ea 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -506,7 +506,28 @@ int main(int argc, char **argv) break; case 'd': - str_to_ulong_strict(optarg, &max_loops, 10); + { /* scoping */ + unsigned long ul = 0; + if (str_to_ulong(optarg, &ul, 10)) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (ul < SIZE_MAX) + max_loops = (size_t)ul; + else + upslogx(LOG_ERR, "Invalid max loops"); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + } else + upslogx(LOG_ERR, "Invalid max loops"); + } break; case 'f':