Using netcat and ssh tunnels, create a shared clipboard allowing client and server clipboards to be fused
NOTE: That we are making the assumption that the clipboards are sending and receiving UTF-8 encoded bytes
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>localhost.pbcopy</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>LC_CTYPE=en_US.UTF-8 pbcopy</string>
</array>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockNodeName</key>
<string>127.0.0.1</string>
<key>SockServiceName</key>
<string>2224</string>
</dict>
</dict>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>localhost.pbpaste</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>LC_CTYPE=en_US.UTF-8 pbpaste</string>
</array>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockNodeName</key>
<string>127.0.0.1</string>
<key>SockServiceName</key>
<string>2225</string>
</dict>
</dict>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
</dict>
</plist>
After adding the plists above, load and start the launch agents:
launchctl load ~/Library/LaunchAgents/pbcopy.plist
launchctl load ~/Library/LaunchAgents/pbpaste.plist
launchctl start ~/Library/LaunchAgents/pbcopy.plist
launchctl start ~/Library/LaunchAgents/pbpaste.plist
IMPORTANT: use localhost
not 127.0.0.1
in RemoteForward directives
Host *
RemoteForward 2224 localhost:2224
RemoteForward 2225 localhost:2225
or
ssh -R 2225:localhost:2225 <my_linux_host>
ssh -R 2224:localhost:2224 <my_linux_host>
Log into Linux host
On Mac: Copy some text with ⌘-c
On Linux: Print the Mac's remote clipboard
nc localhost 2225
On Linux: Write to Mac's remote clipboard
echo "testing" | nc localhost 2224
On Mac: Paste the text with ⌘-v
(defconst *is-a-linux* (eq system-type 'gnu/linux))
(defconst *is-in-ssh* (or (getenv "SSH_CLIENT") (getenv "SSH_TTY") (getenv "SSH_CONNECTION")))
(defconst *is-in-terminal* (not (display-graphic-p))))
(when (and *is-a-linux* *is-in-terminal* *is-in-ssh*)
(progn
(defun is-port-listening (host port)
"Tests if a tcp port is open"
(interactive)
(string-match-p "succeeded!"
(shell-command-to-string (format "nc -zv %s %d" host port))))
(defun copy-from-remote ()
"Use netcat clipboard to paste. PULL"
(shell-command-to-string "nc localhost 2225"))
(defun paste-to-remote (text &optional push)
"Add kill ring entries (TEXT) to netcat clipboard. PUSH."
(let ((process-connection-type nil))
(let ((proc (start-process "remote-clipboard-copy" "*Messages*" "nc" "localhost" "2224")))
(process-send-string proc text)
(process-send-eof proc))))
;; if the reverse forwarding is done we'll use the ports to hook up interprogram cut/paste
(if (is-port-listening "localhost" 2225)
(progn
(message "remote clipboard listening")
(setq interprogram-cut-function 'paste-to-remote)
(setq interprogram-paste-function 'copy-from-remote))
(message "remote clipboard is not listening. skipping..."))))
(defconst *is-a-mac* (eq system-type 'darwin))
(defconst *is-in-terminal* (not (display-graphic-p))))
(defconst *is-in-tmux* (getenv "TMUX"))
(when (and *is-a-mac* *is-in-terminal*)
(if *is-in-tmux*
(progn
"When we are running under macos in tmux.
Be sure to: brew install reattach-to-user-namespace"
(defun copy-from-osx ()
"Use OSX clipboard to paste. PULL"
(shell-command-to-string "reattach-to-user-namespace pbpaste"))
(defun paste-to-osx (text &optional push)
"Add kill ring entries (TEXT) to OSX clipboard. PUSH."
(let ((process-connection-type nil))
(let ((proc (start-process "pbcopy" "*Messages*" "reattach-to-user-namespace" "pbcopy")))
(process-send-string proc text)
(process-send-eof proc))))
(setq interprogram-cut-function 'paste-to-osx)
(setq interprogram-paste-function 'copy-from-osx))
(progn
"When we are running in native macos."
(defun copy-from-osx ()
"Use OSX clipboard to paste. PULL"
(shell-command-to-string "pbpaste"))
(defun paste-to-osx (text &optional push)
"Add kill ring entries (TEXT) to OSX clipboard. PUSH."
(let ((process-connection-type nil))
(let ((proc (start-process "pbcopy" "*Messages*" "pbcopy")))
(process-send-string proc text)
(process-send-eof proc))))
(setq interprogram-cut-function 'paste-to-osx)
(setq interprogram-paste-function 'copy-from-osx))))
Refs:
- https://gist.github.com/burke/5960455
- https://gist.github.com/dergachev/8259104
- https://unix.stackexchange.com/questions/16694/copy-input-to-clipboard-over-ssh/498270#498270
- https://blog.d46.us/zsh-tmux-emacs-copy-paste/
NOTE: The clipboard server should also work with Linux using xclip/xsel and/or just reading/writing a local file, e.g /tmp/clipboard.txt.