etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
backup-crashes.sh (3081B)
1 #!/bin/bash 2 # backup-crashes.sh — archive unique AFL++ crashes with deduplication. 3 # 4 # Intended to run as a cron job or in a background loop: 5 # crontab -e 6 # */15 * * * * /path/to/usbip/backup-crashes.sh >> /tmp/crash-backup.log 2>&1 7 # 8 # Or in a loop from another terminal: 9 # while sleep 900; do bash backup-crashes.sh; done 10 # 11 # What it does: 12 # 1. Scans output/*/crashes/ for crash files newer than .last_backup stamp 13 # 2. Deduplicates by sha256 (avoids archiving the same crash twice) 14 # 3. Compresses unique new crashes into backups/crashes_YYYYMMDD_HHMMSS.tar.gz 15 # 4. Updates .last_backup stamp 16 17 set -euo pipefail 18 19 SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" 20 cd "${SCRIPT_DIR}" 21 22 OUTPUT_DIR="${SCRIPT_DIR}/output" 23 BACKUP_DIR="${SCRIPT_DIR}/backups" 24 STAMP_FILE="${SCRIPT_DIR}/.last_backup" 25 SEEN_HASHES="${SCRIPT_DIR}/.seen_crash_hashes" 26 27 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } 28 29 mkdir -p "${BACKUP_DIR}" 30 touch "${STAMP_FILE}" "${SEEN_HASHES}" 31 32 # ── find new crash files ────────────────────────────────────────────────────── 33 TMPLIST="$(mktemp)" 34 trap "rm -f ${TMPLIST}" EXIT 35 36 # Collect crash files from all AFL++ fuzzer subdirs 37 find "${OUTPUT_DIR}" -path "*/crashes/id:*" -newer "${STAMP_FILE}" \ 38 -not -name "README.txt" \ 39 2>/dev/null > "${TMPLIST}" || true 40 41 TOTAL=$(wc -l < "${TMPLIST}") 42 if [[ "${TOTAL}" -eq 0 ]]; then 43 log "No new crashes since last backup." 44 exit 0 45 fi 46 47 log "Found ${TOTAL} new crash file(s) since last backup." 48 49 # ── deduplicate by sha256 ───────────────────────────────────────────────────── 50 TMPSTAGE="$(mktemp -d)" 51 trap "rm -rf ${TMPSTAGE} ${TMPLIST}" EXIT 52 53 NEW=0 54 while IFS= read -r crash; do 55 hash="$(sha256sum "${crash}" | cut -d' ' -f1)" 56 if ! grep -qF "${hash}" "${SEEN_HASHES}" 2>/dev/null; then 57 cp "${crash}" "${TMPSTAGE}/" 58 echo "${hash}" >> "${SEEN_HASHES}" 59 NEW=$((NEW + 1)) 60 fi 61 done < "${TMPLIST}" 62 63 if [[ "${NEW}" -eq 0 ]]; then 64 log "All ${TOTAL} new files were duplicates. Nothing to archive." 65 touch "${STAMP_FILE}" 66 exit 0 67 fi 68 69 # ── create archive ──────────────────────────────────────────────────────────── 70 ARCHIVE="${BACKUP_DIR}/crashes_$(date +%Y%m%d_%H%M%S).tar.gz" 71 tar -czf "${ARCHIVE}" -C "${TMPSTAGE}" . 72 73 SIZE="$(du -sh "${ARCHIVE}" | cut -f1)" 74 log "Archived ${NEW} unique crash(es) → ${ARCHIVE} (${SIZE})" 75 76 # Update stamp AFTER successful archive 77 touch "${STAMP_FILE}" 78 79 # ── prune old backups (keep last 50) ───────────────────────────────────────── 80 ls -t "${BACKUP_DIR}"/crashes_*.tar.gz 2>/dev/null | tail -n +51 | while read old; do 81 rm -f "${old}" 82 log "Pruned old backup: $(basename "${old}")" 83 done 84 85 exit 0