#!/bin/sh set -eu usage() { cat <<-'EOF' Usage: vm ACTION [OS] vm -h EOF } help() { cat <<-'EOF' Options: -h, --help show this message ACTION one of: - up - down - status OS the name of the OS to be acted upon Manage the state of known virtual machines. The VM QCOW2 images are stored under $XDG_STATE_HOME/euandreh/qemu/, as "$OS.qcow2" files. The PIDs of the running images are stored in individual files under $XDG_RUNTIME_DIR/vm-pids/, as "$OS.pid" files. It also generates an SSH configuration file to bu `Included` by the main $XDG_CONFIG_HOME/ssh/config file, which contains one alias entry for each VM, so that one can do a combination of `vm up alpine && ssh alpine`. Examples: Start the VM for Alpine: $ vm up alpine Stop the VM for Slackware, which was already down $ vm down slackware The VM for "slackware" is not running, already. List the available VMs, and their current state: $ vm status alpine:up slackware:down freebsd:up EOF } for flag in "$@"; do case "$flag" in --) break ;; --help) usage help exit ;; *) ;; esac done while getopts 'h' flag; do case "$flag" in h) usage help exit ;; *) usage >&2 exit 2 ;; esac done shift $((OPTIND - 1)) ACTION="${1:-}" OS="${2:-}" eval "$(assert-arg "$ACTION" 'ACTION')" VMS=' alpine:60022 ' for vm in $VMS; do NAME="$(echo "$vm" | cut -d: -f1)" PORT="$(echo "$vm" | cut -d: -f2)" cat <<-EOF Host $NAME HostName localhost Port $PORT EOF done > "$XDG_DATA_HOME"/euandreh/vm-ssh.conf PIDS_DIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/vm-pids" PID_F="$PIDS_DIR/$OS.pid" mkdir -p "$PIDS_DIR" port_for_os() { _OS="$1" _PORT="$(echo "$VMS" | awk -F: -vOS="$_OS" '$1 == OS { print $2 }')" if [ -z "$_PORT" ]; then printf 'Unknown OS: "%s".\n\n' "$_OS" >&2 usage >&2 exit 2 fi printf '%s' "$_PORT" } case "$ACTION" in status) echo "$VMS" | awk -F: '$0=$1' | while read -r vm; do if [ -e "$PIDS_DIR/$vm.pid" ]; then STATUS=up else STATUS=down fi printf '%s:%s\n' "$vm" "$STATUS" done ;; up) eval "$(assert-arg "$OS" 'OS')" PORT="$(port_for_os "$OS")" if [ -e "$PID_F" ]; then printf 'The VM for "%s" is already running with PID %s.\n' \ "$OS" "$(cat "$PID_F")" >&2 else QCOW="$XDG_STATE_HOME"/euandreh/qemu/"$OS".qcow2 qemu-system-x86_64 \ -m 1G \ -nic user,model=virtio,hostfwd=tcp::"$PORT"-:22 \ -drive file="$QCOW",media=disk,if=virtio \ -enable-kvm \ -nographic 1>/dev/null 2>&1 & printf '%s' $! > "$PID_F" fi ;; down) eval "$(assert-arg "$OS" 'OS')" PORT="$(port_for_os "$OS")" if [ ! -e "$PID_F" ]; then printf 'The VM for "%s" is not running, already.\n' "$OS" >&2 else # shellcheck disable=2064 trap "rm -f $PID_F" EXIT kill "$(cat "$PID_F")" fi ;; *) printf 'Unrecognized action: "%s".\n\n' "$ACTION" >&2 usage >&2 exit 2 esac