#!/usr/bin/env bash set -euo pipefail log() { printf '[rm] %s\n' "$*" } usage() { cat <<'EOF' Usage: ./rm.sh Removes VM artifacts from state/ and cleans up TAP and mapped devices. EOF } find_vm_json() { local query="$1" local vm_json match_count=0 match="" for vm_json in state/vms/*/vm.json; do [[ -f "$vm_json" ]] || continue local id name id="$(jq -r '.meta.id // empty' "$vm_json")" name="$(jq -r '.meta.name // empty' "$vm_json")" if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then match="$vm_json" match_count=$((match_count + 1)) fi done if (( match_count == 0 )); then log "no VM found for prefix: $query" exit 1 fi if (( match_count > 1 )); then log "multiple VMs found for prefix: $query" exit 1 fi printf '%s' "$match" } QUERY="${1:-}" if [[ -z "$QUERY" || "$QUERY" == "-h" || "$QUERY" == "--help" ]]; then usage exit 1 fi VM_JSON="$(find_vm_json "$QUERY")" VM_DIR="$(dirname "$VM_JSON")" PID="$(jq -r '.meta.pid // empty' "$VM_JSON")" TAP="$(jq -r '.meta.tap // empty' "$VM_JSON")" API_SOCK="$(jq -r '.meta.api_sock // empty' "$VM_JSON")" BASE_LOOP="$(jq -r '.meta.base_loop // empty' "$VM_JSON")" COW_LOOP="$(jq -r '.meta.cow_loop // empty' "$VM_JSON")" DM_DEV="$(jq -r '.meta.dm_dev // empty' "$VM_JSON")" DM_NAME="$(jq -r '.meta.dm_name // empty' "$VM_JSON")" if [[ -n "$PID" ]]; then sudo kill "$PID" 2>/dev/null || true fi if [[ -n "$TAP" ]]; then sudo ip link del "$TAP" 2>/dev/null || true fi if [[ -n "$API_SOCK" ]]; then rm -f "$API_SOCK" fi if [[ -n "$DM_DEV" || -n "$DM_NAME" ]]; then sudo dmsetup remove "${DM_NAME:-$DM_DEV}" 2>/dev/null || true fi if [[ -n "$COW_LOOP" ]]; then sudo losetup -d "$COW_LOOP" 2>/dev/null || true fi if [[ -n "$BASE_LOOP" ]]; then sudo losetup -d "$BASE_LOOP" 2>/dev/null || true fi if [[ -d "$VM_DIR" ]]; then rm -rf "$VM_DIR" fi log "removed $VM_DIR"