Dirvish Cleanup

From HyperSecurity Wiki
Jump to: navigation, search

Script that will do a dry-run of outdated files to be deleted for Dirvish. Once dry-run is completed and verified, a full run can be completed.

#!/usr/bin/env bash
# cleanup.sh / dirvish_purge_dates.sh
# Delete snapshot directories named YYYY-MM-DD (or loose prefix) under given roots.
# Dry-run preview by default. Use --force to actually delete.

set -euo pipefail

DRY_RUN=1
YES=0
LOOSE=0
YEARS=(2023 2024)
ROOTS=()
EXCLUDES=()
LOGFILE="/var/log/dirvish_purge_dates.log"

usage() {
cat <<'EOF'
Usage: cleanup.sh [options] <ROOT> [ROOT ...]

Options:
  -f, --force        Actually delete (default is dry-run preview).
  -y, --yes          Auto-confirm when prompted (still dry-run unless --force).
  --loose            Match prefix YYYY-* (not strict YYYY-MM-DD).
  --years LIST       Comma-separated years (e.g. 2021,2022,2023).
  --exclude LIST     Comma-separated dir-name patterns to skip.
  --log FILE         Path to log file (default: /var/log/dirvish_purge_dates.log).
  -h, --help         Show this help.
EOF
}

# -----------------------------
# Parse arguments
# -----------------------------
while [[ $# -gt 0 ]]; do
  case "$1" in
    -f|--force) DRY_RUN=0; shift ;;
    -y|--yes) YES=1; shift ;;
    --loose) LOOSE=1; shift ;;
    --years)
      [[ $# -ge 2 ]] || { echo "Error: --years needs a value" >&2; exit 1; }
      IFS=',' read -r -a YEARS <<< "$2"
      shift 2 ;;
    --exclude)
      [[ $# -ge 2 ]] || { echo "Error: --exclude needs a value" >&2; exit 1; }
      IFS=',' read -r -a EXCLUDES <<< "$2"
      shift 2 ;;
    --log)
      [[ $# -ge 2 ]] || { echo "Error: --log needs a file path" >&2; exit 1; }
      LOGFILE="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    --) shift; break ;;
    -*) echo "Unknown option: $1" >&2; usage; exit 1 ;;                                                                                                                                       
     *) ROOTS+=("$1"); shift ;;                                                                                                                                                               
  esac                                                                                                                                                                                        
done                                                                                                                                                                                          
                                                                                                                                                                                              
if [[ $# -gt 0 ]]; then ROOTS+=("$@"); fi                                                                                                                                                     
if [[ ${#ROOTS[@]} -eq 0 ]]; then                                                                                                                                                             
  echo "Error: provide at least one ROOT directory." >&2                                                                                                                                      
  usage; exit 1                                                                                                                                                                               
fi                                                                                                                                                                                            
                                                                                                                                                                                              
# -----------------------------                                                                                                                                                               
# Safety checks                                                                                                                                                                               
# -----------------------------                                                                                                                                                               
for r in "${ROOTS[@]}"; do                                                                                                                                                                    
  [[ -d "$r" ]] || { echo "Error: not a directory: $r" >&2; exit 1; }                                                                                                                         
  [[ "$r" == "/" ]] && { echo "Refusing to operate on root /" >&2; exit 1; }                                                                                                                  
done                                                                                                                                                                                          
                                                                                                                                                                                              
if [[ $DRY_RUN -eq 0 ]]; then                                                                                                                                                                 
  mkdir -p "$(dirname "$LOGFILE")"                                                                                                                                                            
  touch "$LOGFILE" || { echo "Cannot write log file: $LOGFILE" >&2; exit 1; }                                                                                                                 
fi                                                                                                                                                                                            
                                                                                                                                                                                              
join_alt() { local IFS='|'; echo "${YEARS[*]}"; }                                                                                                                                             
                                                                                                                                                                                              
# -----------------------------                                                                                                                                                               
# Build exclusion arguments (returns a string)                                                                                                                                                
# -----------------------------                                                                                                                                                               
build_exclude_args() {                                                                                                                                                                        
  local args=()                                                                                                                                                                               
  if (( ${#EXCLUDES[@]} )); then                                                                                                                                                              
    for pat in "${EXCLUDES[@]}"; do                                                                                                                                                           
      args+=( -not -path "*/${pat}" -not -path "*/${pat}/*" )                                                                                                                                 
    done                                                                                                                                                                                      
  fi                                                                                                                                                                                          
  echo "${args[@]:-}"                                                                                                                                                                         
}                                                                                                                                                                                             
                                                                                                                                                                                              
# -----------------------------                                                                                                                                                               
# Build find command as array                                                                                                                                                                 
# -----------------------------                                                                                                                                                               
build_find_cmd() {                                                                                                                                                                            
  local root=$1                                                                                                                                                                               
  local excl_args                                                                                                                                                                             
  excl_args=$(build_exclude_args)                                                                                                                                                             
                                                                                                                                                                                              
  if (( LOOSE )); then                                                                                                                                                                        
    local cmd=(find "$root" -type d "(")                                                                                                                                                      
    local first=1                                                                                                                                                                             
    for y in "${YEARS[@]}"; do                                                                                                                                                                
      if (( first )); then                                                                                                                                                                    
        cmd+=( -name "${y}-*" )                                                                                                                                                               
        first=0                                                                                                                                                                               
      else                                                                                                                                                                                    
        cmd+=( -o -name "${y}-*" )                                                                                                                                                            
      fi                                                                                                                                                                                      
    done                                                                                                                                                                                      
    cmd+=( ")" )                                                                                                                                                                              
    if [[ -n "$excl_args" ]]; then cmd+=( $excl_args ); fi                                                                                                                                    
    cmd+=( -prune -print0 )                                                                                                                                                                   
    printf '%s\0' "${cmd[@]}" | tr '\0' ' '                                                                                                                                                   
  else                                                                                                                                                                                        
    local yrs                                                                                                                                                                                 
    yrs=$(join_alt)                                                                                                                                                                           
    local cmd=(find "$root" -regextype posix-extended -type d \                                                                                                                               
               -regex ".*/(${yrs})-[0-9]{2}-[0-9]{2}$" )                                                                                                                                      
    if [[ -n "$excl_args" ]]; then cmd+=( $excl_args ); fi                                                                                                                                    
    cmd+=( -prune -print0 )                                                                                                                                                                   
    printf '%s\0' "${cmd[@]}" | tr '\0' ' '                                                                                                                                                   
  fi                                                                                                                                                                                          
}                                                                                                                                                                                             
                                                                                                                                                                                              
log_msg() {                                                                                                                                                                                   
  [[ $DRY_RUN -eq 0 ]] && echo "[$(date '+%F %T')] $*" >>"$LOGFILE"                                                                                                                           
}                                                                                                                                                                                             
                                                                                                                                                                                              
# -----------------------------                                                                                                                                                               
# Main                                                                                                                                                                                        
# -----------------------------                                                                                                                                                               
echo "=== $( ((DRY_RUN)) && echo 'DRY RUN' || echo 'DELETION MODE (--force)') ==="                                                                                                            
(( ${#EXCLUDES[@]} )) && echo "Excluding patterns: ${EXCLUDES[*]}"                                                                                                                            
echo "Log file: $LOGFILE"                                                                                                                                                                     
                                                                                                                                                                                              
total=0                                                                                                                                                                                       
                                                                                                                                                                                              
for r in "${ROOTS[@]}"; do                                                                                                                                                                    
  echo "Scanning: $r"                                                                                                                                                                         
  cmd_array=($(build_find_cmd "$r"))                                                                                                                                                          
                                                                                                                                                                                              
  count=$("${cmd_array[@]}" | tr -cd '\0' | wc -c | tr -d ' ')                                                                                                                                
  echo "  Matches: $count"                                                                                                                                                                    
  total=$(( total + count ))                                                                                                                                                                  
                                                                                                                                                                                              
  if (( count > 0 )); then                                                                                                                                                                    
    echo "  Preview (up to 30):"                                                                                                                                                              
    "${cmd_array[@]/-print0/-print}" | head -n 30 | sed 's/^/    /'                                                                                                                           
                                                                                                                                                                                              
    if (( DRY_RUN == 0 )); then                                                                                                                                                               
      echo "  Deleting..."                                                                                                                                                                    
      "${cmd_array[@]}" | xargs -0 -I{} sh -c 'echo "    removed: {}"; rm -rf -- "{}"'                                                                                                        
      "${cmd_array[@]}" | xargs -0 -I{} bash -c \                                                                                                                                             
        'printf "[%s] removed: %s\n" "$(date "+%F %T")" "{}"' >>"$LOGFILE"                                                                                                                    
    fi                                                                                                                                                                                        
  fi                                                                                                                                                                                          
done                                                                                                                                                                                          
                                                                                                                                                                                              
echo "Total matches across roots: $total"                                                                                                                                                     
                                                                                                                                                                                              
if (( DRY_RUN == 1 && total > 0 )); then                                                                                                                                                      
  echo                                                                                                                                                                                        
  if (( YES == 0 )); then                                                                                                                                                                     
    read -r -p "Type DELETE to remove these directories now (or Enter to abort): " ans                                                                                                        
    [[ "$ans" == "DELETE" ]] || { echo "Aborted. Nothing deleted."; exit 0; }                                                                                                                 
  fi                                                                                                                                                                                          
                                                                                                                                                                                              
  exec_args=( --force )                                                                                                                                                                       
  (( LOOSE )) && exec_args+=( --loose )                                                                                                                                                       
  exec_args+=( --years "$(IFS=','; echo "${YEARS[*]}")" )                                                                                                                                     
  (( ${#EXCLUDES[@]} )) && exec_args+=( --exclude "$(IFS=','; echo "${EXCLUDES[*]}")" )                                                                                                       
  exec_args+=( --log "$LOGFILE" )                                                                                                                                                             
  exec_args+=( "${ROOTS[@]}" )                                                                                                                                                                
                                                                                                                                                                                              
  exec "$0" "${exec_args[@]}"                                                                                                                                                                 
else                                                                                                                                                                                          
  (( DRY_RUN )) && echo "Dry-run complete. Nothing deleted."                                                                                                                                  
fi