Log Watcher

This commit is contained in:
Amos 2026-04-18 11:04:26 +01:00
parent bd03dcf042
commit e749a787a4
No known key found for this signature in database
6 changed files with 85 additions and 4 deletions

View file

@ -41,11 +41,13 @@ Everything runs as the `plutainer` user from `/home/plutainer/.plutainer`.
5. **`game-config.sh`** — Shared shell library sourced by all other scripts. Single source of truth for game detection, port defaults, config path resolution, and RCON password extraction.
6. **`healthcheck.sh`** — Sources `game-config.sh`, then uses `pyquake3.py` to send an RCON `status` command. Can be disabled with `PLUTO_HEALTHCHECK=true`, `IW4X_HEALTHCHECK=true`, or `ALTER_HEALTHCHECK=true`.
6. **`log-watcher.sh`** — Background poller started by each entrypoint before `exec wine`. Maintains stable symlinks at `/home/plutainer/app/logs/<name>` pointing at the active game log (e.g. `games_mp.log`, `games_zm.log`). Uses container boot time as a mtime cutoff so stale logs from prior sessions and abandoned mod dirs are ignored. Disable with `PLUTAINER_LOG_SYMLINKS=false`; poll interval via `PLUTAINER_LOG_POLL_INTERVAL` (default 2s).
7. **`rcon-cli`** — Python script providing interactive and one-shot RCON access via `docker exec`. Calls `game-config.sh` to resolve port/credentials. Supports Plutonium, IW4x, and Alterware.
7. **`healthcheck.sh`** — Sources `game-config.sh`, then uses `pyquake3.py` to send an RCON `status` command. Can be disabled with `PLUTO_HEALTHCHECK=true`, `IW4X_HEALTHCHECK=true`, or `ALTER_HEALTHCHECK=true`.
7. **`pyquake3.py`** — Python 3 Quake 3 protocol library (UDP). Used by the health check and `rcon-cli` for RCON queries.
8. **`rcon-cli`** — Python script providing interactive and one-shot RCON access via `docker exec`. Calls `game-config.sh` to resolve port/credentials. Supports Plutonium, IW4x, and Alterware.
9. **`pyquake3.py`** — Python 3 Quake 3 protocol library (UDP). Used by the health check and `rcon-cli` for RCON queries.
## Game-Specific Behavior
@ -60,4 +62,5 @@ For Plutonium, `BASE_GAME` is derived by stripping the last two chars from `PLUT
- `/home/plutainer/gamefiles` — bind-mounted read-only game files from host
- `/home/plutainer/app` — persistent volume (gamefiles symlinks, plutonium data, configs, logs)
- `/home/plutainer/app/logs` — stable symlinks to active game logs, maintained by `log-watcher.sh`. Host-side IW4MAdmin should bind-mount this dir and read logs from there instead of reaching into nested mod/storage paths.
- `/home/plutainer/.plutainer` — working directory containing scripts, updaters, and pyquake3

View file

@ -69,7 +69,7 @@ RUN IW4X_URL=$(wget -qO- https://api.github.com/repos/iw4x/launcher/releases/lat
# Copy all scripts and the python module into the image
COPY --chown=plutainer:plutainer scripts/ .
RUN chmod +x entrypoint.sh healthcheck.sh plutoentry.sh iw4xentry.sh alterentry.sh rcon-cli game-config.sh
RUN chmod +x entrypoint.sh healthcheck.sh plutoentry.sh iw4xentry.sh alterentry.sh log-watcher.sh rcon-cli game-config.sh
# Add rcon-cli to PATH so it can be invoked without a full path via docker exec
USER root

View file

@ -109,6 +109,8 @@ rm -f /tmp/.X99-lock
Xvfb :99 -screen 0 320x240x24 &
sleep 1
/home/plutainer/.plutainer/log-watcher.sh &
echo "Starting T7x Server: ${ALTER_SERVER_NAME}"
echo "EXECUTING: wine t7x.exe ${CMD_ARGS[@]}"
exec wine t7x.exe "${CMD_ARGS[@]}"

View file

@ -92,6 +92,8 @@ fi
CMD_ARGS+=(+map_rotate)
# --- Step 5: Launch the iw4x Server ---
/home/plutainer/.plutainer/log-watcher.sh &
echo "Starting ${IW4X_GAME} Server: ${IW4X_SERVER_NAME}"
echo "EXECUTING: wine iw4x.exe ${CMD_ARGS[@]}"
exec wine iw4x.exe "${CMD_ARGS[@]}"

72
scripts/log-watcher.sh Normal file
View file

@ -0,0 +1,72 @@
#!/bin/bash
#
# Maintains stable symlinks at /home/plutainer/app/logs/<name> pointing at the
# game log currently being written. Game logs move around per game/mod (e.g.
# plutonium/storage/t5/mods/<mod>/logs/games_zm.log), which makes IW4MAdmin
# configuration brittle. This watcher surfaces them in one predictable dir.
#
# Strategy:
# - Record container boot time.
# - Poll app/ for files matching known log names.
# - The active log is the one with mtime >= boot time (stale logs from prior
# sessions keep their old mtime and are ignored). Ties broken by newest.
# - Update symlink only when target changes (idempotent).
# - Create empty stub files at startup so host-side IW4MAdmin never sees a
# dangling symlink during the window before the first write event.
#
APP_DIR=/home/plutainer/app
STABLE_DIR="$APP_DIR/logs"
POLL_INTERVAL="${PLUTAINER_LOG_POLL_INTERVAL:-2}"
LOG_NAMES=(games_mp.log games_zm.log console_mp.log console_zm.log)
if [[ "${PLUTAINER_LOG_SYMLINKS}" == "false" ]]; then
echo "[log-watcher] disabled via PLUTAINER_LOG_SYMLINKS=false"
exit 0
fi
mkdir -p "$STABLE_DIR"
for name in "${LOG_NAMES[@]}"; do
target="$STABLE_DIR/$name"
if [[ -L "$target" ]]; then
# Leave existing symlinks from prior run in place until a fresh target is
# identified. Preserves continuity for IW4MAdmin across container restarts
# where the old log path is still valid.
:
elif [[ ! -e "$target" ]]; then
touch "$target"
fi
done
BOOT_TS=$(date +%s)
declare -A CURRENT_TARGET
echo "[log-watcher] started; boot_ts=$BOOT_TS stable_dir=$STABLE_DIR"
while true; do
for name in "${LOG_NAMES[@]}"; do
newest_path=""
newest_mtime=0
while IFS= read -r -d '' path; do
mtime=$(stat -c %Y "$path" 2>/dev/null) || continue
(( mtime < BOOT_TS )) && continue
if (( mtime > newest_mtime )); then
newest_mtime=$mtime
newest_path=$path
fi
done < <(find "$APP_DIR" -path "$STABLE_DIR" -prune -o -type f -name "$name" -print0 2>/dev/null)
if [[ -n "$newest_path" && "$newest_path" != "${CURRENT_TARGET[$name]:-}" ]]; then
link="$STABLE_DIR/$name"
rm -f "$link"
ln -s "$newest_path" "$link"
CURRENT_TARGET[$name]=$newest_path
echo "[log-watcher] $name -> $newest_path"
fi
done
sleep "$POLL_INTERVAL"
done

View file

@ -132,6 +132,8 @@ else
fi
# --- Step 5: Launch the Plutonium Server ---
/home/plutainer/.plutainer/log-watcher.sh &
echo "Starting Plutonium ${PLUTO_GAME} Server: ${PLUTO_SERVER_NAME}"
echo "EXECUTING: wine bin/plutonium-bootstrapper-win32.exe ${CMD_ARGS[@]}"
exec wine bin/plutonium-bootstrapper-win32.exe "${CMD_ARGS[@]}"