dot

NixOS dotfiles
git clone https://git.echoz.io/dot.git
Log | Files | Refs

prune-roots.sh (3919B)


      1 argv0="prune-roots"
      2 
      3 help() {
      4 cat <<EOF
      5 $argv0 - Prune non-current root filesystems.
      6 
      7 Usage: $argv0 [options...]
      8 
      9 Options:
     10   -k, --keep=n
     11     Keep the latest n roots.
     12 
     13   -b, --before=date
     14     Prune roots from before date.
     15 
     16   -y, --yes
     17     Delete filesystems without confirmation.
     18 
     19   -n, --dry-run
     20     List filesystems that would be deleted.
     21 
     22   -l, --list
     23     List all non-current filesystems along with their creation times.
     24 
     25   -h, --help
     26     Display this message.
     27 
     28 If both --keep and --before are specified, a filesystem will need to
     29 match both filters to be selected for deletion.
     30 EOF
     31 }
     32 
     33 list() {
     34   local filesystems=("$@")
     35   (
     36     printf "AGE NAME CREATION\n"
     37     for i in "${!filesystems[@]}"; do
     38       age="$((${#filesystems[@]}-$i))"
     39       fs="${filesystems[$i]}"
     40       creation="$(date -d "@$(zfs get -Hpo value creation "$fs")")"
     41       printf "%d %s %s\n" "$age" "$fs" "$creation"
     42     done
     43   ) | column -tl3
     44 }
     45 
     46 keep=""
     47 before=""
     48 confirmation=""
     49 dry=""
     50 
     51 readarray -t filesystems < <(\
     52   zfs list -t filesystem -Ho name -s creation \
     53     | awk '/^system\/root\// && !/^system\/root\/current$/' \
     54 )
     55 
     56 while [ $# -gt 0 ]; do
     57   case "$1" in
     58     -k*|--keep|--keep=*)
     59         keep="${1/#-k/}"
     60         if [ -z "$keep" ] || [ "$keep" = "$1" ]; then
     61           keep="${1#*=}"
     62         fi
     63         if [ -z "$keep" ] || [ "$keep" = "$1" ]; then
     64           if [ $# -lt 2 ]; then
     65             printf "Error: Flag needs an argument: %s\n" "$1" >&2
     66             exit 1
     67           fi
     68           keep="$2"
     69           shift
     70         fi
     71         shift
     72         if [ ! "$keep" -ge 0 ]; then
     73           printf "Error: Invalid value for --keep: Must be an unsigned integer.\n" >&2
     74           exit 1
     75         fi
     76       ;;
     77     -b*|--before*)
     78         before="${1/#-b/}"
     79         if [ -z "$before" ] || [ "$before" = "$1" ]; then
     80           before="${1#*=}"
     81         fi
     82         if [ -z "$before" ] || [ "$before" = "$1" ]; then
     83           if [ $# -lt 2 ]; then
     84             printf "Error: Flag needs an argument: %s\n" "$1" >&2
     85             exit 1
     86           fi
     87           before="$2"
     88           shift
     89         fi
     90         shift
     91         if date -d "$before" 2>/dev/null >/dev/null; then
     92           before="$(date -d "$before" +%s)"
     93         else
     94           error="$(date -d "$before" 2>&1)"
     95           error="${error#date: }"
     96           printf "Error: Invalid value for --before: %s\n" "${error^}" >&2
     97           exit 1
     98         fi
     99       ;;
    100     -h*|--help*)
    101       help
    102       exit 0
    103       ;;
    104     -y|--yes)
    105       confirmation="Yes"
    106       shift
    107       ;;
    108     -n|--dry-run)
    109       dry=1
    110       shift
    111       ;;
    112     -l|--list)
    113       list "${filesystems[@]}"
    114       exit 0
    115       ;;
    116     *)
    117       printf "Error: Unknown argument: %s\n" "$1" >&2
    118       exit 1
    119   esac
    120 done
    121 
    122 delete=()
    123 for i in "${!filesystems[@]}"; do
    124   fs="${filesystems[$i]}"
    125   creation="$(zfs get -Hpo value creation "$fs")"
    126   if [ ! -z "$before" ] && [ "$creation" -ge "$before" ]; then
    127     continue
    128   fi
    129   if [ ! -z "$keep" ] && [ $((${#filesystems[@]}-$i)) -le "$keep" ]; then
    130     continue
    131   fi
    132   delete+=($fs)
    133 done
    134 
    135 if [ ${#delete[@]} -le 0 ]; then
    136   printf "Nothing to do: No matching filesystems.\n" >&2
    137   exit 0
    138 fi
    139 
    140 list "${delete[@]}"
    141 
    142 if [ ! -z "$dry" ]; then
    143   exit 0
    144 fi
    145 
    146 if [ "$confirmation" != "Yes" ]; then
    147   printf "\n"
    148   read -r -p "Type 'Yes' to confirm deletion of ${#delete[@]} volumes: " confirmation >&2
    149   if [ "$confirmation" != "Yes" ]; then
    150     printf "Abort.\n" >&2
    151     exit 1
    152   fi
    153 fi
    154 
    155 for i in "${!delete[@]}"; do
    156   fs="${filesystems[$i]}"
    157   elevate=""
    158   if [ $UID -ne 0 ]; then
    159     elevate="sudo"
    160     error=$($elevate true 2>&1)
    161     if [ $? -ne 0 ]; then
    162       error="${error#sudo: }"
    163       printf "Error: Could not elevate privileges: %s\n" "${error^}" >&2
    164       exit 1
    165     fi
    166   fi
    167   printf "Deleting filesystem '%s'...\n" "$fs" >&2
    168   error="$($elevate zfs destroy "$fs" 2>&1)"
    169   if [ $? -ne 0 ]; then
    170     printf "Error: Could not delete filesystem: %s\n" "${error^}" >&2
    171     exit 1
    172   fi
    173 done
    174