💾 Archived View for shit.cx › 2020 › 11 › 03 › a-macos-linux-hybrid-part-2.gmi captured on 2020-11-07 at 01:25:03. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
As I described in Part 1, my goal was to configure my Mac with a Linux VM and have the two tightly integrated so that the Linux VM feels native.
Practically, this means that the following continue to work as expected:
To do this, I chose to use VirtualBox. It's mature, free and runs on macOS.
The 2019 Macbook Pro has a crazy fast disk and I wanted its speed in my Linux VM. Initially, I was going to send a [raw partition to VirtualBox][1] but it got complicated so I tried out a fixed size partition instead.
I installed Debian 10, and measured the disk speed. The VM recorded 1.6GB/s. Running on the metal gave me 2.0GB/s. (`dd if=/dev/zero of=./test bs=1MB count=$((1024 * 10 ))`)
It wasn't perfect, but I was happy to move on.
For files that must be available on both the Host and the VM, I use VirtualBox shared folders. The performance isn't as good as a fixed-size disk, but it's still a very respectable 660MB/s.
My files are organised to balance the performance and availability requirements.
Then the VM needed the internet and access to and from the host. To do that, I discovered that the VM requires two network interfaces. A NAT interface for accessing the Internet and a host-only interface for the host and VM communicate between themselves.
I configured the host-only with static ip addresses. The `/etc/network/interfaces` looks like this.
source /etc/network/interfaces.d/* # The loopback network interface auto lo iface lo inet loopback # The NAT network interface allow-hotplug enp0s3 iface enp0s3 inet dhcp # The host-only network interface allow-hotplug enp0s8 iface enp0s8 inet static address 192.168.56.2 netmask 255.255.255.0
SSH keys are stored in an SSH agent on the host and forwarded to the VM using the `ForwardAgent` SSH option. This is configured using `~/.ssh/config`. It allows logging in with the command `ssh devbox` rather than the full `ssh -A jon@192.168.56.2`.
Host devbox User jon HostName 192.168.56.2 ForwardAgent true
To avoid password prompts, you'll need to drop your public key into `~/.ssh/authorized_keys`.
On the VM, the `~/.ssh/config` file is configured to back to the host. This is critical to allow files, state and commands to be sent to the host for that seamless experience I'm looking for.
Host mac User jon HostName 192.168.56.1 ForwardAgent true
To open files on the host, I made a script to copy the local file, then run the `open` command against it on the host. This makes a few assumption (which I've found to be valid so far, but will eventually need to be improved).
This script is saved to `~/bin/ropen`.
#!/usr/bin/env bash set -e [[ $# -eq 0 ]] && echo "No file provided" && exit 1 HOST="mac" ARG="$(readlink -f "$1" || echo $1)" REMOTE_ARG="$(echo "$ARG" | sed -E 's#^/home/jon#/Users/jon)" mode() { if [[ -f "${ARG}" ]]; then if remote_file_exists; then echo "file_remote" && return 0 else echo "file_copy" && return 0 fi fi [[ -d "${ARG}" ]] && echo "dir" && return 0 [[ "${ARG}" =~ ^(https|http|ftp):// ]] && echo "url" && return 0 return 1 } remote_file_exists() { local lmd5=$(md5sum "${ARG}" | awk '{ print $1 }') local rmd5=$(ssh "${HOST}" md5 \""${REMOTE_ARG}"\" | awk '{ print $NF }') [[ "${lmd5}" == "${rmd5}" ]] return $? } case $(mode) in "file_copy") REMOTE_FILE="/tmp/$(basename "${ARG}")" scp "${ARG}" "${HOST}:\"${REMOTE_FILE}\"" &> /dev/null ssh ${HOST} -- open "\"${REMOTE_FILE}\"" 2> /dev/null ;; "file_remote"|"dir") ssh ${HOST} -- open "\"${REMOTE_ARG}\"" 2> /dev/null ;; "url") ssh ${HOST} -- open "\"$1\"" 2> /dev/null ;; *) echo "Invalid input" exit 1 ;; esac
Next, it's trivial to make a command to run a command on the host. (`~/bin/rrun`)
#!/usr/bin/env bash [[ $# -eq 0 ]] && echo "No file provided" && exit 1 ssh mac "$@"
Now, we can clone the macOS tools, `pbcopy` and `pbpaste` to set and fetch the clipboard.
Plus, I would like to be able to pipe things in and out of the clipboards much like you do with `pbcopy` and `pbpaste`.
This is `pbpaste`. It dumps the latest tmux buffer to stdout. If I trust that my clipboards are in sync then I could have equally chosen any of the clipboards. I chose tmux because it's local and unlike Vim, is always available.
#!/usr/bin/env bash latest_buffer() { tmux list-buffers | awk -F: '{ print $1; exit 0 }' } tmux save-buffer -b $(latest_buffer) - echo
And `pbcopy` takes content via stdin, sets the tmux buffer, then sets the macOS clipboard.
#!/usr/bin/env bash cat /dev/stdin | tmux load-buffer - pbpaste | rrun pbcopy
The last part is setting the Vim yank buffer. Using an [autocmd][2], the `pbcopy` script is run whenever the Yank buffer changes.
autocmd TextYankPost * silent! call system('pbcopy &',join(v:event["regcontents"],"\n"))
So this has brought us to the point where we have a Linux VM with synchronised files and clipboards. It basically works, but next I will go though how I have ironed out the experience.
Next: Ironing out the experience
[1] Raw partitions on Virtualbox
---
created_at: 2020-11-03T19:32+00:00
email: jon@shit.cx
tags: tech hardware linux
The content for this site is CC-BY-SA-4.0.