#!/bin/bash # Varaverk job runner — wraps script execution with JSON status tracking. # Called by /etc/cron.d/varaverk for every scheduled job. # # Usage: bash run_job.sh [flags...] # # Flags consumed by run_job.sh (stripped before passing to script): # --manual — marks a UI-triggered run; writes a sentinel on completion so # the next cron fire is suppressed if it falls within the job's # own cron interval (prevents double-firing after manual run). # # Writes: /var/log/varaverk/.json — status, timestamps, exit code, pid # /var/log/varaverk/.log — appended per run, trimmed to LOG_MAX_LINES # /var/log/varaverk/.manual_ts — sentinel: epoch of last manual completion # # Status values: running → ok (exit 0) | warn (exit 1) | error (exit 2+) JOB_ID="$1" SCRIPT="$2" shift 2 LOG_DIR="/var/log/varaverk" BASE="${JOB_ID%.sh}" LOG_FILE="$LOG_DIR/$BASE.log" STAT_FILE="$LOG_DIR/$BASE.json" MANUAL_TS_FILE="$LOG_DIR/$BASE.manual_ts" LOG_MAX_LINES=1000 # Strip --manual from script args — it's for run_job.sh only MANUAL=false SCRIPT_ARGS=() for _arg in "$@"; do [[ "$_arg" == "--manual" ]] && MANUAL=true || SCRIPT_ARGS+=("$_arg") done unset _arg mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$STAT_FILE")" # Cron invocation: skip if a manual run completed recently within this job's own interval. if [[ "$MANUAL" == false && -f "$MANUAL_TS_FILE" ]]; then LAST_MANUAL=$(cat "$MANUAL_TS_FILE" 2>/dev/null || echo 0) ELAPSED=$(( $(date +%s) - LAST_MANUAL )) # Only suppress for interval-based crons (*/N * * * *). # Static schedules like "30 2 * * 0" run at their appointed time and are never suppressed. INTERVAL=0 SCHEDULE_FILE="/boot/config/plugins/varaverk/schedule.json" if [[ -f "$SCHEDULE_FILE" ]]; then CRON_EXPR=$(php -r " \$s = json_decode(file_get_contents('$SCHEDULE_FILE'), true) ?: []; echo \$s['$JOB_ID']['cron'] ?? ''; " 2>/dev/null) if [[ "$CRON_EXPR" =~ ^\*/([0-9]+) ]]; then INTERVAL=$(( ${BASH_REMATCH[1]} * 60 )) fi fi if (( INTERVAL > 0 && ELAPSED < INTERVAL )); then printf '\n── %s [SKIPPED — ran manually %ds ago, interval %ds] ────────\n' \ "$(date '+%Y-%m-%d %H:%M:%S')" "$ELAPSED" "$INTERVAL" >> "$LOG_FILE" printf '{"id":"%s","status":"skipped","manual_elapsed":%s,"interval":%s}\n' \ "$JOB_ID" "$ELAPSED" "$INTERVAL" > "$STAT_FILE" rm -f "$MANUAL_TS_FILE" exit 0 fi rm -f "$MANUAL_TS_FILE" fi START=$(date +%s) printf '{"id":"%s","status":"running","start":%s,"pid":%s}\n' \ "$JOB_ID" "$START" "$$" > "$STAT_FILE" printf '\n── %s ────────────────────────────────────────────────\n' \ "$(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG_FILE" bash "$SCRIPT" "${SCRIPT_ARGS[@]}" >> "$LOG_FILE" 2>&1 EC=$? END=$(date +%s) if [ "$EC" -eq 0 ]; then STATUS="ok" elif [ "$EC" -eq 1 ]; then STATUS="warn" else STATUS="error" fi printf '{"id":"%s","status":"%s","start":%s,"end":%s,"exit":%s}\n' \ "$JOB_ID" "$STATUS" "$START" "$END" "$EC" > "$STAT_FILE" # Write manual sentinel so cron can skip the next fire within the interval if [[ "$MANUAL" == true ]]; then echo "$END" > "$MANUAL_TS_FILE" fi # Trim to last LOG_MAX_LINES to prevent unbounded growth line_count=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0) if [ "$line_count" -gt "$LOG_MAX_LINES" ]; then tail -n "$LOG_MAX_LINES" "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE" fi exit $EC