#!/usr/bin/env bash ## ols-htaccess-watcher.sh — graceful-restart the shared OLS when any tenant's ## .htaccess changes. OLS reads .htaccess (RewriteFile) at (re)start, NOT per ## request, so without this a WordPress permalink/LiteSpeed-Cache change would ## silently not take effect. Required by spec 5.3. ## ## Watches all docroots for .htaccess writes, COALESCES bursts (a WP plugin save ## touches the file several times) within a debounce window, and RATE-LIMITS to ## a floor (one restart per FLOOR seconds) so many tenants saving at once can't ## trigger a restart storm. Debounce/floor are env-tunable (panel discloses the ## resulting "~60s" window to customers). ## ## Failure of THIS process is the silent-ticket failure mode (spec 7): if it ## dies, tenants' rewrite changes stop applying with no error. The entrypoint ## runs it and the panel monitors it (check-ols-htaccess-watcher.php). set -uo pipefail WATCH_ROOT="${OLS_WATCH_ROOT:-/mnt/users}" DEBOUNCE="${OLS_HTACCESS_DEBOUNCE:-15}" # coalesce window (s) FLOOR="${OLS_HTACCESS_FLOOR:-60}" # min seconds between restarts LSWSCTRL=/usr/local/lsws/bin/lswsctrl last_restart=0 log() { echo "ols-htaccess-watcher: $*" >&2; } do_restart() { now=$(date +%s) if [ $((now - last_restart)) -lt "$FLOOR" ]; then log "within ${FLOOR}s floor — coalescing, skipping restart" return fi if "$LSWSCTRL" restart >/dev/null 2>&1; then last_restart=$now log "graceful restart issued (.htaccess change)" else log "WARNING: lswsctrl restart failed" fi } if ! command -v inotifywait >/dev/null 2>&1; then log "FATAL: inotifywait not installed (inotify-tools)"; exit 1 fi mkdir -p "$WATCH_ROOT" log "watching $WATCH_ROOT for .htaccess changes (debounce=${DEBOUNCE}s floor=${FLOOR}s)" ## -m monitor, -r recursive. We filter to .htaccess in the read loop rather than ## --include so this works on older inotify-tools too. modify/create/delete/move ## all matter (delete of .htaccess also changes rewrite behavior). inotifywait -m -r -e modify,create,delete,move "$WATCH_ROOT" --format '%f' 2>/dev/null | while read -r fname; do case "$fname" in .htaccess) ;; *) continue ;; esac ## Drain further events for DEBOUNCE seconds (coalesce the burst), then act. while read -r -t "$DEBOUNCE" _; do :; done do_restart done