From 8c3f016b0983ffde1c48be5af3524e3c39fdb59d Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Tue, 4 Aug 2020 17:13:11 -0400 Subject: [PATCH] document sl function --- .bashrc | 31 ++++++--- brc | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ brc2 | 174 +------------------------------------------------ 3 files changed, 218 insertions(+), 183 deletions(-) diff --git a/.bashrc b/.bashrc index 04df859..7a61595 100644 --- a/.bashrc +++ b/.bashrc @@ -10,12 +10,11 @@ -# History related options first, or else -# we risk screwing up history history. And this is duplicated -# in ~/.bash_profile just for good measure -# history number. History expansion is good. -PS4='$LINENO+ ' -# history file size limit, set to unlimited. +# History related options first and always, or else we risk screwing up +# the history file. This is duplicated in ~/.bash_profile just for good +# measure + +# History file size limit, set to unlimited. # this needs to be different from the default because # default HISTFILESIZE is 500 and could clobber our history HISTFILESIZE= @@ -27,15 +26,19 @@ HISTSIZE=1000000 HISTTIMEFORMAT="%Y-%m-%d %I:%M %p " # consecutive duplicate lines dont go in history HISTCONTROL=ignoredups -# works in addition to HISTCONTROL to do more flexible things +# This works in addition to HISTCONTROL to do more flexible things # it could also do the same things as HISTCONTROL and thus replace it, # but meh. dunno why, but just " *" does glob expansion, so use [ ] to avoid it. HISTIGNORE='pass *:[ ]*:otp *:oathtool *' -# see comments in brc2 sl() function for background. +#### begin section that works with sl() function to return from +#### noninteractive ssh shells if [[ $SSH_CONNECTION ]] \ && [[ $BRC != t ]]; then + # Here we did not opt-in to running our .bashrc file so we just + # return, but we still setup a function to source it without returning + # so if we want it we don't have to restart our ssh connection. brc() { export BRC=t source ~/.bashrc @@ -43,8 +46,10 @@ if [[ $SSH_CONNECTION ]] \ return 0 else - # the distinction between login and non-login shells is lame, - # get rid of it. note ssh shells normally its login if a command is passed + ###### Begin sourcing of files ##### + + # The distinction between login and non-login shells is super lame + # and pretty random. get rid of that distinction. if ! shopt -q login_shell; then if [[ -r /etc/profile ]]; then source /etc/profile @@ -54,6 +59,9 @@ else # this file, and we don't want to do that and cause an infinite # loop. fi + + # source brc and brc2 if they exist. We have to readlink because we + # could be using sl() from ./brc. _tmp=$(readlink -f ${BASH_SOURCE[0]}) _tmp=${_tmp%/*} _tmp2=$_tmp/brc @@ -75,7 +83,10 @@ else source $_tmp2 fi fi + ###### End sourcing of files ##### fi +#### end section that works with sl() function to return from +#### noninteractive ssh shells # ensure no bad programs appending to this file will have an affect diff --git a/brc b/brc index f00310c..01c151a 100644 --- a/brc +++ b/brc @@ -49,6 +49,10 @@ shopt -s extglob # setting this also sets dotglob. export GLOBIGNORE="*/.:*/.." +# Useful info. see man bash. +PS4='$LINENO+ ' + + # broken with bash_completion package. Saw a bug for this once. dont anymore. # still broken in wheezy # still buggered in latest stable from the web, version 2.1 @@ -181,6 +185,10 @@ export SYSTEMD_LESS=$LESS export NNN_COLORS=2136 +export SL_FILES_DIR=/b/ds/sl/.iank +export SL_INFO_DIR=/p/sshinfo + + # * include files @@ -1079,6 +1087,194 @@ sk() { } +# sl: ssh, but firsh rsync our bashrc and related files to a special +# directory on the remote host if needed. + +# Some environment variables and files need to be setup for this to work +# (mine are set at the beginning of this file) + +# SL_FILES_DIR: Environment variable. Path to folder which should at +# least have a .bashrc file or symlink. This dir will be rsynced to +# remote hosts unless the host already has a $SL_FILES_DIR/.bashrc. In +# that case, we assume it is a host you control and sync files to +# separately and already has the ~/.bashrc you want. Symlinks in this +# dir will be resolved. The directory name is important, it will be +# created on the remote host under /root. The remote bash will also take +# its .inputrc config from this folder (default of not existing is +# fine). Mine looks like this: +# https://iankelling.org/git/?p=distro-setup;a=tree;f=sl/.iank + +# SL_INFO_DIR: Environment variable. This folder stores info about what +# we detected on the remote system and when we last synced. We expect it +# to exist ahead of time. Sometimes you may want to forget about a +# remote system, you can use sl --rsync, or the function for that slr +# below. + +# 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 (you can copy this). To make +# this work, you need to alter AcceptEnv in /etc/ssh/sshd_config, adding +# the var BRC to it. And in ~/.ssh/config or equivalent, add SendEnv +# BRC, plus any other environment vars you want to send. This function +# uses the BRC environment variable to enable that. Also, I don't keep +# most of my bashrc in .bashrc, because even though I return, the whole +# file gets parsed which can fail if there is a syntax error (plus, +# faster bash startup when we are just going to return). +# +sl() { + # Background on BRC var (no need to read if you just want to use this + # function): bash builtin vars and env show no difference in ssh vs + # local, except shell level which is not reliable. only reliable way I + # found was env var. env variables sent across ssh are strictly + # limited. We could override an obscure unused LC_var, like telephone, + # but I don't want to run into some edge case where that messes things + # up. Note, on hosts we dont control I start an inner shell with BRC + # set, and the inner shell also allows running a nondefault + # .bashrc. This means the outer shell still ran the default .bashrc, + # but that is the best we can do. + + local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2 type info_sec force_rsync sync_dirname + declare -a args tmpa + + sync_dirname=${SL_FILES_DIR##*/} + + now=$(date +%s) + + # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] + # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address] + # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] + # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname + # [command] + + # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] + # [-D [bind_address:]port] [-E log_file] [-e escape_char] + # [-F configfile] [-I pkcs11] [-i identity_file] + # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec] + # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] + # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] + + force_rsync=false + if [[ $1 == --rsync ]]; then + force_rsync=true + shift + fi + + while [[ $1 ]]; do + case "$1" in + # note we dont support things like -4oOption + -[46AaCfGgKkMNnqsTtVvXxYy]*) + args+=("$1"); shift + ;; + -[bcDEeFIiJLlmOopQRSWw]*) + # -oOption etc is valid + if (( ${#1} >= 3 )); then + args+=("$1"); shift + else + args+=("$1" "$2"); shift 2 + fi + ;; + *) + break + ;; + esac + done + remote="$1"; shift + if [[ ! $remote ]]; then + echo $0: error hostname required >&2 + return 1 + fi + + dorsync=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} + type=${tmp2: -1} + if [[ $type == b ]]; then + info_sec=${tmp::10} + for f in $SL_FILES_DIR/*; do + if (( $(stat -L -c%Y $f) > info_sec )); then + dorsync=true + rm -f $sshinfo + break + fi + done + fi + else + # use this weird yes thing to ensure we know ssh succeeded + if ! tmp=$(command ssh "${args[@]}" "$remote" "if test -e $SL_FILES_DIR/.bashrc -a -L .bashrc; then echo yes; fi"); then + echo failed sl test. doing plain ssh -v + command ssh -v "${args[@]}" "$remote" + fi + if [[ $tmp == yes ]]; then + type=a + else + dorsync=true + type=b + fi + fi + if $dorsync; then + RSYNC_RSH="ssh ${args[*]}" rsync -rptL $SL_FILES_DIR "$remote": + fi + if $dorsync || ! $haveinfo; then + sshinfo=$SL_INFO_DIR/$now$type"$remote" + touch $sshinfo + chmod 666 $sshinfo + fi + if [[ $type == b ]]; then + if (( ${#@} )); then + # Theres a couple ways to pass arguments, im not sure whats best, + # but relying on bash 4.4+ escape quoting seems most reliable. + command ssh "${args[@]}" "$remote" \ + BRC=t bash -c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash ${@@Q} + elif [[ ! -t 0 ]]; then + # This case is when commands are being piped to ssh. + # Normally, no bashrc gets sourced. + # But, since we are doing all this, lets source it because we can. + cat <(echo . $sync_dirname/.bashrc) - | command ssh "${args[@]}" "$remote" BRC=t bash + else + command ssh -t "${args[@]}" "$remote" BRC=t INPUTRC=$sync_dirname/.inputrc bash --rcfile $sync_dirname/.bashrc + fi + else + if [[ -t 0 ]]; then + BRC=t command ssh "${args[@]}" "$remote" ${@@Q} + else + command ssh "${args[@]}" "$remote" BRC=t bash + fi + fi + # this function inspired from https://github.com/Russell91/sshrc +} +slr() { + sl --rsync "$@" +} +sss() { # ssh solo + sl -oControlMaster=no -oControlPath=/ "$@" +} +# kill off old shared socket then ssh +ssk() { + m ssh -O exit "$@" || [[ $? == 255 ]] + m sl "$@" +} +# plain ssh +ssh() { + BRC=t command ssh "$@" +} + + slog() { # log with script. timing is $1.t and script is $1.s # -l to save to ~/typescripts/ diff --git a/brc2 b/brc2 index 8da9bbf..be1aca1 100644 --- a/brc2 +++ b/brc2 @@ -22,6 +22,7 @@ path-add --ifexists --end /a/opt/scancode-toolkit-3.10. export WCDHOME=/a + # * include files # generated instead of dynamic for the benefit of shellcheck @@ -1023,179 +1024,6 @@ spend() { sudo systemctl suspend } -# ssh, copy my universal config over if needed. - -# By default .bashrc is sourced for ALL ssh commands. This is wonky. -# Normally, this file is not sourced when a script is run, but we can -# override that by sourcing ~/.bashrc. I want the same thing for ssh -# commands. when a local script runs an ssh command, bashrc should not be -# sourced, unless we use a modified command. -# -# So, in my bashrc, test for conditions of noninteractive ssh and return -# if so. And we don't keep the rest of the code in .bashrc, because -# even though we return, we parse the whole file which can cause errors -# as we develop it. -# -# To test for an overriding condition: bash builtin vars and env show no -# difference in ssh vs local, except shell level which is not -# reliable. one option is to use an environment variable. env variables -# sent across ssh are strictly limited. We could override an obscure -# unused LC_var, like telephone, but I don't want to run into some edge -# case where that messes things up. I choose to set SendEnv and -# AcceptEnv ssh config vars to allow the environment variable BRC to -# propagate across ssh, and for hosts I don't control, I start an inner -# shell with it set, which doubles up as a way to have a nondefault -# bashrc. -sl() { - # inspired from https://github.com/Russell91/sshrc - - - local now args remote dorsync haveinfo tmpa sshinfo tmp tmp2 type info_sec force_rsync \ - sshinfo_dir sync_files_dir sync_files_dir - declare -a args tmpa - # This folder stores info about what we detected or synced. We expect it to exist - # ahead of time. Change it for your own system. - sshinfo_dir=/p/sshinfo - - # This folder should contain files or symlinks to all files that - # should be rsynced to unknown hosts. The directory name is important, - # it will be created on the remote host under /root. symlinks will be - # resolved. It is expected to contain at least a .bashrc file or - # symlink. - sync_files_dir=/b/ds/sl/.iank - sync_dirname=${sync_files_dir##*/} - - now=$(date +%s) - - # ssh [-1246Antivivisectionist] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] - # [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address] - # [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] - # [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname - # [command] - - # ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] - # [-D [bind_address:]port] [-E log_file] [-e escape_char] - # [-F configfile] [-I pkcs11] [-i identity_file] - # [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec] - # [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] - # [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] - - force_rsync=false - if [[ $1 == --rsync ]]; then - force_rsync=true - shift - fi - - while [[ $1 ]]; do - case "$1" in - # note we dont support things like -4oOption - -[46AaCfGgKkMNnqsTtVvXxYy]*) - args+=("$1"); shift - ;; - -[bcDEeFIiJLlmOopQRSWw]*) - # -oOption etc is valid - if (( ${#1} >= 3 )); then - args+=("$1"); shift - else - args+=("$1" "$2"); shift 2 - fi - ;; - *) - break - ;; - esac - done - remote="$1"; shift - if [[ ! $remote ]]; then - echo $0: error hostname required >&2 - return 1 - fi - dorsync=false - haveinfo=false - - tmpa=($sshinfo_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} - type=${tmp2: -1} - if [[ $type == b ]]; then - info_sec=${tmp::10} - for f in $sync_files_dir/*; do - if (( $(stat -L -c%Y $f) > info_sec )); then - dorsync=true - rm -f $sshinfo - break - fi - done - fi - else - # use this weird yes thing to ensure we know ssh succeeded - if ! tmp=$(command ssh "${args[@]}" "$remote" "if test -e $sync_files_dir/.bashrc -a -L .bashrc; then echo yes; fi"); then - echo failed sl test. doing plain ssh -v - command ssh -v "${args[@]}" "$remote" - fi - if [[ $tmp == yes ]]; then - type=a - else - dorsync=true - type=b - fi - fi - if $dorsync; then - RSYNC_RSH="ssh ${args[*]}" rsync -rptL $sync_files_dir "$remote": - fi - if $dorsync || ! $haveinfo; then - sshinfo=$sshinfo_dir/$now$type"$remote" - touch $sshinfo - chmod 666 $sshinfo - fi - if [[ $type == b ]]; then - if (( ${#@} )); then - - # Theres a couple ways to do this. im not sure whats best, - # but relying on bash 4.4+ escape quoting seems most reliable. - command ssh "${args[@]}" "$remote" \ - BRC=t bash -c '.\ '$sync_dirname'/.bashrc\;"\"\$@\""' bash ${@@Q} - elif [[ ! -t 0 ]]; then - # This case is when commands are being piped to ssh. - # Normally, no bashrc gets sourced. - # But, since we are doing all this, lets source it because we can. - cat <(echo . $sync_dirname/.bashrc) - | command ssh "${args[@]}" "$remote" BRC=t bash - else - command ssh -t "${args[@]}" "$remote" BRC=t INPUTRC=$sync_dirname/.inputrc bash --rcfile $sync_dirname/.bashrc - fi - else - if [[ -t 0 ]]; then - BRC=t command ssh "${args[@]}" "$remote" ${@@Q} - else - command ssh "${args[@]}" "$remote" BRC=t bash - fi - fi -} -slr() { - sl --rsync "$@" -} -sss() { # ssh solo - sl -oControlMaster=no -oControlPath=/ "$@" -} -# kill off old shared socket then ssh -ssk() { - m ssh -O exit "$@" || [[ $? == 255 ]] - m sl "$@" -} -# plain limited ssh -ssh() { - BRC=t command ssh "$@" -} - # mail related testmail() { -- 2.30.2