070b50185b4213270713a0685530e3ac89145f02
[distro-setup] / btrfsmaint
1 #!/bin/bash
2 set -eE -o pipefail
3 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
4
5
6 [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
7 # inspired from
8 # https://github.com/kdave/btrfsmaintenance
9
10
11 # Man page says we could also use a range, i suppose it would be
12 # logical to use a pattern like 5..10 10..20,
13 # but I don't know if this would help us at all.
14 dusage="5 10"
15 musage="5"
16
17 e() { echo "cron: $*"; "$@"; }
18
19 check-idle() {
20 type -p xprintidle &>/dev/null || return 0
21 export DISPLAY=:0
22 # a hours, a movie could run that long.
23 idle_limit=$((1000 * 60 * 60 * 2))
24 idle_time=$idle_limit
25 while read -r user; do
26 new_idle_time=$(sudo -u $user xprintidle 2>/dev/null) ||:
27 if [[ $new_idle_time && $new_idle_time -lt $idle_time ]]; then
28 idle_time=$new_idle_time
29 fi
30 done < <(users | tr " " "\n" | sort -u)
31 if (( idle_time < idle_limit )); then
32 idle=false
33 else
34 idle=true
35 fi
36 }
37
38
39 usage() {
40 cat <<EOF
41 Usage: ${0##*/} args
42 Do btrfs maintence or stop if xprintidle shows a user
43
44 force Run regardless of user idle status
45 check Only check if an existing maintence should be cancelled due to
46 nonidle user. Also, runs in a loop every 20 seconds for 10
47 minutes.
48
49 Note: Uses util-linux getopt option parsing: spaces between args and
50 options, short options can be combined, options before args.
51 EOF
52 exit $1
53 }
54
55
56 force=false
57 check=false
58 if [[ $1 ]]; then
59 case $1 in
60 check)
61 check=true
62 ;;
63 force)
64 force=true
65 ;;
66 *)
67 echo "$0: error: unexpected arg" >&2
68 usage 1
69 ;;
70 esac
71 fi
72
73
74 main() {
75 idle=true
76 if ! $force; then
77 check-idle
78 fi
79
80 tmp=$(mktemp)
81
82 fnd="findmnt --types btrfs --noheading"
83 for x in $($fnd --output "SOURCE" --nofsroot | sort -u); do
84 mnt=$($fnd --output "TARGET" --first-only --source $x)
85 [[ $mnt ]] || continue
86
87 if ! btrfs dev stats -c $mnt >$tmp; then
88 if diff -q $mnt/btrfs-dev-stats $tmp; then
89 diff -u $mnt/btrfs-dev-stats $tmp | mail -s "$HOSTNAME: error: btrfs dev stats -c $mnt" root@localhost
90 cat $tmp >$mnt/btrfs-dev-stats
91 fi
92 fi
93
94 if ! $idle; then
95 btrfs scrub cancel $mnt &>/dev/null ||:
96 continue
97 fi
98 if $check; then
99 continue
100 fi
101
102 # for comparing before and after balance.
103 # the log is already fairly verbose, so commented.
104 # e btrfs filesystem df $mnt
105 # e df -H $mnt
106 if btrfs filesystem df $mnt | grep -q "Data+Metadata"; then
107 for usage in $dusage; do
108 e ionice -c 3 btrfs balance start -dusage=$usage -musage=$usage $mnt
109 done
110 else
111 e ionice -c 3 btrfs balance start -dusage=0 $mnt
112 for usage in $dusage; do
113 e ionice -c 3 btrfs balance start -dusage=$usage $mnt
114 done
115 e ionice -c 3 btrfs balance start -musage=0 $mnt
116 for usage in $musage; do
117 e ionice -c 3 btrfs balance start -musage=$usage $mnt
118 done
119 fi
120 # e btrfs filesystem df $mnt
121 # e df -H $mnt
122 date=$(
123 btrfs scrub status $mnt | \
124 sed -rn 's/^\s*scrub started at (.*) and finished.*/\1/p'
125 )
126 if [[ $date ]]; then
127 date=$(date --date="$date" +%s)
128 # if date is sooner than 90 days ago
129 # the wiki recommends 30 days or so, but
130 # it makes the comp lag like shit for a day,
131 # so I'm going with 90 days.
132 if (( date > $(date +%s) - 60*60*24*30 )); then
133 echo "cron: skiping scrub of $mnt"
134 continue
135 fi
136 fi
137 # -c 2 -n 4 is from btrfsmaintenance, does ionice
138 e btrfs scrub start -Bd -c 2 -n 4 $mnt
139 done
140 }
141
142 if $check; then
143 # this is to prevent systemd from filling up the journal
144 for (( runcount=0; runcount < 90; runcount++ )); do
145 main
146 sleep 60
147 done
148 else
149 main
150 fi