All notable changes to this project will be documented in this file.
- UDP Status Command: Get real-time status of processes via UDP
- New
s<app>UDP command (e.g.,sBot); an empty or*name returns all apps - JSON response with: app, status, circuit state, pid, cpu, mem_kb, cooldown remaining, failure count, ack requirement
- Status values:
running,stopped,crashed,cooldown,circuit_open - Circuit states:
closed,open,half_open,disabled(if not configured) ack_requiredfield indicates if manual acknowledgment is needed before resuming
- New
- Status client (
-s [app],-jfor raw JSON): query a running watchdog and print an aligned table (or JSON), instead of hand-crafting UDP packets. - Resume over UDP (
k<app>): clear cooldown / circuit-breaker state and restart — the UDP equivalent of theresume<app>file command. - Tests: lean GTest unit/contract suite (parsing, backoff, status-JSON contract) and
-tintegration checks (config_parse,status_json,heartbeat_timeout) run via-t all; both wired into CI.
- Enabled control commands (
start<app>,stop<app>,restart<app>,s<app>,k<app>) via UDP - Server module now tracks sender address for UDP responses
-tnow returns a pass/fail exit status (-t allgates CI); signal handlers log the shutdown cause from the main loop, not from signal context- Updated README.md (quick-start, status client, testing model, UDP command reference)
- Progress/stagnation detection was silently disabled:
config_parse_file()unconditionally resetprogress_timeoutto 0 after parsing, discarding the value read from the INI. Because the whole feature is gated onprogress_timeout > 0, stagnation detection never ran for any app despite being documented and shipped default-on. Removed the stray reset (the initialmemsetstill defaults omitting apps to 0). Added theprogress_timeout_configregression test. - Silent permanent outage on UDP-poll failure: the main loop exited with the default
EXIT_NORMALLY, which the supervisor script treats as a clean stop (no respawn). Now exits withEXIT_RESTARTso the watchdog is restarted. A signal-interrupted poll (EINTR) is treated as "no data", not a failure. - Remote out-of-bounds read in the UDP command parser:
parse_numberhad an unbounded scan (ignored length / NUL), signed-charisdigitUB, and acharcounter that could index before the buffer. Now bounded andunsigned char-safe; malformed-datagram tests added. - Undefined behaviour in restart backoff:
base_delay * (1 << (retry_count - 1))could shift past the int width and overflow negative past the 1-hour cap (restart storm) for a largemax_retries. Inputs are now clamped. - Divide-by-zero in first-heartbeat stats guarded (potential
SIGFPE). - Async-signal-unsafe signal handlers: handlers called
LOGN/exit()(mutex + stdio), which could self-deadlock at shutdown. Reduced to flag-setting viasigaction; the cause is logged from the main loop and the forced-exit path uses_exit. - Uninitialised reads on failed
stat,sysinfo, andclock_gettimereturn paths. - Potential crash from
localtimereturningNULL(badtime_t); replaced withlocaltime_r+ NULL guards in the timestamp, stats and daily-reboot paths. - Config handler rejections were silently accepted:
config_parse_fileonly treated a negativeini_parseresult as failure, but a handler rejecting a bad value returns the (positive) line number — so invalid app names, out-of-range integers and duplicate sections booted anyway. Any non-zero result is now a failure. - Integer overflow in
parse_number: a long digit run in a UDP datagram overflowed theintaccumulator (signed-overflow UB); it now clamps atINT32_MAX(rejected as an invalid pid downstream). - Two unchecked
clock_gettimecalls in the restart/backoff path (uninitialisedts.tv_secon failure) now use the checkedget_monotonic_time()wrapper. sysconf(_SC_CLK_TCK)used as a divisor without checking for<= 0(garbage CPU% orSIGFPE); now guarded.- Socket fd leak in
udp_startonsetsockopt/bindfailure (missingclose). circuit_breaker_window * 2could overflowint; widened tolong.- Config validation: reject an app section with no
cmd, a duplicate[app:X], and a too-longcmd(now checked before it is stored truncated).
- Unused, buggy helpers
substringandfind_replace_text.
- CI now cross-builds and publishes Linux binaries for
x86_64,aarch64andarmv7.
-
Circuit-Breaker (Anti-Flap): Prevents cascading failures by opening circuit after repeated failures
- New
circuit_breaker_thresholdconfig (0 = disabled, N = failures to open) - New
circuit_breaker_windowconfig (default: 300s/5min for failure counting) - Circuit states: CLOSED → OPEN → HALF_OPEN (after 2x window)
- Automatic retry after circuit opens (one trial)
- Full exponential backoff integration
- Clear log messages for state transitions
- New
-
Cooldown (Anti-Flap): Prevents rapid restart loops with waiting period
- New
cooldown_periodconfig (0 = disabled, N = seconds to wait) - New
cooldown_manual_ack_thresholdconfig (0 = auto-resume, N = require ack after N cooldowns) - Manual acknowledgment via
resume<app>file command - Status logging during cooldown period
- Automatic resume after cooldown if threshold not reached
- New
-
File Command: Manual acknowledgment for cooldown
resume<app>- Resume a cooled-down app after manual review
- Updated help section in binary to document new config options
- Updated README.md with concise circuit-breaker and cooldown documentation
- Added FlapTest app to config.ini as example
- Progress Signal (Stagnation Detection): Optional feature to detect processes that are alive but stuck/making no progress
- New
progress_timeoutconfig field (0 = disabled, >0 = seconds between progress signals) - Send
p<pid>+when work completes (append+to heartbeat) - Process restarted if no progress signal within
progress_timeoutseconds - Full backward compatibility: existing processes work unchanged
- Auto-adjustment:
progress_timeoutincreased if <=heartbeat_interval - Validation: warnings for unreasonable values (>7 days)
- Debug logging for progress signal reception
- Unit tests for heartbeat parsing with/without
+suffix - Comprehensive documentation with examples in 7 programming languages
- New
- Centralized
get_monotonic_time()function inutils.c - Updated test child script to support progress signal intervals:
python test_child.py 1 crash 45 - Enhanced configuration validation with helpful warnings
- Added best practices documentation for progress timeout values
- Restart backoff logic now uses
CLOCK_MONOTONICfor elapsed time calculations (was using wall-clocktime(NULL)).
- Restart policy with exponential backoff feature.
- Process start time verification to prevent PID reuse issues
- Average heartbeat count tracking before crashes/resets.
- Child process isolation using
setsid()to prevent signal propagation. - Exponential backoff with configurable
max_retriesandbase_delay. --versionflag support (--versionand-vboth work).
- Heartbeat timing now uses
CLOCK_MONOTONICto prevent issues with NTP adjustments and system clock changes. - Restart backoff logic now uses
CLOCK_MONOTONICfor elapsed time calculations. - Signal handler variables now use
sig_atomic_tfor safe signal handling. - CPU statistics use
floatinstead ofdoubleto reduce memory footprint. - Separated CPU and memory monitoring into independent functions.
- Static buffer in
printDate()replaced with caller-provided buffer. - Buffer overflow in stats filename generation (increased buffer size).
- Child processes now close inherited file descriptors before exec.
- Flexible periodic reboot feature. Supports daily time (
HH:MM) and intervals in hours (h), days (d), weeks (w), and months (m).
- Monitor and log CPU and memory usage for each application.
- CMake support for building the project.
- Refactored code: inlined wrappers, tidied up, and reformatted.
- Modularized code: moved command parsing to
cmd.c, heartbeat functions toheartbeat.c, process control functions toprocess.c, and INI parsing toconfig.c.
- Improved INI format for arrays of processes.
- Updated GitHub Actions for building and testing.
- Improved makefile.
- Enhanced application management: renamed
nWdtAppston_apps, improved start/restart/terminate functionality. - Improved config parsing safety.
- Added support for non-heartbeat processes.
- Updated
test_childto support non-heartbeat processes. - Print unknown commands as hex with safe bounds and printable fallback.
- Quoting in
run.sh. - Reduced test time to 300 seconds.
- Set signal to USR1 for normal exit.
- Log file printing.
- Initial project setup, CI creation, and basic makefile.
- Replaced "ping" with "heartbeat" in code and INI configuration.
- Updated README.md and Makefile.