etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

backup-crashes.sh (3355B)

      1 #!/bin/bash
      2 # backup-crashes.sh — archive unique syzkaller crashes with deduplication.
      3 #
      4 # Intended to run as a cron job or in a background loop:
      5 #   crontab -e
      6 #   */15 * * * *  /path/to/linux-usb-stack/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 # syzkaller stores crashes as directories under workdir-*/crashes/<id>/
     12 # Each crash dir contains: description, log0, report0 (and possibly log1, report1, etc.)
     13 #
     14 # This script:
     15 #   1. Scans workdir-*/crashes/ for crash dirs newer than .last_backup stamp
     16 #   2. Deduplicates by sha256 of the report0 file (unique crash signatures)
     17 #   3. Compresses unique new crashes into backups/crashes_YYYYMMDD_HHMMSS.tar.gz
     18 #   4. Updates .last_backup stamp
     19 
     20 set -euo pipefail
     21 
     22 SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)"
     23 cd "${SCRIPT_DIR}"
     24 
     25 BACKUP_DIR="${SCRIPT_DIR}/backups"
     26 STAMP_FILE="${SCRIPT_DIR}/.last_backup"
     27 SEEN_HASHES="${SCRIPT_DIR}/.seen_crash_hashes"
     28 
     29 log()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
     30 
     31 mkdir -p "${BACKUP_DIR}"
     32 touch "${STAMP_FILE}" "${SEEN_HASHES}"
     33 
     34 # ── find new crash directories ────────────────────────────────────────────────
     35 # syzkaller crash dirs contain a 'report0' file with the kernel crash report
     36 TMPLIST="$(mktemp)"
     37 trap "rm -f ${TMPLIST}" EXIT
     38 
     39 find workdir-*/crashes/ -maxdepth 2 -name "report0" -newer "${STAMP_FILE}" \
     40     2>/dev/null > "${TMPLIST}" || true
     41 
     42 TOTAL=$(wc -l < "${TMPLIST}")
     43 if [[ "${TOTAL}" -eq 0 ]]; then
     44     log "No new crashes since last backup."
     45     exit 0
     46 fi
     47 
     48 log "Found ${TOTAL} new crash report(s) since last backup."
     49 
     50 # ── deduplicate by sha256 of report0 ─────────────────────────────────────────
     51 TMPSTAGE="$(mktemp -d)"
     52 trap "rm -rf ${TMPSTAGE} ${TMPLIST}" EXIT
     53 
     54 NEW=0
     55 while IFS= read -r report; do
     56     hash="$(sha256sum "${report}" | cut -d' ' -f1)"
     57     if ! grep -qF "${hash}" "${SEEN_HASHES}" 2>/dev/null; then
     58         # Archive the entire crash directory (includes description, logs, reports)
     59         crash_dir="$(dirname "${report}")"
     60         crash_name="$(basename "${crash_dir}")"
     61         cp -r "${crash_dir}" "${TMPSTAGE}/${crash_name}"
     62         echo "${hash}" >> "${SEEN_HASHES}"
     63         NEW=$((NEW + 1))
     64     fi
     65 done < "${TMPLIST}"
     66 
     67 if [[ "${NEW}" -eq 0 ]]; then
     68     log "All ${TOTAL} new reports were duplicates. Nothing to archive."
     69     touch "${STAMP_FILE}"
     70     exit 0
     71 fi
     72 
     73 # ── create archive ────────────────────────────────────────────────────────────
     74 ARCHIVE="${BACKUP_DIR}/crashes_$(date +%Y%m%d_%H%M%S).tar.gz"
     75 tar -czf "${ARCHIVE}" -C "${TMPSTAGE}" .
     76 
     77 SIZE="$(du -sh "${ARCHIVE}" | cut -f1)"
     78 log "Archived ${NEW} unique crash(es) → ${ARCHIVE} (${SIZE})"
     79 
     80 touch "${STAMP_FILE}"
     81 
     82 # ── prune old backups (keep last 50) ─────────────────────────────────────────
     83 ls -t "${BACKUP_DIR}"/crashes_*.tar.gz 2>/dev/null | tail -n +51 | while read old; do
     84     rm -f "${old}"
     85     log "Pruned old backup: $(basename "${old}")"
     86 done
     87 
     88 exit 0