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