misc minor fixes
[distro-setup] / btrfsmaint
index 6c7dbb1f6788119cafa9cac1dc2cae559cd2c07b..33cae4a36280351c170155f11fc83ba8ca3b2102 100755 (executable)
@@ -15,7 +15,12 @@ source /a/bin/errhandle/err
 dusage="5 10"
 musage="5"
 
-e() { echo "cron: $*"; "$@"; }
+e() {
+  echo "cron: $*"
+  if ! $dryrun; then
+    "$@"
+  fi
+}
 
 check-idle() {
   type -p xprintidle &>/dev/null || return 0
@@ -36,13 +41,12 @@ check-idle() {
   fi
 }
 
-
 usage() {
   cat <<EOF
-Usage: ${0##*/} args
+Usage: ${0##*/} [ARGS]
 Do btrfs maintence or stop if xprintidle shows a user
 
-force  Run regardless of user idle status
+force  Run regardless of user idle status on all disks.
 check  Only check if an existing maintence should be cancelled due to
          nonidle user. Also, runs in a loop every 20 seconds for 10
          minutes.
@@ -56,6 +60,7 @@ EOF
 
 force=false
 check=false
+dryrun=false
 if [[ $1 ]]; then
   case $1 in
     check)
@@ -64,6 +69,9 @@ if [[ $1 ]]; then
     force)
       force=true
       ;;
+    dryrun)
+      dryrun=true
+      ;;
     *)
       echo "$0: error: unexpected arg" >&2
       usage 1
@@ -76,27 +84,53 @@ main() {
   idle=true
   if ! $force; then
     check-idle
+    if ! $check; then
+      min=0
+      max_min=300
+      # When the cron kicks in, we may not be idle (physically sleeping) yet, so
+      # wait.
+      while ! $idle && (( min < max_min )); do
+        min=$(( min + 1 ))
+        sleep 60
+        check-idle
+      done
+      # If we've waited a really long time for idle, just give up.
+      if (( min == max_min )); then
+        return
+      fi
+    fi
   fi
 
-  tmp=$(mktemp)
 
+  tmp=$(mktemp)
   fnd="findmnt --types btrfs --noheading"
   for x in $($fnd --output "SOURCE" --nofsroot | sort -u); do
     mnt=$($fnd --output "TARGET" --first-only --source $x)
     [[ $mnt ]] || continue
 
-    if ! btrfs dev stats -c $mnt >$tmp; then
-      if diff -q $mnt/btrfs-dev-stats $tmp; then
-        diff -u $mnt/btrfs-dev-stats $tmp | mail -s "$HOSTNAME: error: btrfs dev stats -c $mnt" root@localhost
-        cat $tmp >$mnt/btrfs-dev-stats
+    #### begin look for diff in stats, eg: increasing error count ####
+
+    # Only run for $check, since it runs in parallel to non-check, avoid
+    # race condition.
+    if $check; then
+      if ! btrfs dev stats -c $mnt >$tmp; then
+        if diff -q $mnt/btrfs-dev-stats $tmp; then
+          diff -u $mnt/btrfs-dev-stats $tmp | mail -s "$HOSTNAME: error: btrfs dev stats -c $mnt" root@localhost
+          cat $tmp >$mnt/btrfs-dev-stats
+        fi
       fi
+      rm -f $tmp
     fi
+    #### end look for diff in stats, eg: increasing error count ####
 
-    if ! $idle; then
-      btrfs scrub cancel $mnt &>/dev/null ||:
-      continue
-    fi
     if $check; then
+      if ! $idle; then
+        if $dryrun; then
+          echo "$0: not idle. if this wasnt a dry run, btrfs scrub cancel $mnt"
+        else
+          btrfs scrub cancel $mnt &>/dev/null ||:
+        fi
+      fi
       continue
     fi
 
@@ -118,25 +152,42 @@ main() {
         e ionice -c 3 btrfs balance start -musage=$usage $mnt
       done
     fi
-    # e btrfs filesystem df $mnt
-    # e df -H $mnt
-    date=$(
-      btrfs scrub status $mnt | \
-        sed -rn 's/^\s*scrub started at (.*) and finished.*/\1/p'
-        )
+    date=
+    scrub_status=$(btrfs scrub status $mnt)
+    if printf "%s\n" "$scrub_status" | grep -i '^status:[[:space:]]*finished$' &>/dev/null; then
+      date=$(printf "%s\n" "$scrub_status" | sed -rn 's/^Scrub started:[[:space:]]*(.*)/\1/p')
+    fi
+    if [[ ! $date ]]; then
+      # output from older versions, at least btrfs v4.15.1
+      date=$(
+        printf "%s\n" "$scrub_status" | \
+          sed -rn 's/^\s*scrub started at (.*) and finished.*/\1/p'
+          )
+    fi
     if [[ $date ]]; then
+      if $dryrun; then
+        echo "$0: last scrub finish for $mnt: $date"
+      fi
       date=$(date --date="$date" +%s)
-      # if date is sooner than 90 days ago
+      # if date is sooner than 60 days ago
       # the wiki recommends 30 days or so, but
-      # it makes the comp lag like shit for a day,
-      # so I'm going with 90 days.
-      if (( date > $(date +%s) - 60*60*24*30 )); then
-        echo "cron: skiping scrub of $mnt"
+      # I'm going with 60 days.
+      if (( date > EPOCHSECONDS - 60*60*24*60 )); then
+        if $dryrun; then
+          echo "$0: skiping scrub of $mnt, last was $(( (EPOCHSECONDS - date) / 60/60/24 )) days ago, < 30 days"
+        fi
         continue
       fi
     fi
     # -c 2 -n 4 is from btrfsmaintenance, does ionice
     e btrfs scrub start -Bd -c 2 -n 4 $mnt
+
+    # We normally only do one disk since this is meant to be run while I sleep
+    # and if we try to do all disks, we invariably end up doing a scrub still
+    # after I've woken up. So, just do one per day.
+    if ! $force; then
+      return 0
+    fi
   done
 }