Skip to content

Commit

Permalink
Merge pull request networkupstools#2753 from jimklimov/issue-1803
Browse files Browse the repository at this point in the history
Revise `upslog` service integration, allow to limit loop count, use it in NIT
  • Loading branch information
jimklimov authored Jan 5, 2025
2 parents 8bc2bdd + 1d8bf92 commit f04db0c
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 13 deletions.
5 changes: 5 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clients/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 46 additions & 6 deletions clients/upslog.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -78,7 +80,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;
Expand Down Expand Up @@ -153,6 +156,7 @@ static void help(const char *prog)
printf(" -f <format> - Log format. See below for details.\n");
printf(" - Use -f \"<format>\" so your shell doesn't break it up.\n");
printf(" -i <interval> - Time between updates, in seconds\n");
printf(" -d <count> - Exit after specified amount of updates\n");
printf(" -l <logfile> - 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");
Expand Down Expand Up @@ -434,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;
Expand All @@ -446,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);
Expand Down Expand Up @@ -501,6 +505,31 @@ int main(int argc, char **argv)
interval = atoi(optarg);
break;

case 'd':
{ /* 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':
logformat = optarg;
break;
Expand Down Expand Up @@ -594,7 +623,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) {
Expand Down Expand Up @@ -668,7 +698,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 */
Expand All @@ -683,14 +714,23 @@ 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);
upsnotify(NOTIFY_STATE_STOPPING, "Signal %d: exiting", exit_flag);

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);
Expand Down
5 changes: 5 additions & 0 deletions docs/man/upslog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 7 additions & 4 deletions scripts/systemd/nut-logger.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
31 changes: 28 additions & 3 deletions tests/NIT/nit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# ksh, busybox sh...)
#
# Copyright
# 2022-2024 Jim Klimov <[email protected]>
# 2022-2025 Jim Klimov <[email protected]>
#
# License: GPLv2+

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1189,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"
Expand All @@ -1211,14 +1226,24 @@ 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
log_error "[testcase_sandbox_upsc_query_timer] ups.status did not flip over time"
FAILED="`expr $FAILED + 1`"
FAILED_FUNCS="$FAILED_FUNCS testcase_sandbox_upsc_query_timer"
fi

#rm -f "${NUT_STATEPATH}/upslog-dummy.log" || true
}

isTestablePython() {
Expand Down

0 comments on commit f04db0c

Please sign in to comment.