#!/usr/bin/env bash # Function to re-exec the script as another user via sudo (only if needed) exec_as() { local user user="$1" shift if [ "$(whoami)" != "$user" ]; then exec sudo -E -H -u "$user" -- "$0" "$@" fi } # Exec the script as qemu exec_as qemu "$@" # Environment PATH="${HOME}/launchers:${HOME}/bin:${PATH}" EDITOR="${EDITOR:-nvim}" export QEMUSH_NAME # Aliases ls='ls --color=auto' # Set a restrictive umask to make sure qemu user files are private umask 027 # Function to print a colored error perror() { >&2 printf '\033[1;31mKO:\033[0m \033[1m%s\033[0m\n' "$*" } # Function to show the usage public_help() { local name name=$(basename "$0") exec cat << EOF ${name}: usage: ${name} running - (default behaviour) list running VMs ${name} start - start a VM ${name} attach - attach to the VM monitor socket ${name} ls - list available VMs ${name} edit - edit VM launching script ${name} rm - delete launch script ${name} diskls - list available disk images ${name} diskadd - create a disk image ${name} diskrm - delete disk image ${name} shell - start a shell as user qemu ${name} help - show this help ${name} add [] - add a launching script ${name} do - run shell input as user qemu EOF } # Function to throw an invalid usage error (skill issue) error_usage() { perror "Invalid usage" >&2 public_help return 1 } # Function to start a virtual machine public_start() { QEMUSH_NAME="$1" set -- "$@" \ -monitor "unix:$(pathof socket),server,nowait" \ -daemonize if ! "$@"; then perror "error launching virtual machine \"${QEMUSH_NAME}\"" return 2 fi } # Attach to a running virtual machine output, the latest opened if no # argument is provided public_attach() { QEMUSH_NAME="$1" shift exec socat -,rawer,escape=15 "UNIX-CONNECT:$(pathof socket)" } # List running virtual machines public_running() { cd || return echo "Running machines:" set -- $ls -t sockets/monitors "$@" exec "$@" } # List available virtual machines entrypoints public_ls() { cd || return echo "Available machines:" set -- $ls launchers "$@" exec "$@" } # Create a copy-on-write disk for a virtual machine public_diskadd() { QEMUSH_NAME="$1" shift exec qemu-img create -f qcow2 "$(pathof disk)" "$1" } # Delete a disk public_diskrm() { for disk in "$@"; do QEMUSH_NAME="$disk" rm -vi -- "$(pathof disk)" done } # List available disks public_diskls() { cd || return echo "Available disks:" set -- $ls disks "$@" exec "$@" } # Edit a virtual machine entrypoint with a text editor public_edit() { cd || return local file="launchers/${1}" "$EDITOR" "$file" [ -f "$file" ] && exec chmod u+x "$file" } # Delete a virtual machine entrypoint public_rm() { cd ~/launchers || return exec rm -vi -- "$@" } # Invoke bash as qemu user in its home directory public_shell() { cd || return set -- bash -i "$@" exec "$@" } # Output the content of an entrypoint, with coloration if on a virtual # terminal public_cat() { cd ~/launchers || return cat -- "$@" } # Copy a file in entrypoints folder public_add() { trap return EXIT set -e local name if [ -n "$2" ]; then name="$2" else name=$(basename "$1") fi name="${HOME}/launchers/${name}" cp -vi -- "$1" "$name" chmod 740 "$name" set +e trap - EXIT } # Run shell commands as qemu public_do() { exec sh -c "$*" } # Retrieve user requested function function="$1" shift # Defauts to `active` if no function is supplied; else checks for a public # function named after the argument; else fails if [ -z "$function" ]; then public_running elif declare -F | cut -d \ -f 3- | grep '^public_' | sed 's/^public_//' | grep -q "^${function}$"; then "public_${function}" "$@" else error_usage fi