diff --git a/.bashrc b/.bashrc index 26d6dd6..f319c65 100644 --- a/.bashrc +++ b/.bashrc @@ -1,50 +1,58 @@ #!/bin/bash - not strictly necessary, but helps nano with syntax highlighting. __find_ssh_agent_sock() { - # Returns: 0 = Found an alternative socket. + # Find an *active* ssh agent socket. + # Returns: 0 = Found an active socket. # 1 = Did not find a viable socket. - local ERR I + local I # Search the SSH_AUTH_SOCKS array for a viable socket. for ((I = 0; I < ${#SSH_AUTH_SOCKS[@]}; I++)); do + [[ "${SSH_AUTH_SOCKS[$I]}" =~ ^[[:blank:]]*$ ]] && continue SSH_AUTH_SOCK="${SSH_AUTH_SOCKS[$I]}" ssh-add -l >/dev/null 2>&1 - (( $? < 2 )) && { export SSH_AUTH_SOCK="${SSH_AUTH_SOCKS[$I]}"; break; } + (( $? <= 1 )) && { + export SSH_AUTH_SOCK="${SSH_AUTH_SOCKS[$I]}" + return 0 + } unset SSH_AUTH_SOCK done - [[ -z "$SSH_AUTH_SOCK" ]] && return 1 - return 0 + return 1 } __read_ssh_agents() { + # Read all the known ssh agent sockets into an array. # Returns: 0 = Processed and read the agents file without issue. - # 1 = Error processing/read the agents file. - local ERR FD I SOCK + # 1 = Error processing/reading the agents file. + local ERR FD I PLATFORM SOCK - [[ ! -e ~/.ssh/agents ]] && touch ~/.ssh/agents + [[ ! -e "$HOME/.ssh/agents" ]] && touch "$HOME/.ssh/agents" + + # Determine the platform being logged into. + PLATFORM="$(uname -s)" # Lock the ~/.ssh/agents file. - if [[ "$(uname -s)" == "Linux" ]]; then + if [[ "$PLATFORM" == "Linux" ]]; then # Linux has 'flock', thankfully. - { exec {FD}<~/.ssh/agents && flock -E 10 -e -w 0.5 "$FD"; } || { - ERR=$? - if (( ERR == 10 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 - else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error." >&2 - fi + exec {FD}<"$HOME/.ssh/agents" && flock -E 10 -e -w 0.5 "$FD" + ERR=$? + if (( ERR == 10 )); then + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 - } + elif (( ERR > 0 )); then + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error." >&2 + return 1 + fi # Make note of the mtime for use in write_ssh_agents. - SSH_AGENTS_MTIME="$(stat --format=%.9Y ~/.ssh/agents)" - elif [[ "$(uname -s)" == "Darwin" ]]; then + SSH_AGENTS_MTIME="$(stat --format=%.9Y "$HOME/.ssh/agents")" + elif [[ "$PLATFORM" == "Darwin" ]]; then # Do locking the sucky way on macOS. - for ((I = 0; I < 6; I++)); do - if shlock -p "$$" -f ~/.ssh/agents.lock; then - exec {FD}<~/.ssh/agents + for ((I = 0; I <= 5; I++)); do + if shlock -p "$$" -f "$HOME/.ssh/agents.lock"; then + exec {FD}<"$HOME/.ssh/agents" # Make note of the mtime for use in write_ssh_agents. - SSH_AGENTS_MTIME="$(stat -f %Fm ~/.ssh/agents)" + SSH_AGENTS_MTIME="$(stat -f %Fm "$HOME/.ssh/agents")" ERR=0 break else @@ -52,33 +60,41 @@ __read_ssh_agents() { sleep 0.1 fi done - (( ERR != 0 )) && { printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2; return 1; } + (( ERR != 0 )) && { + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + return 1 + } else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on this platform." >&2 + printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on '$PLATFORM'." >&2 return 1 fi - # Read the socket list (bash v3+ compliant) + # Read the socket list (bash v2+ compliant) while read -u "$FD" -r SOCK; do [[ -n "$SOCK" ]] && SSH_AUTH_SOCKS+=("$SOCK") done ERR=$? - # Close the file descriptor (which on Linux releases the lock too). + # Close the file descriptor (which on Linux releases the flock too). exec {FD}<&- # On Darwin, release the lock on the file. - rm -f ~/.ssh/agents.lock + rm -f "$HOME/.ssh/agents.lock" # Remove the . in the mtime. SSH_AGENTS_MTIME="${SSH_AGENTS_MTIME/\.}" # Error out if the data couldn't be read. - (( ERR != 0 )) && { printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to read ssh-agent socket list." >&2; unset SSH_AUTH_SOCKS; return 1; } + (( ERR != 0 )) && { + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to read ssh-agent socket list." >&2 + unset SSH_AUTH_SOCKS SSH_AGENTS_MTIME + return 1 + } return 0 } __write_ssh_agents() { + # Write all unique ssh agent sockets into the ~/.ssh/agents file. # Returns: 0 = Processed and wrote the agents file without issue. # 1 = Error processing/writing the agents file. # 2 = The SSH_AUTH_SOCKS array may be out of date as the agents file'a mtime has changed. @@ -89,40 +105,43 @@ __write_ssh_agents() { # Remove any duplicates from SSH_AUTH_SOCKS. for ((I = 0; I < ${#SSH_AUTH_SOCKS[@]}; I++)); do + [[ -z "${SSH_AUTH_SOCKS[$I]}" ]] && continue for ((J = 0; J < ${#SOCKS[@]}; J++)); do [[ "${SSH_AUTH_SOCKS[$I]}" == "${SOCKS[$J]}" ]] && continue 2 done # Only add the socket if it's still viable. - [[ -n "${SSH_AUTH_SOCKS[$I]}" ]] && { - SSH_AUTH_SOCK="${SSH_AUTH_SOCKS[$I]}" ssh-add -l >/dev/null 2>&1 - (( $? < 2 )) && SOCKS+=("${SSH_AUTH_SOCKS[$I]}") - } + SSH_AUTH_SOCK="${SSH_AUTH_SOCKS[$I]}" ssh-add -l >/dev/null 2>&1 + (( $? <= 1 )) && SOCKS+=("${SSH_AUTH_SOCKS[$I]}") done + # Determine the platform being logged into. + PLATFORM="$(uname -s)" + # Lock the ~/.ssh/agents file. - if [[ "$(uname -s)" == "Linux" ]]; then + if [[ "$PLATFORM" == "Linux" ]]; then # Make sure SSH_AUTH_SOCKS has the most up to date data. - MTIME="$(stat --format=%.9Y ~/.ssh/agents)" + MTIME="$(stat --format=%.9Y "$HOME/.ssh/agents")" (( ${MTIME/\.} > SSH_AGENTS_MTIME )) && return 2 # Lock the agents file. - { exec {FD}>~/.ssh/agents && flock -E 10 -e -w 0.5 "$FD"; } || { - if (( $? == 10 )); then - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 - else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error" >&2 - fi + exec {FD}<"$HOME/.ssh/agents" && flock -E 10 -e -w 0.5 "$FD" + ERR=$? + if (( ERR == 10 )); then + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 return 1 - } + elif (( ERR > 0 )); then + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Flock usage error." >&2 + return 1 + fi elif [[ "$(uname -s)" == "Darwin" ]]; then # Make sure SSH_AUTH_SOCKS has the most up to date data. - MTIME="$(stat --format=%.9Y ~/.ssh/agents)" + MTIME="$(stat --format=%.9Y "$HOME/.ssh/agents")" (( ${MTIME/\.} > SSH_AGENTS_MTIME )) && return 2 # Do locking the sucky way on OSX. - for ((I = 0; I < 6; I++)); do - if shlock -p "$$" -f ~/.ssh/agents.lock; then - exec {FD}>~/.ssh/agents + for ((I = 0; I <= 5; I++)); do + if shlock -p "$$" -f "$HOME/.ssh/agents.lock"; then + exec {FD}<"$HOME/.ssh/agents" ERR=0 break else @@ -130,38 +149,48 @@ __write_ssh_agents() { sleep 0.1 fi done - (( ERR != 0 )) && { printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2; return 1; } + (( ERR != 0 )) && { + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to obtain lock on ~/.ssh/agents." >&2 + return 1 + } else - printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on this platform." >&2 + printf "\\033[1;31;40m%s\\033[0;39m\\n" "File locking unsupported on '$PLATFORM'." >&2 return 1 fi # Write the cleaned array to disk. - printf "%s\\n" "${SOCKS[@]}" >~/.ssh/agents 2>/dev/null - ERR=$? + ERR=-1 + [[ -n "${SOCKS[*]}" ]] && { printf "%s\\n" "${SOCKS[@]}" >"$HOME/.ssh/agents" 2>/dev/null; ERR=$?; } # Release locks. exec {FD}>&- - rm -f ~/.ssh/agents.lock + rm -f "$HOME/.ssh/agents.lock" # Error out if the data couldn't be written. - (( ERR != 0 )) && { printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to write ssh-agent socket list." >&2; return 1; } + if (( ERR == -1 )); then + rm -f "$HOME/.ssh/agents" 2>/dev/null + elif (( ERR >= 1 )); then + rm -f "$HOME/.ssh/agents" 2>/dev/null + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to write ssh-agent socket list." >&2 + return 1 + fi return 0 } __ssh_agent_prompt_command() { + # If necessary, find and activate a new ssh agent socket before each prompt is displayed. # Returns: 0 = All is good. - # 1 = And error occured. - local ERR SSH_AUTH_SOCKS=() + # 1 = An error occured. + local ERR if [[ -z "$SSH_AUTH_SOCK" ]]; then ERR=2 else - SSH_AUTH_SOCK="$SSH_AUTH_SOCK" ssh-add -l >/dev/null 2>&1 + ssh-add -l >/dev/null 2>&1 ERR=$? fi (( ERR == 2 )) && { - # Read previous sockets from ~/.ssh/agents. + # Read alternative sockets from ~/.ssh/agents. __read_ssh_agents || { unset SSH_AUTH_SOCK return 1 @@ -173,25 +202,22 @@ __ssh_agent_prompt_command() { else # Start a new agent. eval "$(ssh-agent -s 2>/dev/null | grep -v 'echo'; printf "%s" "ERR=${PIPESTATUS[0]}")" - (( ERR > 0 )) && { printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to start ssh-agent - continuing with no agent." >&2; return 1; } + (( ERR > 0 )) && { + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Failed to start new ssh-agent - continuing with no agent." + return 1 + } printf "\\033[1;32;40m%s\\033[0;39m\\n" "Started new ssh-agent." fi - - # Update the agents file. - while :; do - __write_ssh_agents - ERR=$? - (( ERR == 2 )) && { __read_ssh_agents || return 1; } && continue - (( ERR == 1 )) && return 1 - break - done } return 0 } imagebin() { - [[ -z "$1" ]] && { printf "%s: %s\\n" "Usage" "${FUNCNAME[0]} " >&2; return 1; } - curl -F file="@${1:-}" https://imagebin.ca/upload.php | grep ^url: | cut -d: -f2- + [[ -z "$1" ]] && { + printf "%s: %s\\n" "Usage" "${FUNCNAME[0]} " >&2 + return 1 + } + curl -F file="@$1" https://imagebin.ca/upload.php | grep '^url:' | cut -d: -f2- } # Make bash a little more pleasent - these are valid for all versions. @@ -203,17 +229,19 @@ IGNOREEOF=0 # History control. HISTCONTROL="ignoredups" HISTFILE="$HOME/.bash_history-${HOSTNAME%%.*}" -HISTFILESIZE=100000 +HISTFILESIZE=1000000 HISTIGNORE="bg:bg *:fg:fg *:jobs:exit:clear:history" HISTSIZE=-1 HISTTIMEFORMAT="%d/%m/%y %H:%M:%S " history -r # Determine the colour of the username in the prompt. -if (( $(id -u) == 0 )); then +if [[ "$LOGNAME" == "root" ]]; then COLOUR=1 # Red -else +elif [[ "$LOGNAME" == "tadgy" ]]; then COLOUR=2 # Green +else + COLOUR=5 # Purple fi # Version specific set up. @@ -221,18 +249,20 @@ if (( BASH_VERSINFO[0] >= 4 )); then # Add to the shopts. shopt -s checkjobs dirspell - # Set the prompts. + # Trim the path in the prompt. PROMPT_DIRTRIM=2 # Coloured username + host + directory: - PS1="[\[$(tput bold)$(tput setaf $COLOUR)\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\w\[$(tput sgr0)\] ->" + PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\w\[$(tput sgr0)\] ->" else # Set the prompts. # Coloured username + host + directory: - PS1="[\[$(tput bold)$(tput setaf $COLOUR)\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}')\[$(tput sgr0)\] ->" + # shellcheck disable=SC2154 + PS1="[\[$(tput bold)$(tput setaf "$COLOUR")\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \[$(tput bold)$(tput setaf 4)\]\$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}')\[$(tput sgr0)\] ->" fi unset COLOUR # Set the debugger prompt. +# shellcheck disable=SC2155 export PS4="+(\[\e[1;33;40m\]\$?\[$(tput sgr0)\]) \[$(tput bold)$(tput setaf 4)\]\${BASH_SOURCE##*/}\[$(tput sgr0)\]\${FUNCNAME[0]:+(\[$(tput bold)$(tput setaf 2)\]\${FUNCNAME[0]}\[$(tput sgr0)\])}:\[$(tput bold)$(tput setaf 1)\]\$LINENO\[$(tput sgr0)\]: " # The commands to execute before the prompt is displayed. @@ -241,11 +271,15 @@ PROMPT_COMMAND="__ssh_agent_prompt_command" # Common aliases. hash bc >/dev/null 2>&1 && alias bc='bc -lq' hash diff >/dev/null 2>&1 && alias diff='diff --color=auto -u' -hash grep >/dev/null 2>&1 && alias egrep='grep -E --color=auto' -hash grep >/dev/null 2>&1 && alias fgrep='grep -F --color=auto' -hash grep >/dev/null 2>&1 && alias grep='grep --color=auto' +hash gpg2 >/dev/null 2>&1 && alias gpg='gpg2' +hash grep >/dev/null 2>&1 && { + alias egrep='grep -E --color=auto' + alias fgrep='grep -F --color=auto' + alias grep='grep --color=auto' +} hash nc >/dev/null 2>&1 && alias pastebin='nc termbin.com 9999' -hash screen >/dev/null 2>&1 && alias screen='screen -Ua' + +# Auto start the ssh agent and add keys for scp/sftp/ssh. hash scp ssh ssh-add >/dev/null 2>&1 && alias scp='_EXEC=scp ssh' hash sftp ssh ssh-add >/dev/null 2>&1 && alias sftp='_EXEC=sftp ssh' hash ssh ssh-add >/dev/null 2>&1 && ssh() { @@ -254,13 +288,16 @@ hash ssh ssh-add >/dev/null 2>&1 && ssh() { if [[ -z "$SSH_AUTH_SOCK" ]]; then ERR=2 else - SSH_AUTH_SOCK="$SSH_AUTH_SOCK" ssh-add -l >/dev/null 2>&1 + ssh-add -l >/dev/null 2>&1 ERR=$? fi - (( ERR == 2 )) && __ssh_agent_prompt_command && ssh-add - (( ERR == 1 )) && ssh-add + if (( ERR == 1 )); then + ssh-add + elif (( ERR == 2 )); then + __ssh_agent_prompt_command + ssh-add + fi command "${_EXEC:-${FUNCNAME[0]}}" "$@" - unset _EXEC } # Determine the platform being logged into. @@ -269,9 +306,24 @@ PLATFORM="$(uname -s)" # Platform specific set up. if [[ "$PLATFORM" = "Linux" ]]; then # Linux specific functions. - psgrep() { - [[ -z "$1" ]] && { printf "%s: %s\\n" "Usage" "${FUNCNAME[0]} " >&2; return 1; } - ps -auwwx | command grep -E --color=always -- "(.*RSS.*|$1)" | grep -F -v "(.*RSS.*|" + hash nano >/dev/null 2>&1 && nano() { + # shellcheck disable=SC2155 + local NANO_VER="$(command nano --version | awk '/version/ { print $4 }' | cut -d. -f1)" + if [[ -f "$HOME/.nanorc-$NANO_VER" ]]; then + command nano -f "$HOME/.nanorc-$NANO_VER" "$@" + else + printf "%s: %s\\n" "${FUNCNAME[0]}" "no .nanorc for version '$NANO_VER'" >&2 + return 1 + fi + } + hash ps grep >/dev/null 2>&1 && psgrep() { + if [[ -n "$1" ]]; then + # shellcheck disable=SC2009 + ps -auwwx | grep -E --color=always -- "(.*RSS.*|$1)" | grep -F -v '(.*RSS.*|' + else + printf "%s: %s\\n" "Usage" "${FUNCNAME[0]} " >&2 + return 1 + fi } # Linux specific aliases. @@ -287,6 +339,16 @@ if [[ "$PLATFORM" = "Linux" ]]; then hash pinfo >/dev/null 2>&1 && alias info='pinfo' hash ping >/dev/null 2>&1 && alias ping='ping -b' elif [[ "$PLATFORM" = "Darwin" ]]; then + # Create .nanorc symlink if necessary. + [[ ! -e "$HOME/.nanorc" ]] && { + NANO_VER="$(command nano --version | awk '/version/ { print $4 }' | cut -d. -f1)" + if [[ -f "$HOME/.nanorc-$NANO_VER" ]]; then + ( cd "$HOME" && ln -sf ".nanorc-$NANO_VER" .nanorc ) || printf "%s: %s\\n" "${BASH_SOURCE##*/}" "failed to create .nanorc symlink" >&2 + else + printf "%s: %s\\n" "${BASH_SOURCE##*/}" "no .nanorc for version '$NANO_VER'" >&2 + fi + } + # Darwin specific aliases. hash df >/dev/null 2>&1 && alias df='df -P' hash ls >/dev/null 2>&1 && alias ls='ls -bFG'