SSH_CLIPBOARD_DIR="${SSH_CLIPBOARD_DIR:-$HOME/.cache}" SSH_CLIPBOARD_COPY_SOCKET="${SSH_CLIPBOARD_COPY_SOCKET:-$SSH_CLIPBOARD_DIR/ssh-clipboard-copy.sock}" SSH_CLIPBOARD_PASTE_SOCKET="${SSH_CLIPBOARD_PASTE_SOCKET:-$SSH_CLIPBOARD_DIR/ssh-clipboard-paste.sock}" SSH_CLIPBOARD_PIDFILE="${SSH_CLIPBOARD_PIDFILE:-$SSH_CLIPBOARD_DIR/ssh-clipboard.pid}" SSH_CLIPBOARD_PASTE_PIDFILE="${SSH_CLIPBOARD_PASTE_PIDFILE:-$SSH_CLIPBOARD_DIR/ssh-clipboard-paste.pid}" SSH_CLIPBOARD_LOG="${SSH_CLIPBOARD_LOG:-$SSH_CLIPBOARD_DIR/ssh-clipboard.log}" SSH_CLIPBOARD_CLIENT_ID="$( printf '%s' "$HOSTNAME" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_.-' '_' )" SSH_CLIPBOARD_REMOTE_COPY_SOCKET="/tmp/ssh-clipboard-copy-${SSH_CLIPBOARD_CLIENT_ID}.sock" SSH_CLIPBOARD_REMOTE_PASTE_SOCKET="/tmp/ssh-clipboard-paste-${SSH_CLIPBOARD_CLIENT_ID}.sock" function _ssh_clipboard_find_remote_socket { local type="$1" local socket found="" for socket in /tmp/ssh-clipboard-"$type"-*.sock; do [ -S "$socket" ] || continue # More than one clipboard client available. if [ -n "$found" ]; then return 1 fi found="$socket" done [ -n "$found" ] || return 1 printf '%s\n' "$found" } function clipboard_server_running { local pid if [ -n "${SSH_CLIPBOARD_REMOTE_COPY_SOCKET:-}" ] && [ -S "$SSH_CLIPBOARD_REMOTE_COPY_SOCKET" ]; then return 0 fi [[ -f "$SSH_CLIPBOARD_PIDFILE" ]] || return 1 read -r pid < "$SSH_CLIPBOARD_PIDFILE" [[ "$pid" =~ ^[0-9]+$ ]] || return 1 kill -0 "$pid" 2>/dev/null } function init_clipboard_server { if clipboard_server_running; then return 0 fi command -v socat >/dev/null 2>&1 || { echo "clipboard server: socat is not installed" >&2 return 1 } case "$OSTYPE" in darwin*) [[ -x /usr/bin/pbcopy ]] || { echo "clipboard server: /usr/bin/pbcopy was not found" >&2 return 1 } [[ -x /usr/bin/pbpaste ]] || { echo "clipboard server: /usr/bin/pbpaste was not found" >&2 return 1 } mkdir -p "$SSH_CLIPBOARD_DIR" chmod 700 "$SSH_CLIPBOARD_DIR" rm -f \ "$SSH_CLIPBOARD_COPY_SOCKET" \ "$SSH_CLIPBOARD_PASTE_SOCKET" \ "$SSH_CLIPBOARD_PIDFILE" \ "$SSH_CLIPBOARD_PASTE_PIDFILE" ( umask 077 # Remote -> local clipboard socat \ "UNIX-LISTEN:$SSH_CLIPBOARD_COPY_SOCKET,fork,mode=600" \ EXEC:/usr/bin/pbcopy \ >>"$SSH_CLIPBOARD_LOG" 2>&1 & echo "$!" > "$SSH_CLIPBOARD_PIDFILE" # Local clipboard -> remote socat \ "UNIX-LISTEN:$SSH_CLIPBOARD_PASTE_SOCKET,fork,mode=600" \ EXEC:/usr/bin/pbpaste \ >>"$SSH_CLIPBOARD_LOG" 2>&1 & echo "$!" > "$SSH_CLIPBOARD_PASTE_PIDFILE" ) ;; cygwin*) # Eventually: # copy -> cat > /dev/clipboard # paste -> cat /dev/clipboard echo "clipboard server: Cygwin support is not implemented" >&2 return 1 ;; *) echo "clipboard server: $OSTYPE is not supported" >&2 return 1 ;; esac } function stop_clipboard_server { local pid if [[ -f "$SSH_CLIPBOARD_PIDFILE" ]]; then read -r pid < "$SSH_CLIPBOARD_PIDFILE" [[ "$pid" =~ ^[0-9]+$ ]] && kill "$pid" 2>/dev/null || true fi if [[ -f "$SSH_CLIPBOARD_PASTE_PIDFILE" ]]; then read -r pid < "$SSH_CLIPBOARD_PASTE_PIDFILE" [[ "$pid" =~ ^[0-9]+$ ]] && kill "$pid" 2>/dev/null || true fi rm -f \ "$SSH_CLIPBOARD_COPY_SOCKET" \ "$SSH_CLIPBOARD_PASTE_SOCKET" \ "$SSH_CLIPBOARD_PIDFILE" \ "$SSH_CLIPBOARD_PASTE_PIDFILE" } function sshc { init_clipboard_server || return local control_socket="/tmp/sshc-%C.sock" if ! command ssh \ -S "$control_socket" \ -O check \ "$@" \ >/dev/null 2>&1 then echo "-- cleaning stale clipboard sockets" command ssh "$@" \ "rm -f \ '$SSH_CLIPBOARD_REMOTE_COPY_SOCKET' \ '$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET'" \ /dev/null 2>&1 || { echo "pbcopy: socat is required for the remote clipboard" >&2 return 1 } socat - "UNIX-CONNECT:$remote_socket" return $? fi fi case "$OSTYPE" in darwin*) if [ -x /usr/bin/pbcopy ]; then /usr/bin/pbcopy else echo "pbcopy: /usr/bin/pbcopy was not found" >&2 return 1 fi ;; cygwin*) cat > /dev/clipboard ;; *) echo "pbcopy: no remote clipboard socket is available" >&2 return 1 ;; esac } function pbpaste { local remote_socket if [ -n "${SSH_CONNECTION:-}" ]; then remote_socket="$(_ssh_clipboard_find_remote_socket paste)" if [ -n "$remote_socket" ]; then command -v socat >/dev/null 2>&1 || { echo "pbpaste: socat is required for the remote clipboard" >&2 return 1 } socat -t0 - "UNIX-CONNECT:$remote_socket" return $? fi fi case "$OSTYPE" in darwin*) if [ -x /usr/bin/pbpaste ]; then /usr/bin/pbpaste else echo "pbpaste: /usr/bin/pbpaste was not found" >&2 return 1 fi ;; cygwin*) cat /dev/clipboard ;; *) echo "pbpaste: no remote clipboard socket is available" >&2 return 1 ;; esac } if [ -z "${SSH_CONNECTION:-}" ] && init_clipboard_server; then alias ssh='sshc' fi