bug fix sl, add feature, refactor for simplicity
authorIan Kelling <ian@iankelling.org>
Thu, 5 Feb 2026 22:50:27 +0000 (17:50 -0500)
committerIan Kelling <ian@iankelling.org>
Thu, 5 Feb 2026 22:50:27 +0000 (17:50 -0500)
brc

diff --git a/brc b/brc
index 9a8de9167dbb0f51ffe2233d9f871341c53d3013..fde7dcb4a658775b983b9556f07a310bdef4ca82 100644 (file)
--- a/brc
+++ b/brc
@@ -806,7 +806,7 @@ tsl() {
 # Gets all the essential disk info on a machine. In case it gets ill, this
 # will help us know the expected state.
 disk-info() {
-  local cmds cmd
+  local cmds cmd cmd_name
   mapfile -t cmds <<'EOF'
 tail -n +1 /proc/mdstat /etc/mdadm/mdadm.conf /etc/fstab /etc/crypttab
 lsblk
@@ -816,6 +816,8 @@ btrfs fi show
 EOF
 
   for cmd in "${cmds[@]}"; do
+    cmd_name="${cmd%% *}"
+    command -p $cmd_name &>/dev/null || continue # we dont have btrfs on some old hosts
     cat <<EOF
 ### $cmd
 
@@ -917,6 +919,17 @@ EOF
 
 #### end fsf section
 
+sl-local-test() {
+  remote="$1"
+  if [[ $remote == *.fsf.org ||
+          $remote == *.gnu.org ||
+          $remote == *.libreplanet.org ]]; then
+    e sync
+  elif [[ $remote == *.b8.nz ]]; then
+    e skip
+  fi
+}
+export SL_LOCAL_TEST_CMD=sl-local-test
 
 ..() { c ..; }
 ...() { c ../..; }
@@ -3077,11 +3090,56 @@ slowdo() {
           done
 }
 
+# sets $host_type, and maybe some related vars: $pre_synced, $extra_info
+_sl-get-host-type() {
+  local tmp tmp2 testbool testcmd
+  local -a tmpa
+  if [[ $sl_local_test_cmd ]]; then
+    host_type=$($sl_local_test_cmd "$remote")
+  fi
+  if [[ $host_type ]]; then return; fi
 
-#### sl: ssh wrapper, but maybe first rsync files that we configure and
+  tmpa=($SL_INFO_DIR/??????????-????-"$remote")
+  sshinfo=${tmpa[0]}
+  if [[ -e $sshinfo ]]; then
+    tmp=${sshinfo[0]##*/}
+    tmp2=${tmp:11} # skip 11 chars
+    host_type=${tmp2%%-*}
+    extra_info=$(cat $sshinfo)
+
+    ### block to see if past sync is up to date
+    if $force_rsync; then return; fi
+    info_sec=${tmp::10}
+    read -r files_sec _ < <(find -L $SL_FILES_DIR -printf "%T@ %p\n" | sort -nr || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]]  )
+    files_sec=${files_sec%%.*}
+    # info_sec is the last time we synced to that host. files_sec is the
+    # greatest mod time out of files we want to sync.
+    if (( files_sec <= info_sec )); then
+      pre_synced=true
+    fi
+    return
+  fi
+
+  # we test for string to know ssh succeeded
+  testbool="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
+  testcmd="if $testbool; then printf y; else printf n; fi"
+  if ! tmp=$(mq LC_USEBASHRC=t command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
+    echo failed sl test. doing plain ssh -v
+    command ssh -v "${args[@]}" "$remote"
+  fi
+  if [[ $tmp == y* ]]; then # y = yes test result
+    host_type=skip
+  else
+    host_type=sync
+  fi
+  extra_info="${tmp:1}"
+}
+
+
+#### sl: ssh wrapper, but if needed, first rsync files that we configure and
 # always source our .bashrc on the remote.
 
-# TODO: this needs better documentation.
+# TODO: docs needs polishing.
 
 # Usage: all the same args as ssh + a few below. Note, combining options
 # that have arguments with ones that dont, like -4oOption is not
@@ -3113,6 +3171,12 @@ slowdo() {
 # is that the ssh target does not have files we previously rsynced for
 # various reasons. I have a wrapper for this called slr below.
 
+# SL_LOCAL_TEST_CMD / --sl-local-test-cmd: Env var or cli option. If
+# set, we run this command with the remote host as an argument and if
+# its output is "sync" we do rsync. If it is "skip", we don't
+# rsync. Otherwise, we carry on. This will avoid the ssh done for
+# new hosts to discover if SL_FILES_DIR exists, which can be convenient.
+
 # SL_TEST_CMD / --sl-test-cmd CMD: Env var or cli option. If set, we run
 # this string on the remote host the first time sl is run (or if we run
 # slr). Its standard out is passed to SL_TEST_HOOK which gets run
@@ -3134,18 +3198,19 @@ slowdo() {
 
 # SL_SSH_ARGS: Env var. Default arguments passed to ssh.
 
-# For when ~/.bashrc is already customized on the remote server, you
-# might find it problematic that ~/.bashrc is sourced for ALL ssh
-# commands, even in scripts. This paragraph is all about that. bash
-# scripts dont source ~/.bashrc, but call ssh in scripts and you get
-# ~/.bashrc. You dont want this. .bashrc is meant for interactive shells
-# and if you customize it, probably has bugs from time to time. This is
-# bad. Here's how I fix it. I have a special condition to "return" in my
-# .bashrc for noninteractive ssh shells (copy that code). Then use this
-# function or similar that passes LC_USEBASHRC=t when sshing and I want
-# my bashrc. Also, I don't keep most of my bashrc in .bashrc, i source a
-# separate file because even if I return early on, the whole file gets
-# parsed which can fail if there is a syntax error.
+# When ~/.bashrc is already customized on the remote server, you might
+# find it problematic that ~/.bashrc is sourced for ALL ssh commands,
+# even in scripts. This paragraph is all about that. bash scripts dont
+# source ~/.bashrc, but call ssh in scripts and you get ~/.bashrc. You
+# dont want this. .bashrc is meant for interactive shells and bugs in it
+# should be limited to that. This is a bad default. I created a
+# workaround so that .bashrc is unused by default in ssh then I opt-in
+# for interactive ssh. The mechanism is: I have a condition to "return"
+# in my .bashrc if under ssh and the variable LC_USEBASHRC=t is not
+# set. Then, when sshing interactively, I use an ssh wrapper which sets
+# that variable.  Also, I don't keep most of my bashrc in .bashrc, i
+# source a separate file because even if I return early on, the whole
+# file gets parsed which can fail if there is a syntax error.
 sl() {
   # Background on LC_USEBASHRC var (no need to read if you just want to
   # use this function): env variables sent across ssh are strictly
@@ -3156,10 +3221,10 @@ sl() {
   # .bashrc. This means the outer shell still ran the default .bashrc,
   # but that is the best we can do.
 
-  local verbose now args remote do_rsync haveinfo tmpa sshinfo tmp tmp2 host_type info_sec force_rsync
-  local sync_dirname testcmd extra_info testbool files_sec sl_test_cmd sl_test_hook
-  local sl_rsync_cmd sl_test_cmd sl_test_hook
-  declare -a args tmpa
+  local verbose now args remote sshinfo tmp host_type info_sec force_rsync
+  local sync_dirname extra_info files_sec sl_test_cmd sl_test_hook pre_synced
+  local sl_rsync_cmd sl_test_cmd sl_test_hook sl_local_test_cmd
+  local -a args
 
   args=($SL_SSH_ARGS)
 
@@ -3178,7 +3243,6 @@ sl() {
   # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]
   # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
 
-  # shellcheck disable=SC3127 # todo: remove this once we use this var
   verbose=false
   force_rsync=false
   if [[ $1 == --rsync ]]; then
@@ -3186,6 +3250,8 @@ sl() {
     shift
   fi
   # shellcheck disable=SC2153 # intentional
+  sl_local_test_cmd=$SL_LOCAL_TEST_CMD
+  # shellcheck disable=SC2153 # intentional
   sl_test_cmd=$SL_TEST_CMD
   # shellcheck disable=SC2153 # intentional
   sl_test_hook=$SL_TEST_HOOK
@@ -3196,6 +3262,10 @@ sl() {
       --rsync)
         force_rsync=true
         ;;
+      --sl-local-test-cmd)
+        sl_local_test_cmd="$2"
+        shift
+        ;;
       --sl-test-cmd)
         sl_test_cmd="$2"
         shift
@@ -3219,11 +3289,12 @@ sl() {
     case "$1" in
       # note we dont support things like -4oOption
       -[46AaCfGgKkMNnqsTtVvXxYy]*)
+
         if [[ $1 == *v* ]]; then
-          # todo: try running slowdo if this is false
-          # shellcheck disable=SC2034 # todo: remove this once we use this var
+          # used by mq etc
           verbose=true
         fi
+
         args+=("$1"); shift
         ;;
       -[bcDEeFIiJLlmOopQRSWw]*)
@@ -3251,79 +3322,28 @@ sl() {
     return 1
   fi
 
-  do_rsync=false
-  haveinfo=false
-  tmpa=($SL_INFO_DIR/??????????-????-"$remote")
-  sshinfo=${tmpa[0]}
-  if [[ -e $sshinfo ]]; then
-    if $force_rsync; then
-      rm -f $sshinfo
-    else
-      haveinfo=true
-    fi
-  fi
-  if $haveinfo; then
-    tmp=${sshinfo[0]##*/}
-    tmp2=${tmp:11} # skip 11 chars
-    host_type=${tmp2%%-*}
-    extra_info=$(cat $sshinfo)
-    # debug
-    #echo d1 $host_type
-  else
-    # we test for string to know ssh succeeded
-    testbool="test -e $SL_FILES_DIR/.bashrc -a -L .bashrc -a -v LC_USEBASHRC"
-    testcmd="if $testbool; then printf y; else printf n; fi"
-    if ! tmp=$(mq LC_USEBASHRC=y command ssh "${args[@]}" "$remote" "$testcmd; $sl_test_cmd"); then
-      echo failed sl test. doing plain ssh -v
-      command ssh -v "${args[@]}" "$remote"
-    fi
-    if [[ $tmp == y* ]]; then
-      # yes test result, no need to rsync this host.
-      host_type=skip
-    else
-      do_rsync=true
-      host_type=sync
-    fi
-    extra_info="${tmp:1}"
-  fi
+  pre_synced=false
+  _sl-get-host-type
+
   if [[ $sl_test_hook ]]; then
     mq RSYNC_RSH="ssh ${args[*]}" $sl_test_hook "$extra_info" "$remote"
   fi
 
-  if $haveinfo && [[ $host_type == sync ]]; then
-    info_sec=${tmp::10}
-    read -r files_sec _ < <(find -L $SL_FILES_DIR -printf "%T@ %p\n" | sort -nr || [[ $? == 141 || ${PIPESTATUS[0]} == 32 ]]  )
-    files_sec=${files_sec%%.*}
-    # info_sec is the last time we synced to that host. files_sec is the
-    # greatest mod time out of files we want to sync.
-
-    if (( files_sec > info_sec )); then
-      #echo "d4 $files_sec > $info_sec"
-      do_rsync=true
-    fi
-  fi
-
   sync_dirname=${SL_FILES_DIR##*/}
-
   if [[ ! $SL_FILES_DIR  ]]; then
     echo 'error: missing SL_FILES_DIR env var' >&2
     return 1
   fi
-  if $do_rsync; then
-    # todo: it would be nice if we did this with -v, but
-    # only showed the output if the command lasted more than
-    # about 4 seconds.
+
+  if [[ $host_type == sync ]] && ! $pre_synced; then
     m RSYNC_RSH="ssh ${args[*]}" $sl_rsync_cmd -rptL --delete $SL_FILES_DIR "$remote":
-  fi
-  if $do_rsync || ! $haveinfo; then
-    if [[ $sshinfo && -e $sshinfo ]]; then rm -f $sshinfo; fi
+
     sshinfo=$SL_INFO_DIR/$EPOCHSECONDS-$host_type-"$remote"
     [[ -e $SL_INFO_DIR ]] || mkdir -p $SL_INFO_DIR
-    # debug
-    #echo d3: $sshinfo
     printf "%s\n" "$extra_info" >$sshinfo
     chmod 666 $sshinfo
   fi
+
   if [[ $host_type == sync ]]; then
     if (( ${#@} )); then
       # in the past, I used ${@@Q} passed to a bash subshell. Then
@@ -3331,7 +3351,7 @@ sl() {
       # didn't see any benefit to doing it that way (perhaps forgot what
       # I originally saw in it).
       mq command ssh "${args[@]}" "$remote" \
-              LC_USEBASHRC=t . $sync_dirname/.bashrc\; "$@"
+         LC_USEBASHRC=t . $sync_dirname/.bashrc\; "$@"
     elif [[ ! -t 0 ]]; then
       # This case is when commands are being piped to ssh.
       # Normally, no bashrc gets sourced.
@@ -3349,6 +3369,12 @@ sl() {
   fi
   # this function inspired from https://github.com/Russell91/sshrc
 }
+# sl dev notes:
+#
+# todo: it would be nice if -v would do verbose rsync, but only show the
+# output if it took longer than 4 seconds or so.
+# todo: try running slowdo on verbose ssh
+
 
 slr() {
   sl --rsync "$@"