#!/usr/bin/env bash # # qvm-stop - Gracefully shut down the QEMU VM # # This script stops the running VM by sending SIGTERM first for graceful # shutdown, waiting up to 30 seconds, then sending SIGKILL if necessary. # It cleans up state files (vm.pid, ssh.port) after shutdown completes. # # Usage: qvm stop # set -euo pipefail # Source common library readonly QVM_LIB_DIR="${QVM_LIB_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../lib" && pwd)}" source "${QVM_LIB_DIR}/common.sh" # Timeout for graceful shutdown in seconds readonly SHUTDOWN_TIMEOUT=30 # # wait_for_process_exit - Wait for process to terminate # Args: $1 - PID to wait for # $2 - timeout in seconds # Returns: 0 if process exits, 1 on timeout # wait_for_process_exit() { local pid="$1" local timeout="$2" local elapsed=0 while (( elapsed < timeout )); do if ! kill -0 "$pid" 2>/dev/null; then return 0 fi sleep 1 (( elapsed++ )) done return 1 } # # main - Main shutdown orchestration # main() { # Check if VM is running if ! is_vm_running; then log_info "VM is not running" exit 0 fi # Get VM process PID local vm_pid vm_pid=$(cat "$QVM_PID_FILE") log_info "Shutting down VM (PID: $vm_pid)..." # Send SIGTERM for graceful shutdown if kill -TERM "$vm_pid" 2>/dev/null; then log_info "Sent SIGTERM, waiting up to ${SHUTDOWN_TIMEOUT}s for graceful shutdown..." if wait_for_process_exit "$vm_pid" "$SHUTDOWN_TIMEOUT"; then log_info "VM shut down gracefully" else log_warn "Graceful shutdown timeout, forcefully terminating..." # Send SIGKILL to force termination if kill -KILL "$vm_pid" 2>/dev/null; then # Wait briefly to ensure process is dead sleep 1 # Verify process is actually dead if kill -0 "$vm_pid" 2>/dev/null; then die "Failed to kill VM process $vm_pid" fi log_info "VM forcefully terminated" else log_warn "Process $vm_pid already terminated" fi fi else log_warn "Process $vm_pid already terminated (could not send SIGTERM)" fi # Clean up state files log_info "Cleaning up state files..." rm -f "$QVM_PID_FILE" "$QVM_SSH_PORT_FILE" log_info "VM stopped successfully" } main "$@"