diff options
author | EuAndreh <eu@euandre.org> | 2022-12-02 22:22:27 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2022-12-02 22:24:54 -0300 |
commit | 5f981d9fbe486e37e90ee18d8c959a7be7616558 (patch) | |
tree | c5a6b6d24ae70e23d592381ebda3d1b117e2636e /bin/vm | |
parent | bin/serve: Find a port instead of having a hard-coded fallback (diff) | |
download | dotfiles-5f981d9fbe486e37e90ee18d8c959a7be7616558.tar.gz dotfiles-5f981d9fbe486e37e90ee18d8c959a7be7616558.tar.xz |
bin/vm: v2
- support -S flag for toggling snapshot mode;
- support -G flag for toggling GUI mode;
- dynamically discover available images;
- dynamically find available ports to map to 22.
Diffstat (limited to 'bin/vm')
-rwxr-xr-x | bin/vm | 169 |
1 files changed, 113 insertions, 56 deletions
@@ -5,7 +5,7 @@ set -eu usage() { cat <<-'EOF' Usage: - vm ACTION [OS] + vm [-G] [-S] [-v] ACTION [OS] vm -h EOF } @@ -15,6 +15,9 @@ help() { Options: + -G use graphics + -S don't write to VM image (snapshot on) + -v verbose mode -h, --help show this message ACTION one of: @@ -27,14 +30,14 @@ help() { 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 + $XDG_STATE_HOME/vm/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. + $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`. + It also generates an SSH configuration file under + $XDG_DATA_HOME/vm/ssh.conf to be `Included` by the main + ~/.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: @@ -53,8 +56,8 @@ help() { List the available VMs, and their current state: $ vm status - alpine:up - slackware:down + alpine up + slackware down freebsd:up EOF } @@ -75,8 +78,20 @@ for flag in "$@"; do esac done -while getopts 'h' flag; do +GRAPHICS=false +SNAPSHOT=false +VERBOSE=false +while getopts 'GSvh' flag; do case "$flag" in + G) + GRAPHICS=true + ;; + S) + SNAPSHOT=true + ;; + v) + VERBOSE=true + ;; h) usage help @@ -91,83 +106,125 @@ done shift $((OPTIND - 1)) -ACTION="${1:-}" -OS="${2:-}" -eval "$(assert-arg "$ACTION" 'ACTION')" +QCOW_DIR="${XDG_STATE_HOME:-$HOME/.local/state}"/vm/qemu +RUNDIR="${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/vm" +LOGS="$RUNDIR"/logs +mkdir -p "$RUNDIR" "$QCOW_DIR" "$XDG_DATA_HOME"/vm +guess_name() { + PREFIX="$1" + IMAGES="$(find "$QCOW_DIR" -type f -name "${PREFIX}*")" + COUNT="$(echo "$IMAGES" | wc -l)" + if [ "$COUNT" != 1 ]; then + printf 'Cannot guess name with the given prefix: "%s".\n' "$PREFIX" >&2 + printf '\nThe possibilities are:\n' >&2 + printf '%s\n' "$IMAGES" | + xargs -I% basename % .qcow2 | + sed 's/^/- /' >&2 + exit 2 + fi + printf '%s\n' "$(basename "$IMAGES" .qcow2)" +} -VMS=' -alpine:60022 -' +write_ssh_config() { + for port in "$RUNDIR"/*.port; do + if [ ! -e "$port" ]; then + break + fi + NAME="$(basename "$port" .port)" + PORT="$(cat "$port")" + cat <<-EOF + Host $NAME + HostName localhost + Port $PORT + + EOF + done > "$XDG_DATA_HOME"/vm/ssh.conf +} -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 +write_ssh_config - 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" +ACTION="${1:-}" +OS="${2:-}" +eval "$(assert-arg "$ACTION" 'ACTION')" + + +FLAGS='' +if [ "$GRAPHICS" = false ]; then + FLAGS="$FLAGS -nographic" +else + FLAGS="$FLAGS -display sdl" +fi +if [ "$SNAPSHOT" = true ]; then + FLAGS="$FLAGS -snapshot" +fi -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 + for img in "$QCOW_DIR"/*.qcow2; do + if [ ! -e "$img" ]; then + break + fi + NAME="$(basename "$img" .qcow2)" + if [ -e "$RUNDIR/$NAME.pid" ]; then STATUS=up else STATUS=down fi - printf '%s:%s\n' "$vm" "$STATUS" + printf '%s\t%s\n' "$NAME" "$STATUS" done ;; up) eval "$(assert-arg "$OS" 'OS')" - PORT="$(port_for_os "$OS")" + OS="$(guess_name "$OS")" + PID_F="$RUNDIR/$OS.pid" + PORT_F="$RUNDIR/$OS.port" 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" + exit 1 + fi + + PORT="$(free-port)" + QCOW="$QCOW_DIR"/"$OS".qcow2 + if [ "$VERBOSE" = true ]; then + set -x fi + # shellcheck disable=2086 + qemu-system-x86_64 \ + -m 1G \ + -nic user,model=virtio,hostfwd=tcp::"$PORT"-:22 \ + -drive file="$QCOW",media=disk,if=virtio \ + -enable-kvm \ + $FLAGS 1>>"$LOGS" 2>&1 & + PID=$! + set +x + printf '%s' "$PID" > "$PID_F" + printf '%s' "$PORT" > "$PORT_F" + + write_ssh_config ;; down) eval "$(assert-arg "$OS" 'OS')" - PORT="$(port_for_os "$OS")" + OS="$(guess_name "$OS")" + PID_F="$RUNDIR/$OS.pid" + PORT_F="$RUNDIR/$OS.port" 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")" + exit 1 fi + + PID="$(cat "$PID_F")" + rm -f "$PID_F" "$PORT_F" + kill "$PID" + + write_ssh_config ;; *) printf 'Unrecognized action: "%s".\n\n' "$ACTION" >&2 |