dusage="5 10"
musage="5"
-e() { echo "cron: $*"; "$@"; }
+e() {
+ echo "cron: $*"
+ if ! $dryrun; then
+ "$@"
+ fi
+}
check-idle() {
type -p xprintidle &>/dev/null || return 0
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.
force=false
check=false
+dryrun=false
if [[ $1 ]]; then
case $1 in
check)
force)
force=true
;;
+ dryrun)
+ dryrun=true
+ ;;
*)
echo "$0: error: unexpected arg" >&2
usage 1
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
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
}
-if $check; then
- # this is to prevent systemd from filling up the journal
- for (( runcount=0; runcount < 90; runcount++ )); do
+loop-main() {
+ while true; do
main
sleep 60
done
+}
+
+if $check; then
+ loop-main
else
main
fi