# Functions. _agent_prompt_command() { hash ssh-add >/dev/null 2>&1 && { local OUTPUT SOCK _PLATFORM="$(uname -s)" declare -g _SSH_PROMPT_ERROR_ISSUED # Check the ssh agent socket is still alive. # Need to work around an ssh-add bug here: ssh-add doesn't always return 2 on failure # to contact the agent, so also check if any errors were produced. OUTPUT="$(ssh-add -l 2>&1 >/dev/null)" if ((${PIPESTATUS[0]} >= 2)) || [[ ! -z "$OUTPUT" ]]; then # Auth socket has become unusable, search for a new one. SOCK="$(_find_agent_sock)" if ((${PIPESTATUS[0]} == 0)); then export SSH_AUTH_SOCK="$SOCK" _lock_agents_file && { if _push_agent_sock; then echo "Connected to alternate ssh-agent - you may need to re-add keys." _SSH_AGENT_REG_FAILED=0 else echo "$(tput setaf 3)Connected to, but failed to register, alternate ssh-agent - you may need to re-add keys.$(tput op)" _SSH_AGENT_REG_FAILED=1 fi } _unlock_agents_file _SSH_PROMPT_ERROR_ISSUED=0 else # Failed to find a new socket. if [[ -z "$(egrep -v '^$' ~/.ssh/agents)" ]]; then ((${_SSH_PROMPT_ERROR_ISSUED:-0} == 0)) && { echo "$(tput setaf 1)Lost connection to ssh-agent - no alternate available!$(tput op)" unset SSH_AUTH_SOCK _SSH_PROMPT_ERROR_ISSUED=1 } else ((${_SSH_PROMPT_ERROR_ISSUED:-0} == 0)) && { echo "$(tput setaf 1)Lost connection to ssh-agent - failed to connect to new agent!$(tput op)" unset SSH_AUTH_SOCK _SSH_PROMPT_ERROR_ISSUED=1 } fi fi else _SSH_PROMPT_ERROR_ISSUED=0 fi } } _clean_agent_socks() { local I OUTPUT SSH_AUTH_SOCK # Go through the array of sockets and validate each one. for ((I = 0; I < ${#SOCKS[@]}; I++)); do OUTPUT="$(SSH_AUTH_SOCK="${SOCKS[$I]}" ssh-add -l 2>&1 >/dev/null)" ((${PIPESTATUS[0]} >= 2)) || [[ ! -z "$OUTPUT" ]] && { unset SOCKS[$I] } done } _find_agent_sock() { local I IFS=$'\n' OUTPUT REPLY SOCKS=() SSH_AUTH_SOCK="$SSH_AUTH_SOCK" # Load the array of sockets, minus the one we know is unusable. if ((${BASH_VERSINFO[0]} >= 4)); then mapfile -t SOCKS < <(egrep -v "^$SSH_AUTH_SOCK\$" ~/.ssh/agents 2>/dev/null) else while read -r; do SOCKS+=("$REPLY") done < <(egrep -v "^$SSH_AUTH_SOCK\$" ~/.ssh/agents 2>/dev/null) fi # Search backwards through the list to find an active socket. for ((I = (${#SOCKS[@]} - 1); I >= 0; I--)); do OUTPUT="$(SSH_AUTH_SOCK="${SOCKS[$I]}" ssh-add -l 2>&1 >/dev/null)" { ((${PIPESTATUS[0]} <= 1)) && [[ -z "$OUTPUT" ]]; } && [[ ! -z "${SOCKS[$I]}" ]] && { printf "%s" "${SOCKS[$I]}" return 0 } done return 1 } _lock_agents_file() { local I # Lock the ~/.ssh/agents file. if [[ "$_PLATFORM" == "Linux" ]]; then # Linux has 'flock', thankfully. if exec 9>~/.ssh/agents.lock && flock -E 10 -w 0.5 9; then return 0 else echo "$(tput setaf 1)Failed to obtain lockfile!$(tput op)" fi elif [[ "$_PLATFORM" == "Darwin" ]]; then # Do locking the sucky way on OSX. for ((I=0; I < 5; I++)); do if shlock -f "/tmp/agents.lock.$$" -p "$$"; then return 0 else sleep 0.1 fi done [[ "${_GOT_LOCK:-0}" == "0" ]] && echo "$(tput setaf 1)Failed to obtain lockfile!$(tput op)" else echo "$(tput setaf 1)File locking unsupported - skipping update of agents file!$(tput op)" fi return 1 } _pop_agent_sock() { local I IFS=$'\n' REPLY SOCKS=() # Read the current list of auth sockets. if ((${BASH_VERSINFO[0]} >= 4)); then mapfile -t SOCKS <~/.ssh/agents else while read -r; do SOCKS+=("$REPLY") done <~/.ssh/agents fi # Remove the last instance of the socket in $SSH_AUTH_SOCK. for ((I = (${#SOCKS[@]} - 1); I >= 0; I--)); do [[ "${SOCKS[$I]}" == "$SSH_AUTH_SOCK" ]] && { unset SOCKS[$I] break } done # Clean up any dead sockets - this modifies the SOCKS array. _clean_agent_socks # Write the new list back to disk. printf "%s\n" "${SOCKS[@]}" >~/.ssh/agents return $? } _push_agent_sock() { local IFS=$'\n' REPLY SOCKS=() # Read the current list of auth sockets. if ((${BASH_VERSINFO[0]} >= 4)); then mapfile -t SOCKS <~/.ssh/agents else while read -r; do SOCKS+=("$REPLY") done <~/.ssh/agents fi # Clean up any dead sockets - this modifies the SOCKS array. _clean_agent_socks # Write the new list back to disk. printf "%s\n" "${SOCKS[@]}" "$SSH_AUTH_SOCK" >~/.ssh/agents return $? } _unlock_agents_file() { # Unlock the ~/.ssh/agents file. if [[ "$_PLATFORM" == "Linux" ]]; then exec 9>&- elif [[ "$_PLATFORM" == "Darwin" ]]; then rm -f "/tmp/agents.lock.$$" fi } screen_attach() { local TTY SCREENS # Only continue if it's not a remote connection, and not inside screen already. [[ -z "$SSH_TTY" ]] && [[ -z "$STY" ]] && { TTY="$(tty | cut -d/ -f3-)" SCREENS="$(screen -list | fgrep -v "Dead" | fgrep "$HOSTNAME" | fgrep "${TTY//\//-}")" case "$(echo "${SCREENS:--n}" | wc -l)" in 0) # No screens found - start a new instance. screen ;; 1) # Just one screen - reconnect. screen -dr "${TTY//\//-}.$HOSTNAME" ;; *) # Multiple screens - output a list echo "Multiple screens found for $TTY:" echo "$SCREENS" | sed -e 's/^/ /g' ;; esac } } # Make bash a little more pleasent - these are valid for all versions. shopt -s cdspell checkhash checkwinsize cmdhist histappend no_empty_cmd_completion # Exit the shell on a Ctl+D. IGNOREEOF=0 # History control. HISTCONTROL="ignoredups" HISTFILE="$HOME/.bash_history-$(hostname --short)" HISTFILESIZE=100000 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 _COLOUR=1 # Red else _COLOUR=2 # Green fi # Version specific set up. if (( ${BASH_VERSINFO[0]} >= 4 )); then # Add to the shopts. shopt -s checkjobs dirspell # Set the prompts. PROMPT_DIRTRIM=2 # No colour: # PS1="[\u@\h] \w ->" # Coloured username: # PS1="[\[$(tput bold)$(tput setaf $_COLOUR)\]\u\[$(tput sgr0)\]@\h] \w ->" # Coloured username + host: # PS1="[\[$(tput bold)$(tput setaf $_COLOUR)\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \w ->" # 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)\] ->" else # Set the prompts. # No colour: # PS1="[\u@\h] \$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}') ->" # Coloured username: # PS1="[\[$(tput bold)$(tput setaf $_COLOUR)\]\u\[$(tput sgr0)\]@\h] \$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}') ->" # Coloured username + host: # PS1="[\[$(tput bold)$(tput setaf $_COLOUR)\]\u\[$(tput sgr0)\]@\[$(tput bold)$(tput setaf 3)\]\h\[$(tput sgr0)\]] \$(echo \"\${PWD/#\$HOME/~}\" | awk -F/ '{if (NF>3) {printf \".../\" \$(NF-1) \"/\" \$NF} else {printf \$0}}') ->" # 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)\] ->" fi unset _COLOUR # Set the debugger prompt. # PS4="+(\\\$? = \$?) \${BASH_SOURCE##*/}\${FUNCNAME:+(\$FUNCNAME)}:\$LINENO: " # PS4="+(\[\e[33m\]\\\$? = \$?\[$(tput sgr0)\]) \[$(tput bold)$(tput setaf 4)\]\${BASH_SOURCE##*/}\[$(tput sgr0)\]\${FUNCNAME:+(\[$(tput bold)$(tput setaf 2)\]\$FUNCNAME\[$(tput sgr0)\])}:\[$(tput bold)$(tput setaf 1)\]\$LINENO\[$(tput sgr0)\]: " export PS4="+(\[\e[33m\]\$?\[$(tput sgr0)\]) \[$(tput bold)$(tput setaf 4)\]\${BASH_SOURCE##*/}\[$(tput sgr0)\]\${FUNCNAME:+(\[$(tput bold)$(tput setaf 2)\]\$FUNCNAME\[$(tput sgr0)\])}:\[$(tput bold)$(tput setaf 1)\]\$LINENO\[$(tput sgr0)\]: " # The commands to execute before the prompt is displayed. PROMPT_COMMAND="_agent_prompt_command" # Platform specific set up. _PLATFORM="$(uname -s)" if [[ "$_PLATFORM" = "Linux" ]]; then # Linux specific aliases. hash ftpwho >/dev/null 2>&1 && alias ftpwho='ftpwho -v' hash iftop >/dev/null 2>&1 && alias iftop='TERM=vt100 iftop' hash last less >/dev/null 2>&1 && alias laston='last -a | less' hash ls >/dev/null 2>&1 && alias ls='ls -bFv --color=auto' hash minicom >/dev/null 2>&1 && alias minicom='minicom -m -c on' hash mkpasswd >/dev/null 2>&1 && alias pwgen='mkpasswd -m sha512crypt' hash pine >/dev/null 2>&1 && alias pine='pine -p "{mail.opensourcerers.net/Service=IMAP/User=darren@afterdark.org.uk/TLS/NoValidate-Cert/NoRsh}.pinerc"' hash pinfo >/dev/null 2>&1 && alias info='pinfo' hash ping >/dev/null 2>&1 && alias ping='ping -b' # Linux specific functions. psgrep() { ps -auwwx | egrep "(RSS|$1)" | fgrep -v "(RSS|" } elif [[ "$_PLATFORM" = "Darwin" ]]; then # Darwin specific aliases. hash df >/dev/null 2>&1 && alias df='df -P' hash ls >/dev/null 2>&1 && alias ls='ls -bFG' hash top >/dev/null 2>&1 && alias top='top -o cpu -S' else echo "${BASH_SOURCE##*/}: unsupported platform: $_PLATFORM" >&2 fi unset _PLATFORM # Common aliases. hash bc >/dev/null 2>&1 && alias bc='bc -lq' #hash curl >/dev/null 2>&1 && alias pastebin="curl -F 'sprunge=<-' http://sprunge.us" hash curl >/dev/null 2>&1 && imagebin() { curl -F file="@${1:-}" https://imagebin.ca/upload.php | grep ^url: | cut -d: -f2-; } hash diff >/dev/null 2>&1 && alias diff='diff -u' hash egrep >/dev/null 2>&1 && alias egrep='egrep --color=auto' hash fgrep >/dev/null 2>&1 && alias fgrep='fgrep --color=auto' hash grep >/dev/null 2>&1 && 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'