penguin/utils

my env utils

bash/sources/42_clipboard_server

raw ยท 5239 bytes

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}"

# These paths are created on the remote host.
SSH_CLIPBOARD_REMOTE_COPY_SOCKET="/tmp/ssh-clipboard-copy-client.sock"
SSH_CLIPBOARD_REMOTE_PASTE_SOCKET="/tmp/ssh-clipboard-paste-client.sock"


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 "-- starting clipboard ssh tunnel"

		command ssh \
			-M \
			-S "$control_socket" \
			-o ControlPersist=10 \
			-fN \
			-R "$SSH_CLIPBOARD_REMOTE_COPY_SOCKET:$SSH_CLIPBOARD_COPY_SOCKET" \
			-R "$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET:$SSH_CLIPBOARD_PASTE_SOCKET" \
			"$@" || return
	fi

	echo "-- ssh with clipboard server"

	command ssh \
		-S "$control_socket" \
		"$@"
}

function pbcopy {
	if [ -n "${SSH_CLIPBOARD_REMOTE_COPY_SOCKET:-}" ] &&
		[ -S "$SSH_CLIPBOARD_REMOTE_COPY_SOCKET" ]; then

		command -v socat >/dev/null 2>&1 || {
			echo "pbcopy: socat is required for the remote clipboard" >&2
			return 1
		}

		socat - "UNIX-CONNECT:$SSH_CLIPBOARD_REMOTE_COPY_SOCKET"
		return $?
	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 {
	if [ -n "${SSH_CLIPBOARD_REMOTE_PASTE_SOCKET:-}" ] &&
		[ -S "$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET" ]; then

		command -v socat >/dev/null 2>&1 || {
			echo "pbpaste: socat is required for the remote clipboard" >&2
			return 1
		}

		socat - "UNIX-CONNECT:$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET"
		return $?
	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
}


function cleanup_ssh_clipboard {
	[ -n "${SSH_CONNECTION:-}" ] || return

	[ -S "$SSH_CLIPBOARD_REMOTE_COPY_SOCKET" ] &&
		rm -f -- "$SSH_CLIPBOARD_REMOTE_COPY_SOCKET"

	[ -S "$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET" ] &&
		rm -f -- "$SSH_CLIPBOARD_REMOTE_PASTE_SOCKET"
}


if [ -z "${SSH_CONNECTION:-}" ] && init_clipboard_server; then
	alias ssh='sshc'
fi

if [ -n "${SSH_CONNECTION:-}" ]; then
	trap cleanup_ssh_clipboard EXIT HUP TERM
fi