diff --git a/scwrypts/ssh/configure b/scwrypts/ssh/configure new file mode 100755 index 0000000..aa66928 --- /dev/null +++ b/scwrypts/ssh/configure @@ -0,0 +1,6 @@ +#!/bin/zsh +##################################################################### + +MAIN() { + EDIT "$REMOTE_CONNECTIONS_FILE" +} diff --git a/scwrypts/ssh/connect b/scwrypts/ssh/connect new file mode 100755 index 0000000..f89282d --- /dev/null +++ b/scwrypts/ssh/connect @@ -0,0 +1,143 @@ +#!/bin/zsh +use config --group remote +##################################################################### + +USAGE__options=" + -c, --command override configured remote command + -s, --shell override configured remote login shell + -t, --type one of the following connection types: + - ssh (default) simple ssh execution + - xserver ssh connection with remote-xserver settings + - tmux connect directly to configured, remote tmux session + + --no-rc disable loading the login RC for 'sh -c' commands + --no-tty don't request a tty connection + + --force-local-login force login through ssh when working with localhost +" + +USAGE__description=" + Connection / bastion wrapper for $REMOTE__TARGET. To configure, + use 'scwrypts remote configure' +" + + +##################################################################### + +MAIN() { + [ $REMOTE__TARGET ] \ + || ERROR 'missing REMOTE__TARGET context; this must be run through scwrypts' \ + || return 1 + + local CONNECTION_TYPE=ssh + local REMOTE_NAME=$REMOTE__TARGET + local REMOTE_COMMAND LOGIN_SHELL + local LOAD_RC=true + local USE_TTY=true + local FORCE_LOCAL_LOGIN=false + + while [[ $# -gt 0 ]] + do + case $1 in + -t | --type ) CONNECTION_TYPE=$2; shift 1 ;; + -c | --command ) REMOTE_COMMAND=$2; shift 1 ;; + -s | --shell ) LOGIN_SHELL=$2; shift 1 ;; + + --no-rc ) LOAD_RC=false ;; + --no-tty ) USE_TTY=false ;; + + --force-local-login ) FORCE_LOCAL_LOGIN=true ;; + + * ) ERROR "unknown argument '$1'" ;; + esac + shift 1 + done + + CHECK_ERRORS + + ########################################## + + GET_SSH_ARGS() { + REMOTE__GET_SSH_ARGS \ + --type $CONNECTION_TYPE \ + --use-tty $USE_TTY \ + $REMOTE_NAME \ + ; + } + + GET_SSH_ARGS >/dev/null \ + && local SSH_ARGS=($(GET_SSH_ARGS)) \ + || FAIL 1 'unable to load ssh args; aborting' + + ########################################## + + local CONNECTION_STRING=$(REMOTE__GET_CONNECTION_STRING $REMOTE_NAME) + [ $CONNECTION_STRING ] \ + || FAIL 1 'unable to determine connection string' + + ########################################## + + LOGIN_SHELL=$(\ + REMOTE__QUERY_CONNECTION_WITH_FALLBACK \ + "$LOGIN_SHELL" \ + ".$REMOTE_NAME.shell" \ + ".default.shell" \ + "zsh" \ + ) + + REMOTE_COMMAND=$(\ + REMOTE__QUERY_CONNECTION_WITH_FALLBACK \ + "$REMOTE_COMMAND" \ + ".$REMOTE_NAME.$CONNECTION_TYPE.command" \ + ".$REMOTE_NAME.command" \ + ".default.$CONNECTION_TYPE.command" \ + ".default.command" \ + ) + + [ $REMOTE_COMMAND ] || { + case $CONNECTION_TYPE in + tmux ) + local TMUX_SESSION_NAME=$( + REMOTE__QUERY_CONNECTION_WITH_FALLBACK \ + ".sessions.$REMOTE_NAME.tmux.session" \ + ".default.tmux.session" \ + "wryn" \ + ) + local TMUX_ARGS=() + + locale charmap 2>/dev/null | grep -qi 'UTF-8' \ + && TMUX_ARGS+=(-u) + + REMOTE_COMMAND="tmux ${TMUX_ARGS[@]} new-session -As $TMUX_SESSION_NAME" + ;; + esac + } + + [ $REMOTE_COMMAND ] && [[ $LOAD_RC =~ true ]] && { + REMOTE_COMMAND="$LOGIN_SHELL -l -c 'source ~/.$(basename $LOGIN_SHELL)rc &>/dev/null; $REMOTE_COMMAND'" + } + + [ ! $REMOTE_COMMAND ] && { + [[ $LOAD_RC =~ true ]] \ + && REMOTE_COMMAND="$LOGIN_SHELL -l" \ + || REMOTE_COMMAND="$LOGIN_SHELL" + } + + ########################################## + + STATUS " + attempting execution on $REMOTE_NAME + type : $CONNECTION_TYPE + connection : $REMOTE_NAME + command : \"$REMOTE_COMMAND\" + " + + case $CONNECTION_STRING in + localhost | $USER@localhost ) + eval "$REMOTE_COMMAND" + return $? + ;; + esac + + ssh ${SSH_ARGS[@]} $CONNECTION_STRING "$REMOTE_COMMAND" +} diff --git a/scwrypts/ssh/connect/ssh b/scwrypts/ssh/connect/ssh deleted file mode 100755 index 2559f14..0000000 --- a/scwrypts/ssh/connect/ssh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/zsh -use ssh --group remote - -CHECK_ENVIRONMENT - -REMOTE__LOGIN $@ diff --git a/scwrypts/ssh/connect/tmux b/scwrypts/ssh/connect/tmux deleted file mode 100755 index 39e1b18..0000000 --- a/scwrypts/ssh/connect/tmux +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/zsh -use tmux --group remote - -CHECK_ENVIRONMENT - -REMOTE_CONNECT_TMUX $@ diff --git a/scwrypts/ssh/connect/xserver b/scwrypts/ssh/connect/xserver deleted file mode 100755 index 13fedde..0000000 --- a/scwrypts/ssh/connect/xserver +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/zsh -use ssh --group remote - -CHECK_ENVIRONMENT - -REMOTE_ARGS=(-Y) REMOTE__LOGIN $@ diff --git a/scwrypts/ssh/lib/config.module.zsh b/scwrypts/ssh/lib/config.module.zsh new file mode 100644 index 0000000..a81eecd --- /dev/null +++ b/scwrypts/ssh/lib/config.module.zsh @@ -0,0 +1,99 @@ +##################################################################### + +DEPENDENCIES+=(yq) + +##################################################################### + +REMOTE__GET_CONNECTION_STRING() { + local REMOTE_NAME="$1" + [ $(REMOTE__QUERY_CONNECTION .sessions.$REMOTE_NAME.host) ] \ + || ERROR "no such connection $REMOTE_NAME exists" \ + || return 1 + + local CONNECTION_HOST=$(REMOTE__QUERY_CONNECTION .sessions.$REMOTE_NAME.host) + [ $CONNECTION_HOST ] \ + || ERROR "connection $REMOTE_NAME is misconfigured; missing 'host' field" \ + || return 1 + + local CONNECTION_USER=$(REMOTE__QUERY_CONNECTION .sessions.$REMOTE_NAME.user) + [ $CONNECTION_USER ] || CONNECTION_USER=$(REMOTE__QUERY_CONNECTION .default.user) + + [ $CONNECTION_USER ] \ + && CONNECTION_STRING="${CONNECTION_USER}@${CONNECTION_HOST}" \ + || CONNECTION_STRING="$CONNECTION_HOST" \ + ; + + echo $CONNECTION_STRING +} + +REMOTE__GET_SSH_ARGS() { + local REMOTE_NAME + local TYPE=ssh + local USE_TTY=true + + while [[ $# -gt 0 ]] + do + case $1 in + -t | --type ) TYPE=$2; shift 1 ;; + --use-tty ) USE_TTY=$2; shift 1 ;; + + --no-tty ) USE_TTY=false ;; + + * ) + [ $REMOTE_NAME ] && { ERROR "too many args :c"; return 1; } + REMOTE_NAME=$1 + ;; + esac + shift 1 + done + + local ARGS=() + [ $REMOTE_NAME ] || { + echo "${ARGS[@]}" + return 0 + } + + local PORT=$(REMOTE__QUERY_CONNECTION .sessions.$REMOTE_NAME.port) + + [ $PORT ] && { + case $TYPE in + ssh | xserver | tmux ) ARGS+=(-p $PORT) ;; + scp ) ARGS+=(-P $PORT) ;; # not really in use, just a sample + * ) + WARNING " + port is specified, but I'm not sure whether to use '-p' or '-P' + + if this command fails, try adding your --type to the appropriate + list in '$SCWRYPTS_ROOT__remote/lib/config.module.zsh' + " + ARGS+=(-p $PORT) + ;; + esac + } + + ARGS+=($(REMOTE__QUERY_CONNECTION .session.$REMOTE_NAME.$TYPE.args)) + + [[ $USE_TTY =~ true ]] && ARGS+=(-t) + + echo "${ARGS[@]}" +} + +##################################################################### + +REMOTE__QUERY_CONNECTION() { + YQ -oy -r $@ "$REMOTE_CONNECTIONS_FILE" \ + | grep -v ^null$ +} + +REMOTE__QUERY_CONNECTION_WITH_FALLBACK() { + while [[ $# -gt 0 ]] && [ ! $QUERY_RESULT ] + do + case $1 in + .* ) QUERY_RESULT=$(REMOTE__QUERY_CONNECTION $1) ;; + * ) QUERY_RESULT="$1" ;; # allows raw default value + esac + shift 1 + done + + echo $QUERY_RESULT +} diff --git a/scwrypts/ssh/lib/connection/parse.module.zsh b/scwrypts/ssh/lib/connection/parse.module.zsh deleted file mode 100644 index 351ebe2..0000000 --- a/scwrypts/ssh/lib/connection/parse.module.zsh +++ /dev/null @@ -1,47 +0,0 @@ -##################################################################### - -DEPENDENCIES+=() -REQUIRED_ENV+=() - -##################################################################### - -PARSE_CONNECTION() { - [ ! $CONNECTION ] && [ ! $REMOTE_NAME ] && { - ERROR 'cannot parse connection without specifying CONNECTION or REMOTE_NAME' - return 1 - } - - [ $REMOTE_NAME ] && CONNECTION=$(GET_CONNECTION $REMOTE_NAME) - - [ ! $CONNECTION ] && { - ERROR "no such connection with name '$REMOTE_NAME'" - } - - local CONNECTION_DETAILS=$(echo $CONNECTION | sed 's/|/ /g;') - - REMOTE_ID=$(echo $CONNECTION_DETAILS | awk '{print $1;}') - REMOTE_NAME=$(echo $CONNECTION_DETAILS | awk '{print $2;}') - REMOTE_HOST=$(echo $CONNECTION_DETAILS | awk '{print $3;}') - REMOTE_ARGS+=($(echo $CONNECTION_DETAILS | awk '{$1=$2=$3="";}1')) -} - -GET_CONNECTIONS() { - ( - echo '0 | localhost | localhost' - echo "0 | $(hostnamectl --static) | localhost" - sed -n 's/#.*//;s/ \+$//;/./p' "$REMOTE_CONNECTIONS_FILE" - ) | sort -u -} - -GET_CONNECTION() { - [ ! $1 ] && return 1 - GET_CONNECTIONS | grep "^[0-9]\+ *| *$1 *|" | head -n1 -} - -GET_CONNECTION_NAMES() { - echo ($(GET_CONNECTIONS | awk '{print $1;}')) -} - -IS_VALID_CONNECTION_NAME() { - GET_CONNECTION $1 | grep -q '|' -} diff --git a/scwrypts/ssh/lib/ssh.module.zsh b/scwrypts/ssh/lib/ssh.module.zsh deleted file mode 100644 index d7abcb9..0000000 --- a/scwrypts/ssh/lib/ssh.module.zsh +++ /dev/null @@ -1,45 +0,0 @@ -##################################################################### - -DEPENDENCIES+=( - ssh -) -REQUIRED_ENV+=() - -use connection/parse --group remote - -##################################################################### - -[ ! $REMOTE_DEFAULT_SHELL ] && REMOTE_DEFAULT_SHELL=(zsh -l) - -REMOTE__LOGIN() { - local READ_COMMAND=0 - - [ ! $REMOTE_NAME ] && { - REMOTE_NAME=$1 - shift 1 - } - - [[ $# -gt 0 ]] && { - REMOTE_SHELL_ARGS+=(-c "'$@'") - } - - [ $REMOTE_NAME ] && { - STATUS "connecting to $REMOTE_NAME" - } - - PARSE_CONNECTION - - [ ! $REMOTE_HOST ] && { - ERROR "could not parse REMOTE_HOST from REMOTE_NAME; - check the configuration file for errors" - return 1 - } - - [[ $REMOTE_HOST =~ ^localhost$ ]] && { - WARNING 'performing login to localhost' - eval "cd; "${REMOTE_DEFAULT_SHELL[@]} ${REMOTE_SHELL_ARGS[@]} - return $? - } - - ssh -t ${REMOTE_ARGS[@]} $REMOTE_HOST "$REMOTE_DEFAULT_SHELL ${REMOTE_SHELL_ARGS[@]} $@" -} diff --git a/scwrypts/ssh/lib/tmux.module.zsh b/scwrypts/ssh/lib/tmux.module.zsh deleted file mode 100644 index d59f40f..0000000 --- a/scwrypts/ssh/lib/tmux.module.zsh +++ /dev/null @@ -1,36 +0,0 @@ -##################################################################### - -DEPENDENCIES+=( - tmux -) -REQUIRED_ENV+=() - -use ssh --group remote - -##################################################################### - -REMOTE_CONNECT_TMUX() { - local REMOTE_NAME="$1" - local USAGE=" - usage: REMOTE_NAME - Connect to remote tmux session on '$REMOTE_NAME' - " - - local REMOTE_HOST - local REMOTE_ARGS=() - - IS_VALID_CONNECTION_NAME $REMOTE_NAME || ERROR "invalid REMOTE_NAME '$REMOTE_NAME'" - - CHECK_ERRORS - - local TMUX_ARGS=() - - # enforce UTF-8 if supported by host locale - TMUX_ARGS+=($(locale charmap | grep -q 'UTF-8' && echo '-u')) - - # create or connect to session - TMUX_ARGS+=(new-session -As wryn) - - REMOTE__LOGIN "tmux ${TMUX_ARGS[@]}" -} - diff --git a/scwrypts/ssh/omni/launcher b/scwrypts/ssh/omni/launcher deleted file mode 100755 index 1409b76..0000000 --- a/scwrypts/ssh/omni/launcher +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/zsh -DEPENDENCIES+=( - tmux - hostnamectl - scwrypts # must be on path -) - -CHECK_ENVIRONMENT - -##################################################################### - -[[ $TERM =~ tmux ]] && FAIL 69 "\n Cannot run tmux-omni within a tmux session!\n " - -OMNI_SOCKET="omni.socket" -OMNI_TMUX() { tmux -L $OMNI_SOCKET $@; } - -OMNI_TMUX list-sessions 2>/dev/null | grep -v omni-manager | grep -qi omni || { - STATUS "initializing omni server" - OMNI_TMUX kill-session -t=omni-manager >/dev/null 2>&1 - - OMNI_TMUX -f "$SCWRYPTS_ROOT__remote/omni/tmux.conf" new -d -s omni \ - "echo searching for first connection...; sleep 30" \; \ - split-window "sleep 3; TMUX= tmux -L $OMNI_SOCKET a -t=omni-manager" \; \ - move-window -t 99 \; - - OMNI_TMUX new -d -s omni-manager "$SCWRYPTS_ROOT__remote/omni/manager" -} - -STATUS 'connecting to omni server' -OMNI_TMUX a -t=omni diff --git a/scwrypts/ssh/omni/manager b/scwrypts/ssh/omni/manager deleted file mode 100755 index d884e0c..0000000 --- a/scwrypts/ssh/omni/manager +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/zsh -# -# works as a standalone zsh script -# -CONNECTIONS_FILE="$HOME/.config/wryn/remote-connections" -OMNI_SOCKET="omni.socket" -OMNI_TMUX() { tmux -L $OMNI_SOCKET $@; } - -sleep 1 -OMNI_TMUX new-window -t=omni-manager -dn 'harakiri' " -while true -do - tmux -L $OMNI_SOCKET list-sessions | grep -v omni-manager | grep -qi omni || { - tmux -L $OMNI_SOCKET kill-session -t omni-manager - } - sleep 5 -done -" - -CONNECTED() { echo "\\033[1;32mconnected\\033[0m"; } -DISCONNECTED() { echo "\\033[1;31mdisconnected\\033[0m"; } - -SET_UNIQUE_ID() { - i=0 - while : \ - && [[ $(OMNI_TMUX list-windows -t=omni | grep "^$REMOTE_ID:" | wc -l) -gt 0 ]] \ - && [[ ! $(OMNI_TMUX list-windows -t=omni | grep "^$REMOTE_ID:" | awk '{print $2;}') =~ ^$REMOTE_NAME ]] \ - ; - do - echo "$REMOTE_NAME wants id $REMOTE_ID, but it's in use" - sleep 1 - REMOTE_ID=$((69-$i)) - ((i+=1)) - [[ $i -gt 10 ]] && break - done -} - -while true -do - clear - REMOTE_HOSTS=() - CONNECTION_LIST=$(cat "$CONNECTIONS_FILE") - cat "$CONNECTIONS_FILE" | grep -q localhost || { - CONNECTION_LIST=$(echo "1 | $(hostnamectl --static) | localhost\n$CONNECTION_LIST") - } - - CONNECTION_LIST=$( - echo "$CONNECTION_LIST" | grep localhost - echo "$CONNECTION_LIST" | grep -v localhost - ) - - for CONNECTION in $(sed -n 's/#.*//;s/ \+$//;s/|//g;s/ \+/^/g;/./p;' <<< $CONNECTION_LIST) - do - CONNECTION=$(echo $CONNECTION | sed 's/[\^]/ /g') - echo "'$CONNECTION'" - - REMOTE_ID=$(echo $CONNECTION | awk '{print $1;}') - REMOTE_NAME=$(echo $CONNECTION | awk '{print $2;}') - REMOTE_HOST=$(echo $CONNECTION | awk '{print $3;}') - REMOTE_ARGS=($(echo $CONNECTION | awk '{$1=$2=$3="";}1')) - - SET_UNIQUE_ID - - printf "$REMOTE_NAME : " - - OMNI_TMUX list-windows -t=omni | awk '{print $2;}' | grep -q $REMOTE_NAME \ - && CONNECTED && continue - - SUCCESS=0 - [[ $REMOTE_HOST =~ ^localhost$ ]] && SUCCESS=1 - - [[ $SUCCESS -eq 1 ]] || { - timeout 3 ssh ${REMOTE_ARGS} -o BatchMode=yes $REMOTE_HOST 'command -v tmux 2>/dev/null' 2>/dev/null | grep -q tmux \ - && SUCCESS=1 - } - - [[ $SUCCESS -eq 1 ]] && { - OMNI_TMUX new-window -t=omni:$REMOTE_ID -dn $REMOTE_NAME "source ~/.zshrc >/dev/null 2>&1; TMUX= scwrypts -n connect tmux $REMOTE_NAME; echo 'connection closed'; sleep 2" - OMNI_TMUX list-window -t=omni | awk '{print $1;}' | grep -q '99:' \ - && OMNI_TMUX kill-window -t omni:99 - } - - OMNI_TMUX list-windows -t=omni | awk '{print $2;}' | grep -q $REMOTE_NAME \ - && CONNECTED || DISCONNECTED - done - - echo "\nPress ENTER to search again now (checks every 60 seconds)" - read -t 60 -done diff --git a/scwrypts/ssh/omni/spawn b/scwrypts/ssh/omni/spawn deleted file mode 100755 index 1babd9d..0000000 --- a/scwrypts/ssh/omni/spawn +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/zsh -CHECK_ENVIRONMENT -##################################################################### - -[[ $TERM =~ tmux ]] && FAIL 69 "\n Cannot run tmux-omni within a tmux session!\n " - -OMNI_SOCKET="omni.socket" -OMNI_TMUX() { tmux -L $OMNI_SOCKET $@; } - -OMNI_TMUX list-sessions 2>/dev/null | grep -v omni-manager | grep -qi omni || { - STATUS "initializing omni server" - OMNI_TMUX kill-session -t=omni-manager >/dev/null 2>&1 - - OMNI_TMUX -f "$SCWRYPTS_ROOT__remote/omni/tmux.conf" new -d -s omni \ - "echo searching for first connection...; sleep 30" \; \ - split-window "sleep 3; TMUX= tmux -L $OMNI_SOCKET a -t=omni-manager" \; \ - move-window -t 99 \; - - OMNI_TMUX new -d -s omni-manager "$SCWRYPTS_ROOT__remote/omni/manager" -} - -STATUS 'connecting to omni server' -OMNI_TMUX a -t=omni diff --git a/scwrypts/ssh/ssh.scwrypts.zsh b/scwrypts/ssh/ssh.scwrypts.zsh index 56c238a..bc70236 100644 --- a/scwrypts/ssh/ssh.scwrypts.zsh +++ b/scwrypts/ssh/ssh.scwrypts.zsh @@ -5,47 +5,42 @@ export SCWRYPTS_TYPE__remote=zsh export SCWRYPTS_ROOT__remote="$DOTWRYN/scwrypts/ssh" export SCWRYPTS_COLOR__remote='\033[0;34m' -REMOTE_CONNECTIONS_FILE="$HOME/.config/wryn/remote-connections" +DEPENDENCIES+=(yq) +REMOTE_CONNECTIONS_FILE="$HOME/.config/wryn/remote-connections.toml" SCWRYPTS__LIST_AVAILABLE_SCWRYPTS__remote() { - [ ! -f "$REMOTE_CONNECTIONS_FILE" ] && { - [ -f "$HOME/.config/wryn/ssh-connections" ] \ - && cp "$HOME/.config/wryn/ssh-connections" "$REMOTE_CONNECTIONS_FILE" \ - || touch "$REMOTE_CONNECTIONS_FILE" + [ -f "$REMOTE_CONNECTIONS_FILE" ] || { + mkdir -p "$(dirname -- "$REMOTE_CONNECTIONS_FILE")" &>/dev/null + echo " + [sessions] + + [sessions.$(hostnamectl --static)] + enabled = true + host = localhost + " | sed 's/^\s\+//; 1d; $d;' > "$REMOTE_CONNECTIONS_FILE" } - for CONNECTION_TYPE in $(cd $SCWRYPTS_ROOT__remote/connect; find . -mindepth 1 -type f -executable) - do - CONNECTION_TYPE="$(echo $CONNECTION_TYPE | sed 's/\.\///;')" - ( - echo "0 | $(hostnamectl --static) | localhost" - echo '0 | localhost | localhost' - cat "$REMOTE_CONNECTIONS_FILE" - ) | sed -n 's/#.*//; /./p' | awk '{print $3;}' | sort -u | sed "s|^|$SCWRYPTS_TYPE__remote/connect/$CONNECTION_TYPE/|" - done + { + yq -oy -r '.sessions | keys | .[]' "$REMOTE_CONNECTIONS_FILE" \ + | sed 's|^|connect/|' - echo "$SCWRYPTS_TYPE__remote/tmux/omni" - echo "$SCWRYPTS_TYPE__remote/tmux/spawn" - echo "$SCWRYPTS_TYPE__remote/remote/configure" + echo "tmux/omni" + echo "configure" + echo "test" + } | sed "s|^|$SCWRYPTS_TYPE__remote/|" } -SCWRYPTS__GET_RUNSTRING__remote__$SCWRYPTS_TYPE__remote() { - [[ $SCWRYPT_NAME =~ tmux/omni ]] \ - && echo "source $SCWRYPTS_ROOT__remote/omni/launcher" \ - && return 0 +SCWRYPTS__GET_RUNSTRING__remote__zsh() { + local SCWRYPT_FILENAME + case $SCWRYPT_NAME in + connect/* ) + SCWRYPT_FILENAME="$SCWRYPTS_ROOT__remote/connect" + echo "export REMOTE__TARGET=$(echo $SCWRYPT_NAME | sed 's|^.*connect/||')" + ;; + * ) + SCWRYPT_FILENAME="$SCWRYPTS_ROOT__remote/$SCWRYPT_NAME" + ;; + esac - [[ $SCWRYPT_NAME =~ tmux/spawn ]] \ - && echo "source $SCWRYPTS_ROOT__remote/omni/spawn" \ - && return 0 - - [[ $SCWRYPT_NAME =~ remote/configure ]] \ - && echo "EDIT $REMOTE_CONNECTIONS_FILE" \ - && return 0 - - local DETAILS=$(echo $SCWRYPT_NAME | sed 's|connect||; s|/| |g;') - - local CONNECTION_TYPE=$(echo $DETAILS | awk '{print $1;}') - local TARGET=$(echo $DETAILS | awk '{print $2;}') - - echo "source $SCWRYPTS_ROOT__remote/connect/$CONNECTION_TYPE $TARGET" + SCWRYPTS__GET_RUNSTRING__zsh__generic "$SCWRYPT_FILENAME" } diff --git a/scwrypts/ssh/test b/scwrypts/ssh/test new file mode 100755 index 0000000..ccbbc2f --- /dev/null +++ b/scwrypts/ssh/test @@ -0,0 +1,84 @@ +#!/bin/zsh +use config --group remote + +DEPENDENCIES+=(timeout ssh) +##################################################################### + +USAGE__options=' + -n, --name session name to test + -s, --connection-string explicit session host / ssh connection string to test + -t, --maximum-timeout maximum connection timeout before failure (in seconds) + -c, --command testing command; performs echo by default +' + +USAGE__description=' + Tests whether you can connect to a particular session or + host string. +' + +##################################################################### + +MAIN() { + local CONNECTION_STRING REMOTE_NAME + local TIMEOUT_SECONDS=3 + local COMMAND='echo OK &>/dev/null' + while [[ $# -gt 0 ]] + do + case $1 in + -n | --name ) + REMOTE_NAME=$2 + CONNECTION_STRING=$(REMOTE__GET_CONNECTION_STRING $REMOTE_NAME) + shift 1 + ;; + -s | --connection_string ) + CONNECTION_STRING="$2" + shift 1 + ;; + -t | --maximum-timout ) + TIMEOUT_SECONDS=$2 + [[ $TIMEOUT_SECONDS -gt 0 ]] \ + || ERROR "invalid timeout seconds '$TIMEOUT_SECONDS'" + shift 1 + ;; + -c | --command ) + COMMAND=$2 + shift 1 + ;; + * ) ERROR "unrecognized argument '$1'" ;; + esac + shift 1 + done + + [ $CONNECTION_STRING ] \ + || ERROR "unable to determine connection string" + + CHECK_ERRORS + + ########################################## + + case $CONNECTION_STRING in + localhost | $USER@localhost ) + CONNECTION_TEST() { return 0; } # current user on local machine can always connect + ;; + * ) + CONNECTION_TEST() { + [ $REMOTE_NAME ] && { + [[ $(REMOTE__QUERY_CONNECTION .sessions.$REMOTE_NAME) =~ false ]] && { + return 1 + } + } + local REMOTE_ARGS=() + REMOTE_ARGS+=($(REMOTE__GET_SSH_ARGS --type ssh --no-tty $REMOTE_NAME)) + REMOTE_ARGS+=(-o BatchMode=yes) + DEBUG "attempting\nssh ${REMOTE_ARGS[@]} \"$CONNECTION_STRING\" \"$COMMAND\"" + timeout $TIMEOUT_SECONDS ssh ${REMOTE_ARGS[@]} "$CONNECTION_STRING" "$COMMAND" >&2 + } + ;; + esac + + [ $REMOTE_NAME ] || REMOTE_NAME=explicit + STATUS "testing connection $CONNECTION_STRING ($REMOTE_NAME)" \ + && CONNECTION_TEST \ + && SUCCESS "successfully connected to '$CONNECTION_STRING' ($REMOTE_NAME)" \ + || ERROR "connection to '$CONNECTION_STRING ($REMOTE_NAME)' failed" \ +} diff --git a/scwrypts/ssh/tmux/manager b/scwrypts/ssh/tmux/manager new file mode 100755 index 0000000..6d0f4cf --- /dev/null +++ b/scwrypts/ssh/tmux/manager @@ -0,0 +1,131 @@ +#!/bin/zsh +# +# works as a standalone zsh script +# + +##################################################################### + +CONNECTIONS_FILE="$HOME/.config/wryn/remote-connections.toml" + +OMNI_SOCKET="omni.socket" +OMNI_TMUX() { tmux -L $OMNI_SOCKET $@; } + +CONFIG_QUERY() { yq -oy -r $@ "$CONNECTIONS_FILE" | grep -v ^null$; } + +CONNECTED() { + [ $WINDOW_ID ] \ + && echo "\\033[1;32mconnected (window $WINDOW_ID)\\033[0m" >&2 \ + || echo "\\033[1;32mconnected\\033[0m" >&2 \ +} +DISCONNECTED() { echo "\\033[1;31mdisconnected\\033[0m" >&2; } + +GET_UNIQUE_WINDOW_ID() { + local MODE=use-default + + local WINDOW_ID="$(eval "echo \$WINDOW_ID__$REMOTE_NAME")" + : \ + && [[ $(eval "echo \$CAN_CONNECT__$REMOTE_NAME") =~ true ]] \ + && [ $WINDOW_ID ] \ + && echo $WINDOW_ID \ + && return 0 \ + ; + + unset WINDOW_ID__$REMOTE_NAME &>/dev/null + + WINDOW_ID="$1" + WINDOW_ID=$(CONFIG_QUERY .sessions.$REMOTE_NAME.id) + [ $WINDOW_ID ] && MODE=specify-preferred + + WINDOW_ID_IS_TAKEN() { + : \ + && [[ $(OMNI_TMUX list-windows -t=omni | grep "^$WINDOW_ID:" | wc -l) -gt 0 ]] \ + && [[ ! $(OMNI_TMUX list-windows -t=omni | grep "^$WINDOW_ID:" | awk '{print $2;}' =~ ^$REMOTE_NAME$) ]] \ + ; + } + + local FALLBACK_STARTING_POINT + case $MODE in + use-default ) FALLBACK_STARTING_POINT=42 ;; + specify-preferred ) FALLBACK_STARTING_POINT=69 ;; + esac + + local I=0 + while WINDOW_ID_IS_TAKEN + do + echo "$REMOTE_NAME tried to acquire window id $WINDOW_ID, but it is already in-use (trying $(($FALLBACK_STARTING_POINT-$I)))" >&2 + WINDOW_ID=$(($FALLBACK_STARTING_POINT-$I)) + ((I+=1)) + [[ $I -gt 10 ]] && WINDOW_ID= && break + done + + echo $WINDOW_ID +} + +##################################################################### + +sleep 1 +OMNI_TMUX new-window -t=omni-manager -dn 'harakiri' " +while true +do + tmux -L $OMNI_SOCKET list-sessions | grep -v omni-manager | grep -qi omni || { + tmux -L $OMNI_SOCKET kill-session -t omni-manager + } + sleep 5 +done +" + +while true +do + clear + for REMOTE_NAME in $(CONFIG_QUERY '.sessions | keys | .[]') + do + WINDOW_ID=$(GET_UNIQUE_WINDOW_ID) + export WINDOW_ID__$REMOTE_NAME=$WINDOW_ID + + OMNI_TMUX list-windows -t=omni | awk '{print $2;}' | grep -q $REMOTE_NAME \ + && continue + + printf "testing connection $REMOTE_NAME..." + scwrypts -n --name test --group remote --type zsh \ + -- \ + --name $REMOTE_NAME \ + --command 'command -v tmux' \ + &>/dev/null \ + && export CAN_CONNECT__$REMOTE_NAME=true \ + || export CAN_CONNECT__$REMOTE_NAME=false \ + ; + + [[ $(eval echo "\$CAN_CONNECT__$REMOTE_NAME") =~ true ]] \ + && echo " \\033[1;32m✔\\033[0m" >&2 \ + || { echo " \\033[1;31m✖\\033[0m" >&2; continue; } + + OMNI_TMUX new-window \ + -t=omni:$WINDOW_ID \ + -dn $REMOTE_NAME " + source ~/.zshrc &>/dev/null + TMUX='' scwrypts -n connect/$REMOTE_NAME -- --type tmux + echo 'connection closed' + sleep 2 + " + + OMNI_TMUX list-window -t=omni | awk '{print $1;}' | grep -q '99:' && OMNI_TMUX kill-window -t omni:99 + + OMNI_TMUX list-windows -t=omni | awk '{print $2;}' | grep -q $REMOTE_NAME \ + || export CAN_CONNECT__$REMOTE_NAME=false + done + + clear + echo "connections:\n" + local STATUS + { + for REMOTE_NAME in $(CONFIG_QUERY '.sessions | keys | .[]') + do + [[ $(eval "echo \$CAN_CONNECT__$REMOTE_NAME") =~ true ]] \ + && echo "${REMOTE_NAME}^$(eval "echo \$WINDOW_ID__$REMOTE_NAME")^\\033[1;32mconnected\\033[0m" \ + || echo "${REMOTE_NAME}^-^\\033[1;31mdisconnected\\033[0m" \ + ; + done + } | column -ts '^' + echo "\nPress ENTER to search again now (checks every 60 seconds)" >&2 + read -t 60 +done diff --git a/scwrypts/ssh/tmux/omni b/scwrypts/ssh/tmux/omni new file mode 100755 index 0000000..b0df702 --- /dev/null +++ b/scwrypts/ssh/tmux/omni @@ -0,0 +1,53 @@ +#!/bin/zsh +DEPENDENCIES+=(tmux scwrypts) +##################################################################### + +USAGE__description=" + A program which automatically connects to any available connections + described in the configuration file. + (configure with 'scwrypts remote configure') + + The omni-session is running it's own wrapper TMUX server with a special + config. In the wrapper session, 'M-s' is overwritten as the prefix. + Switch between connections by using 'M-s' followed by the session ID + number (or use any other default default tmux navigation command). Full + configuration can be found here: + - $SCWRYPTS_ROOT__remote/omni/tmux.conf + + Shut-down the omni-session by pressing 'M-Q' (Q not q! ALT + SHIFT + Q) + + Disconnect (but leave the session running) with 'M-s, d' + + A background process periodically attempts to refresh lost connections. + Immediate retry can be forced with 'M-R'. + + Running this command in an existing tmux session will likely result + in an infinite loop, so: + - don't run the omni-session in a TMUX session + - don't connect to the omni-session directly (always use this scwrypt) +" + +##################################################################### + +MAIN() { + [[ $TERM =~ tmux ]] && ERROR "\n Cannot run tmux-omni within a tmux session!\n " + CHECK_ERRORS + + local OMNI_SOCKET="omni.socket" + OMNI_TMUX() { tmux -L $OMNI_SOCKET $@; } + + OMNI_TMUX list-sessions 2>/dev/null | grep -v omni-manager | grep -qi omni || { + STATUS "initializing omni server" + OMNI_TMUX kill-session -t=omni-manager >/dev/null 2>&1 + + OMNI_TMUX -f "$SCWRYPTS_ROOT__remote/tmux/tmux.conf" new -d -s omni \ + "echo searching for first connection...; sleep 30" \; \ + split-window "sleep 3; TMUX= tmux -L $OMNI_SOCKET a -t=omni-manager" \; \ + move-window -t 99 \; + + OMNI_TMUX new -d -s omni-manager "$SCWRYPTS_ROOT__remote/omni/manager" + } + + STATUS 'connecting to omni server' + OMNI_TMUX a -t=omni +} diff --git a/scwrypts/ssh/omni/tmux.conf b/scwrypts/ssh/tmux/tmux.conf similarity index 100% rename from scwrypts/ssh/omni/tmux.conf rename to scwrypts/ssh/tmux/tmux.conf