Commit Graph
3 Commits
Author SHA1 Message Date
Nic BoetandClaude Opus 5.5 367e19d4f6 fix: apply SIGUSR1/SIGUSR2 log level changes outside the signal handler
Logger::usrHandler changed the level and called Info() from the handler.
Info() takes log_mutex, glibc's syslog and tzset locks (syslog,
localtime_r) and the database queue's mutex, and allocates. A SIGUSR1 or
SIGUSR2 delivered to a thread already inside one of those waits on
itself, and the process hangs until zmwatch/zmdc kill it.

The handler now only adds +1/-1 to a lock-free atomic, as the SIGWINCH
branch already sets a flag. Logger::fetch(), which every log macro calls
before its level check, applies a pending change under log_mutex and logs
"Level changed" from normal context, so the new level is in effect at the
next log call on any thread. Several signals before that add up to one
change, clamped to NOLOG..DEBUG9. logInit() drops a change still pending
for the logger it replaces.

fixes #5216

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:34:23 -05:00
Isaac Connor 88591b4168 fix: let SIGUSR1 raise logging to debug without ZM_LOG_DEBUG
With ZM_LOG_DEBUG off, Logger::initialise() clamped every target level
(file, database, syslog, terminal) to INFO as well as the overall level.
SIGUSR1 raises the overall level, but the effective level is the lower of
that and the highest target level, so it stayed at INFO and the signal
did nothing.

Only cap the overall level. Targets keep their configured levels, so
SIGUSR1 behaves as if ZM_LOG_DEBUG_LEVEL had been incremented: targets
configured for debug start receiving it, targets at INFO stay at INFO.
Startup behaviour is unchanged since the effective level is still INFO.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit af970628c3fe5fe76d163a30747cf534babbb8a6)
2026-10-10 09:08:19 -04:00
Isaac ConnorandClaude Opus 5 bde61e7af6 fix: rotate logs on SIGWINCH instead of SIGHUP fixes #5063
SIGHUP means reload. zmc responds by closing its events, disconnecting the
camera and reconnecting; the perl daemons respond by exiting so zmdc restarts
them. zmdc.pl logrot hupped every managed process, so the nightly logrotate
run cost about 8 seconds of capture on a default install.

Rotating a log file only needs the daemon to drop its file handle, so use a
separate signal for it. SIGWINCH is otherwise unused, is ignored by default and
exists on every supported platform.

- Logger (C++) installs a SIGWINCH handler beside its USR1/USR2 handler. The
  handler only sets a flag; the next logPrint closes the file and the write
  reopens it at the original path. This covers every C++ binary without
  touching any daemon's main loop.
- Logger.pm registers WINCH alongside HUP in logSetSignal, which logInit
  already calls, so the scripts that install their own HUP handler still
  rotate.
- zmdc.pl logrot sends WINCH. The logrotate config is unchanged - it still
  calls zmpkg.pl logrot.

Filter.pm and FilterTerm.php justified MAX_EVENT_DAYS by events not outliving
the nightly HUP, which is no longer what bounds them; cite SectionLength.

Tests: tests/zm_logger_rotate.cpp and tests/perl/test_log_rotate_signal.pl both
log, rename the file out from under the process, confirm writes still land in
the renamed file, signal WINCH and confirm the original path is written again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDhTBPj9xEaT52pRmAufaP
2026-08-22 15:30:46 -04:00