#!/usr/bin/env bash set -euo pipefail log() { printf '[stop] %s\n' "$*" } DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$DIR/dns.sh" STATE="$DIR/state" VM_ROOT="$STATE/vms" usage() { cat <<'EOF' Usage: ./stop.sh ... Sends Ctrl+Alt+Del to the guest via the Firecracker API socket. EOF } find_vm_json() { local query="$1" local vm_json match_count=0 match="" for vm_json in "$VM_ROOT"/*/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" } QUERIES=("$@") if (( ${#QUERIES[@]} == 1 )) && [[ "${QUERIES[0]}" == "-h" || "${QUERIES[0]}" == "--help" ]]; then usage exit 0 fi if (( ${#QUERIES[@]} == 0 )); then usage exit 1 fi VM_JSONS=() for query in "${QUERIES[@]}"; do VM_JSON="$(find_vm_json "$query")" already_added=0 for existing_vm_json in "${VM_JSONS[@]}"; do if [[ "$existing_vm_json" == "$VM_JSON" ]]; then already_added=1 break fi done if (( already_added == 0 )); then VM_JSONS+=("$VM_JSON") fi done for vm_json in "${VM_JSONS[@]}"; do vm_id="$(jq -r '.meta.id // empty' "$vm_json")" vm_name="$(jq -r '.meta.name // empty' "$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")" dns_name="$(jq -r '.meta.dns_name // empty' "$vm_json")" if [[ -z "$dns_name" ]]; then dns_name="$(banger_dns_name "$vm_name")" fi if [[ -z "$pid" ]]; then log "pid not found for ${vm_name:-$vm_json}" exit 1 fi if [[ -z "$api_sock" || ! -S "$api_sock" ]]; then log "api socket not found for ${vm_name:-$vm_json}: $api_sock" exit 1 fi log "sending Ctrl+Alt+Del to ${vm_name:-$vm_json}" sudo -E curl --unix-socket "$api_sock" -X PUT http://localhost/actions \ -H "Content-Type: application/json" \ -d '{ "action_type": "SendCtrlAltDel" }' >/dev/null if ! banger_wait_for_vm_exit "$pid" "$api_sock" 30; then log "timed out waiting for ${vm_name:-$vm_json} to exit" exit 1 fi banger_teardown_vm_runtime "$tap" "$api_sock" "$dm_name" "$dm_dev" "$cow_loop" "$base_loop" banger_mark_vm_stopped "$vm_json" banger_dns_remove_record_name "$dns_name" log "requested shutdown for ${vm_name:-$vm_json}" done