4 [[ $EUID == 0 ]] ||
exec sudo
-E "${BASH_SOURCE[0]}" "$@"
6 source /a
/bin
/errhandle
/err
9 # https://github.com/kdave/btrfsmaintenance
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.
26 type -p xprintidle
&>/dev
/null ||
return 0
28 # a hours, a movie could run that long.
29 idle_limit
=$
((1000 * 60 * 60 * 2))
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
36 done < <(users |
tr " " "\n" |
sort -u)
37 if (( idle_time
< idle_limit
)); then
46 Usage: ${0##*/} [ARGS]
47 Do btrfs maintence or stop if xprintidle shows a user
49 force Run regardless of user idle status on all disks.
50 check Only check if an existing maintence should be cancelled due to
51 nonidle user. Also, runs in a loop every 20 seconds for 10
54 Note: Uses util-linux getopt option parsing: spaces between args and
55 options, short options can be combined, options before args.
76 echo "$0: error: unexpected arg" >&2
90 # When the cron kicks in, we may not be idle (physically sleeping) yet, so
92 while ! $idle && (( min
< max_min
)); do
97 # If we've waited a really long time for idle, just give up.
98 if (( min
== max_min
)); then
105 fnd
="findmnt --types btrfs --noheading"
106 for x
in $
($fnd --output "SOURCE" --nofsroot |
sort -u); do
107 mnt
=$
($fnd --output "TARGET" --first-only --source $x)
108 [[ $mnt ]] ||
continue
110 #### begin look for diff in stats, eg: increasing error count ####
112 # Only run for $check, since it runs in parallel to non-check, avoid
116 # if mnt is /, avoid making a buggy looking path
117 stats_path
=${mnt%/}/btrfs-dev-stats
118 if [[ ! -e $stats_path ]]; then
119 btrfs dev stats
-c $mnt >$stats_path ||
: # populate initial reading
120 elif ! btrfs dev stats
-c $mnt >$tmp; then
121 if ! diff -q $stats_path $tmp; then
123 From: root@$HOSTNAME.b8.nz
124 To: alerts@iankelling.org
125 Subject: btrfsmaintstop: btrfs dev stats -c $mnt
127 $(diff -u $stats_path $tmp)
129 mv $stats_path $stats_path.1
130 cat $tmp >$stats_path
135 #### end look for diff in stats, eg: increasing error count ####
140 echo "$0: not idle. if this wasnt a dry run, btrfs scrub cancel $mnt"
142 btrfs scrub cancel
$mnt &>/dev
/null ||
:
148 # for comparing before and after balance.
149 # the log is already fairly verbose, so commented.
150 # e btrfs filesystem df $mnt
152 if btrfs filesystem df
$mnt |
grep -q "Data+Metadata"; then
153 for usage
in $dusage; do
154 e ionice
-c 3 btrfs balance start
-dusage=$usage -musage=$usage $mnt
157 e ionice
-c 3 btrfs balance start
-dusage=0 $mnt
158 for usage
in $dusage; do
159 e ionice
-c 3 btrfs balance start
-dusage=$usage $mnt
161 e ionice
-c 3 btrfs balance start
-musage=0 $mnt
162 for usage
in $musage; do
163 e ionice
-c 3 btrfs balance start
-musage=$usage $mnt
167 scrub_status
=$
(btrfs scrub status
$mnt)
168 if printf "%s\n" "$scrub_status" |
grep -i '^status:[[:space:]]*finished$' &>/dev
/null
; then
169 date=$
(printf "%s\n" "$scrub_status" |
sed -rn 's/^Scrub started:[[:space:]]*(.*)/\1/p')
171 if [[ ! $date ]]; then
172 # output from older versions, at least btrfs v4.15.1
174 printf "%s\n" "$scrub_status" | \
175 sed -rn 's/^\s*scrub started at (.*) and finished.*/\1/p'
180 echo "$0: last scrub finish for $mnt: $date"
182 date=$
(date --date="$date" +%s
)
183 # if date is sooner than 60 days ago
184 # the wiki recommends 30 days or so, but
185 # I'm going with 60 days.
186 if (( date > EPOCHSECONDS
- 60*60*24*60 )); then
188 echo "$0: skiping scrub of $mnt, last was $(( (EPOCHSECONDS - date) / 60/60/24 )) days ago, < 30 days"
193 # -c 2 -n 4 is from btrfsmaintenance, does ionice
194 e btrfs scrub start
-Bd -c 2 -n 4 $mnt
196 # We normally only do one disk since this is meant to be run while I sleep
197 # and if we try to do all disks, we invariably end up doing a scrub still
198 # after I've woken up. So, just do one per day.