# jfilter() {
# grep -Evi -e "^(\S+\s+){4}(sudo|sshd|cron)\[\S*:" \
- # -e "^(\S+\s+){4}systemd\[\S*: (starting|started) (btrfsmaintstop|dynamicipupdate|spamd dns bug fix cronjob|rss2email)\.*$"
+ # -e "^(\S+\s+){4}systemd\[\S*: (starting|started) (dynamicipupdate|spamd dns bug fix cronjob|rss2email)\.*$"
# }
# jtail() {
# journalctl -n 10000 -f "$@" | jfilter
fi
-check-idle() {
- type -p xscreensaver-command &>/dev/null || return 0
- export XAUTHORITY=/home/iank/.Xauthority
- export DISPLAY=:0
- locked=false
- if lock_info=$(xscreensaver-command -time 2>/dev/null); then
- if [[ $lock_info != *non-blanked* ]]; then
- locked=true
- fi
- fi
-}
-
usage() {
cat <<EOF
Usage: ${0##*/} [OPTIONS]
esac
shift
done
-readonly check dry_run force stats
##### end command line parsing ########
-# todo: for previous scrubs, we shoudl do:
-# cd /var/lib/btrfs
-# for uid in *; do
-# # grab 3 things out of that file:
-# # scrub status:1
-# finished:1
-# t_start:1738738808 (epoch seconds roughly at the end or start of the last scrub. good enough for our purposes)
-# I don't now what other status numbers are, but I should test it.
-#
-# Instead of canceling, we will just have a formula:
-#
-# $(nproc)
-#
-# read -r _ la1 la2 _ </proc/loadavg
-# la1=$(e "$la1 * 100" | bc | sed 's/\..*//)
-# la2=$(e "$la2 * 100" | bc | sed 's/\..*//)
-#
-# load_avg="$la1"
-# if (( la1 < la2 )); then
-# load_avg="$la2"
-# fi
-#
-# bw_limit=500mb/s * (1 - (load_avg / nproc))
-#
-# And set that per https://btrfs.readthedocs.io/en/latest/Scrub.html
-
main() {
- 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 ! $locked && (( 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
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
+ tmps=$($fnd --output "TARGET,UUID" --first-only --source $x)
+ read -r mnt uuid <<<"$tmps"
+ [[ $uuid ]] || continue
####### begin look for diff in stats, eg: increasing error count ####
- if $stats; then
- tmp=$(mktemp)
- # ${mnt%/} so that if mnt is / we avoid making a buggy looking path
- stats_path=${mnt%/}/btrfs-dev-stats
- if [[ ! -e $stats_path ]]; then
- btrfs dev stats -c $mnt >$stats_path ||: # populate initial reading
- elif ! btrfs dev stats -c $mnt >$tmp; then
- if ! diff -q $stats_path $tmp; then
- mv $stats_path $stats_path.1
- cat $tmp >$stats_path
- diff=$(diff -u $stats_path $tmp 2>&1 ||:)
- printf "diff of: btrfs dev stats -c %s\n%s\n" "$mnt" "$diff"
- exim -odf -i root <<EOF
+ tmp=$(mktemp)
+ # ${mnt%/} so that if mnt is / we avoid making a buggy looking path
+ stats_path=${mnt%/}/btrfs-dev-stats
+ if [[ ! -e $stats_path ]]; then
+ btrfs dev stats -c $mnt >$stats_path ||: # populate initial reading
+ elif ! btrfs dev stats -c $mnt >$tmp; then
+ if ! diff -q $stats_path $tmp; then
+ mv $stats_path $stats_path.1
+ cat $tmp >$stats_path
+ diff=$(diff -u $stats_path $tmp 2>&1 ||:)
+ printf "diff of: btrfs dev stats -c %s\n%s\n" "$mnt" "$diff"
+ exim -odf -i root <<EOF
From: root@$(hostname -f)
To: root@$(hostname -f)
Subject: btrfsmaint: device stats changed for $mnt
diff of: btrfs dev stats -c $mnt
$diff
EOF
- fi
fi
- rm -f $tmp
fi
+ rm -f $tmp
######## end look for diff in stats, eg: increasing error count ####
- echo 1 | tee /sys/fs/btrfs/*/allocation/data/periodic_reclaim /sys/fs/btrfs/*/allocation/data/dynamic_reclaim >/dev/null
+ # only documented in source code. wonderful. takes care of balance for us.
+ echo 1 | tee /sys/fs/btrfs/*/allocation/data/periodic_reclaim >/dev/null
- date=
+ t_start=
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'
- )
+ start_cmd=start
+ if printf "%s\n" "$scrub_status" | grep -iE '^status:[[:space:]]*(aborted|interrupted)$' &>/dev/null; then
+ start_cmd=resume
+ elif printf "%s\n" "$scrub_status" | grep -i '^status:[[:space:]]*finished$' &>/dev/null; then
+ t_start=$(sed -rn 's/.*t_start:([0-9]+).*/\1/p' /var/lib/btrfs/scrub.status.$uuid | sort -n | tail -n1)
fi
- if [[ $date ]]; then
+ if [[ $t_start ]]; then
if $dry_run; then
- echo "$0: last scrub finish for $mnt: $date"
+ echo "$0: last scrub finish for $mnt: $t_start"
fi
- date=$(date --date="$date" +%s)
# if date is sooner than 60 days ago
# the wiki recommends 30 days or so, but
# I'm going with 60 days.
- if (( date > EPOCHSECONDS - 60*60*24*60 )); then
+ if (( t_start > EPOCHSECONDS - 60*60*24*60 )); then
if $dry_run; then
- echo "$0: skiping scrub of $mnt, last was $(( (EPOCHSECONDS - date) / 60/60/24 )) days ago, < 30 days"
+ echo "$0: skiping scrub of $mnt, last was $(( (EPOCHSECONDS - t_start) / 60/60/24 )) days ago, < 60 days"
fi
continue
fi
fi
- # btrfsmaintenance does -c 2 -n 4, but I want lowest pri.
- dr btrfs scrub start -Bd -c 3 $mnt
-
- # We normally only do one disk since this is meant to be run in
- # downtime and if we try to do all disks, we invariably end up doing
- # a scrub after downtime. So, just do one disk per day.
- if ! $force; then
- return 0
+ # 20m = 11 days for a 20 tb hd.
+ # dynamic way to set the limit is:
+ # -B means don't background. this allows us to run just one at a time.
+ if $dry_run; then
+ echo "$0: dry run. would run btrfs scrub $start_cmd -Bd -c 3 $mnt + fstrim"
+ else
+ echo 20m | tee /sys/fs/btrfs/$uuid/devinfo/*/scrub_speed_max >/dev/null
+ sleep 1 # just paranoid
+ # note: --limit in man page is buggy, sets it only on some devices, but without it, sets it to 0.
+ m btrfs scrub $start_cmd --limit 20m -Bd $mnt
+ m fstrim $mnt
fi
done
}
-loop-main() {
- while true; do
- main
- sleep 60
- done
-}
-
-if $check; then
- loop-main
-else
- main
-fi
+main
old-files-cleanup() {
# old files 2022-03
- for t in systemstatus epanicclean btrfsmaintstop dynamicipupdate; do
+ for t in systemstatus epanicclean dynamicipupdate; do
f=/etc/systemd/system/$t.timer
if [[ -e $f ]]; then
v systemctl stop $t.timer
sysd-prom-fail-install dynamicipupdate
sysd-prom-fail-install systemstatus
-sysd-prom-fail-install btrfsmaintstop
+sysd-prom-fail-install btrfsmaint
sgo btrfsmaint.timer
-sgo btrfsmaintstop
+if [[ -e /etc/systemd/system/btrfsmaintstop.service ]]; then
+ soff btrfsmaintstop
+ sudo rm -rf /etc/systemd/system/btrfsmaintstop.service*
+fi
sgo systemstatus
sgo dynamicipupdate
- alert: mysers_units_missing
expr: |-
count(up{job="node"} == 1) by (instance) * 3 unless
- count(node_systemd_unit_state{name=~"(systemstatus|btrfsmaintstop|dynamicipupdate).service",state="active"}) by (instance)
+ count(node_systemd_unit_state{name=~"(systemstatus|dynamicipupdate).service",state="active"}) by (instance)
for: 20m
labels:
severity: warn
- alert: mysers_not_active
expr: |-
- node_systemd_unit_state{name=~"(systemstatus|btrfsmaintstop|dynamicipupdate).service",state="active"} != 1
+ node_systemd_unit_state{name=~"(systemstatus|dynamicipupdate).service",state="active"} != 1
for: 20m
labels:
severity: warn
--- /dev/null
+Defaults pwfeedback
[Service]
Type=simple
# note, btrfs maint is hardlinked into fsf ansible repo
-ExecStart=/usr/local/bin/btrfsmaint --no-stats
+ExecStart=/usr/local/bin/btrfsmaint
+# note: these may make very little difference depending on scheduler.
IOSchedulingClass=idle
CPUSchedulingPolicy=idle
# readlink /etc/localtime | sed -r 's,^.*/([^/]+/[^/]+)$,\1,'
# run daily
-OnCalendar=*-*-* 02:00:00 America/New_York
+OnCalendar=*-*-* 18:00:00 America/New_York
# previously, was running weekly:
#OnCalendar=Thu *-*-* 02:00:00 America/New_York
+++ /dev/null
-[Unit]
-Description=btrfsmaintstop
-After=local-fs.target
-StartLimitIntervalSec=0
-
-[Service]
-Type=simple
-ExecStart=/usr/local/bin/btrfsmaint --check
-Restart=always
-RestartSec=600
-
-[Install]
-WantedBy=graphical.target
# limitations under the License.
+glib=${BASH_SOURCE[0]%/*}/g-lib.sh
-# note: starting emacs server can hang due to errors in init files
-# which don't cause problems for non-server emacs. And things
-# that aren't errors can cause hang too, like when I added gnus
-# git to load path.
+if [[ ! -s $glib ]]; then
+ echo "$0: error, can't find g-lib.sh" >&2; exit 1
+fi
-# note: I was experimenting with running
-# under gdb always to diagnose any hangs/crashes
-# and it won't work due to a gdb crash:
-# gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacsclient -nc
-#
-# -n = --no-wait, go into background
-# -c = create new frame instead of reusing the current frame
-# -a = alternate editor, empty string makes it start emacs daemon.
-# This is also set via env variable, but that doesn't propagate everywhere.
-
-
-g() {
-
- local args gdb=false
-
- if [[ $DISPLAY ]]; then
- args=-n
- fi
-
- if (( $# == 0 )); then
- args+=" -c"
- fi
-
- id=$(i3-msg -t get_tree | jq -e '.. | select(.name?) | select(.name | test("^e-iak ")).id'| sort -n| head -n1)
-
- if [[ ! $id ]]; then
- if (( $# == 0 )) && type -p gdb &>/dev/null; then
- # no time for messing with gdb right now
- #gdb=true
- :
- else
- # duplicate arg, but oh well.
- args+=" -c"
- fi
- fi
-
- if $gdb; then
- # due to a bug, we cant debug from the start unless we get a new gdb
- # https://sourceware.org/bugzilla/show_bug.cgi?id=24454
- # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon
- emacsclient -a "" $args "$@"
- sleep 1
- cd "/a/opt/emacs-$(distro-name)$(distro-num)"
- sudo gdb -p "$(pgrep -f 'emacs --daemon')" -ex c
- # note: this is dead code since this is called as a script.
- # shellcheck disable=SC2103 # preference for simplicity.
- cd -
- else
- emacsclient -a "" $args "$@"
- fi
-}
+source $glib
echo "$*" >>/tmp/g.log
-g "$@"
+g none "$@"
--- /dev/null
+#!/bin/bash
+
+# note: starting emacs server can hang due to errors in init files
+# which don't cause problems for non-server emacs. And things
+# that aren't errors can cause hang too, like when I added gnus
+# git to load path.
+
+# note: I was experimenting with running
+# under gdb always to diagnose any hangs/crashes
+# and it won't work due to a gdb crash:
+# gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacsclient -nc
+#
+# -n = --no-wait, go into background
+# -c = create new frame instead of reusing the current frame
+# -a = alternate editor, empty string makes it start emacs daemon.
+# This is also set via env variable, but that doesn't propagate everywhere.
+
+
+g() {
+
+ local args gdb=false server="$1"
+ shift
+
+ if [[ $DISPLAY ]]; then
+ args=-n
+ fi
+
+ if (( $# == 0 )); then
+ args+=" -c"
+ fi
+
+ if [[ $server == none ]]; then
+ server=
+ else
+ args+=" -s $server"
+ fi
+
+ id=$(i3-msg -t get_tree | jq -e '.. | select(.name?) | select(.name | test("^e-'$server'iak ")).id'| sort -n| head -n1)
+
+ if [[ ! $id ]]; then
+ if (( $# == 0 )) && type -p gdb &>/dev/null; then
+ # no time for messing with gdb right now
+ #gdb=true
+ :
+ else
+ # duplicate arg, but oh well.
+ args+=" -c"
+ fi
+ fi
+
+ if $gdb; then
+ # due to a bug, we cant debug from the start unless we get a new gdb
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=24454
+ # m gdb -ex="set follow-fork-mode child" -ex=r -ex=quit --args emacs --daemon
+ emacsclient -a "" $args "$@"
+ sleep 1
+ cd "/a/opt/emacs-$(distro-name)$(distro-num)"
+ sudo gdb -p "$(pgrep -f 'emacs --daemon')" -ex c
+ # note: this is dead code since this is called as a script.
+ # shellcheck disable=SC2103 # preference for simplicity.
+ cd -
+ else
+ emacsclient -a "" $args "$@"
+ fi
+}
# See the License for the specific language governing permissions and
# limitations under the License.
+glib=${BASH_SOURCE[0]%/*}/g-lib.sh
-# force creation of new frame.
-exec emacsclient -a "" -nc "$@"
+if [[ ! -s $glib ]]; then
+ echo "$0: error, can't find g-lib.sh" >&2; exit 1
+fi
+
+source $glib
+
+echo "$*" >>/tmp/g2.log
+g g2 "$@"
--- /dev/null
+#!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
+
+# Copyright 2024 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+glib=${BASH_SOURCE[0]%/*}/g-lib.sh
+
+if [[ ! -s $glib ]]; then
+ echo "$0: error, can't find g-lib.sh" >&2; exit 1
+fi
+
+source $glib
+
+echo "$*" >>/tmp/g2.log
+g g3 "$@"
--- /dev/null
+#!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
+
+# Copyright 2024 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+glib=${BASH_SOURCE[0]%/*}/g-lib.sh
+
+if [[ ! -s $glib ]]; then
+ echo "$0: error, can't find g-lib.sh" >&2; exit 1
+fi
+
+source $glib
+
+echo "$*" >>/tmp/g2.log
+g g4 "$@"
file="${line:12}"
#echo debug: file: $file
case $file in
- btrfsmaint)
- sre btrfsmaintstop
- ;;
mailtest-check)
# we stopped removing the dashes in services recently.
sre $file
*)
services+=(
systemstatus
- btrfsmaintstop
dynamicipupdate
)
bads=()