mostly simply btrfsmaint, add multiple emacs daemon features
authorIan Kelling <ian@iankelling.org>
Mon, 15 Jun 2026 19:19:24 +0000 (15:19 -0400)
committerIan Kelling <ian@iankelling.org>
Mon, 15 Jun 2026 19:19:24 +0000 (15:19 -0400)
16 files changed:
brc2
btrfsmaint
conflink
distro-end
filesystem/etc/prometheus/rules/iank.yml
filesystem/etc/sudoers.d/iank [new file with mode: 0644]
filesystem/etc/systemd/system/btrfsmaint.service
filesystem/etc/systemd/system/btrfsmaint.timer
filesystem/etc/systemd/system/btrfsmaintstop.service [deleted file]
g
g-lib.sh [new file with mode: 0755]
g2
g3 [new file with mode: 0755]
g4 [new file with mode: 0755]
install-my-scripts
system-status

diff --git a/brc2 b/brc2
index e67c8cdd9c1c7eed4395c7f9992047570a95ecb9..a5daa4ba6e5f11451af40b1dc21de43e580d0e72 100644 (file)
--- a/brc2
+++ b/brc2
@@ -2009,7 +2009,7 @@ ccomp xdg-open o
 
 # 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
index 8f56e4c8e92d96668ad9d6eaf57343f6db621ca8..e6f08149f2d42c5e79c28f66d89dfb366c9c5cff 100755 (executable)
@@ -43,18 +43,6 @@ EOF
 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]
@@ -90,76 +78,31 @@ while true; do
   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
@@ -167,63 +110,50 @@ 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
index e6c947759154eb7f9e466acde58aeba16c068242..b303d539dee3b5a468d1426d350740118276016a 100755 (executable)
--- a/conflink
+++ b/conflink
@@ -109,7 +109,7 @@ common-file-setup() {
 
 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
index eda39db111b27548354b241baeb5b8df7ddc74f3..1b66a78d332eae0678915683747f239f736b5e7b 100755 (executable)
@@ -1794,9 +1794,12 @@ EOF
 
 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
 
index 47012ccbcb74b20f517f825d1cc327fd70fe2c0d..4231582516eb8cfb816625698dfb67b3152aafa2 100644 (file)
@@ -85,7 +85,7 @@ groups:
   - 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
@@ -107,7 +107,7 @@ groups:
 
   - 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
diff --git a/filesystem/etc/sudoers.d/iank b/filesystem/etc/sudoers.d/iank
new file mode 100644 (file)
index 0000000..d5a1728
--- /dev/null
@@ -0,0 +1 @@
+Defaults pwfeedback
index 73fb29463a302961ca431f2c10cdb9f2bf550ebc..5092274a377dccb9cbf7f993319d894dfcc8d807 100644 (file)
@@ -5,6 +5,7 @@ After=multi-user.target
 [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
index a60d3479ec3e0091c08d9ad4bd649eadccfb3c6f..8a4ab0b0f0ba12cfd21d1435ae7f6440e5f56e5b 100644 (file)
@@ -8,7 +8,7 @@ Description=btrfsmaint
 # 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
 
diff --git a/filesystem/etc/systemd/system/btrfsmaintstop.service b/filesystem/etc/systemd/system/btrfsmaintstop.service
deleted file mode 100644 (file)
index 88f90a9..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-[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
diff --git a/g b/g
index 713735c4a5936e3d4d6c0526a1c4fb9084b77a36..ac1be0b68c31341388091beab80b6ad63dad8ff3 100755 (executable)
--- a/g
+++ b/g
 # 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 "$@"
diff --git a/g-lib.sh b/g-lib.sh
new file mode 100755 (executable)
index 0000000..7fb15d9
--- /dev/null
+++ b/g-lib.sh
@@ -0,0 +1,65 @@
+#!/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
+}
diff --git a/g2 b/g2
index a6b8da78c7213c80874d4c64c02dd2e949db962f..d015f4fbcd048fe9271966f5bbba656a9d984a02 100755 (executable)
--- a/g2
+++ b/g2
 # 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 "$@"
diff --git a/g3 b/g3
new file mode 100755 (executable)
index 0000000..5067e87
--- /dev/null
+++ b/g3
@@ -0,0 +1,32 @@
+#!/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 "$@"
diff --git a/g4 b/g4
new file mode 100755 (executable)
index 0000000..e275235
--- /dev/null
+++ b/g4
@@ -0,0 +1,32 @@
+#!/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 "$@"
index 0d37653bf0eece3b0d2a84337bd8e3aafabe5f8f..0b774f1f12de9c1890d9ba31c3a369ee147ecc38 100755 (executable)
@@ -80,9 +80,6 @@ while read -r line; do
   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
index 34ac06d75b6db26fbfe4e6dfd524f118ceda65c4..92c5b85c5fe729954836acb146d50bfe21a41a12 100755 (executable)
@@ -80,7 +80,6 @@ write-status() {
     *)
       services+=(
         systemstatus
-        btrfsmaintstop
         dynamicipupdate
       )
       bads=()