Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise upslog service integration, allow to limit loop count, use it in NIT #2753

Merged
merged 8 commits into from
Jan 5, 2025
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
Loading