using System.Diagnostics; using System.Threading; using QemuVmManager.Models; namespace QemuVmManager.Core; public class QemuProcessManager { private readonly Dictionary _runningVms = new(); private readonly Dictionary _vmStatuses = new(); private readonly Dictionary _performanceMonitors = new(); public event EventHandler? VmStatusChanged; public bool IsQemuInstalled() { try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "--version", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) { return false; } process.WaitForExit(5000); return process.ExitCode == 0; } catch { return false; } } public string GetQemuVersion() { try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "--version", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) { return "Unknown"; } var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(5000); if (process.ExitCode == 0) { var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); return lines.FirstOrDefault() ?? "Unknown"; } return "Unknown"; } catch { return "Unknown"; } } public string GetQemuAccelerators() { try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "-accel help", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; var started = process.Start(); if (!started) { return "Unknown"; } var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(5000); if (process.ExitCode == 0) { return output.Trim(); } return "Unknown"; } catch { return "Unknown"; } } public VirtualizationType GetAvailableVirtualization() { try { // First check if virtualization is enabled in BIOS if (!IsVirtualizationEnabled()) { return VirtualizationType.TCG; } if (OperatingSystem.IsLinux()) { // Check for KVM support on Linux if (File.Exists("/dev/kvm")) { // Test if KVM is accessible var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "-accel help", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); if (output.Contains("kvm") && process.ExitCode == 0) { return VirtualizationType.KVM; } } } return VirtualizationType.TCG; } else if (OperatingSystem.IsWindows()) { // Check QEMU's available accelerators first var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "-accel help", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); // Check for WHPX (Windows Hypervisor Platform) support if (output.Contains("whpx") && process.ExitCode == 0) { return VirtualizationType.HyperV; } // Check for Hyper-V support (hvf) if (output.Contains("hvf") && process.ExitCode == 0) { return VirtualizationType.HVF; } // Check for HAXM support if (output.Contains("hax") && process.ExitCode == 0) { return VirtualizationType.HAXM; } } // Fallback: Check for Hyper-V support using WMI try { var wmiProcess = new Process { StartInfo = new ProcessStartInfo { FileName = "powershell", Arguments = "-Command \"Get-WmiObject -Class Msvm_VirtualSystemSettingData -Namespace root\\virtualization\\v2 -ErrorAction SilentlyContinue | Select-Object -First 1\"", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (wmiProcess.Start()) { wmiProcess.WaitForExit(3000); if (wmiProcess.ExitCode == 0 && !string.IsNullOrEmpty(wmiProcess.StandardOutput.ReadToEnd())) { return VirtualizationType.HyperV; } } } catch { // Hyper-V check failed } return VirtualizationType.TCG; } else if (OperatingSystem.IsMacOS()) { // Check for HVF (Hypervisor.framework) on macOS var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = "-accel help", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); if (output.Contains("hvf") && process.ExitCode == 0) { return VirtualizationType.HVF; } } return VirtualizationType.TCG; } return VirtualizationType.TCG; } catch { return VirtualizationType.TCG; } } public bool IsVirtualizationEnabled() { try { if (OperatingSystem.IsLinux()) { // Check if virtualization is enabled in BIOS var process = new Process { StartInfo = new ProcessStartInfo { FileName = "lscpu", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); return output.Contains("Virtualization:") && (output.Contains("VT-x") || output.Contains("AMD-V") || output.Contains("SVM")); } } else if (OperatingSystem.IsWindows()) { // Multiple methods to check virtualization on Windows // Method 1: Check using systeminfo try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "systeminfo", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); // Check multiple possible virtualization indicators if (output.Contains("Virtualization Enabled In Firmware: Yes") || output.Contains("Virtualization: Enabled") || output.Contains("Hyper-V Requirements:") && output.Contains("Yes")) { return true; } } } catch { // Continue to next method } // Method 2: Check using PowerShell Get-ComputerInfo try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "powershell", Arguments = "-Command \"Get-ComputerInfo | Select-Object HyperVRequirementVirtualizationFirmwareEnabled\"", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); return output.Contains("True"); } } catch { // Continue to next method } // Method 3: Check using wmic try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "wmic", Arguments = "cpu get VirtualizationFirmwareEnabled", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); return output.Contains("TRUE"); } } catch { // Continue to next method } // Method 4: Check using PowerShell Get-WmiObject try { var process = new Process { StartInfo = new ProcessStartInfo { FileName = "powershell", Arguments = "-Command \"Get-WmiObject -Class Msvm_VirtualSystemSettingData -Namespace root\\virtualization\\v2 | Select-Object VirtualSystemIdentifiers\"", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true } }; if (process.Start()) { process.WaitForExit(3000); var output = process.StandardOutput.ReadToEnd(); // If we can query Hyper-V WMI, virtualization is likely enabled return !output.Contains("Get-WmiObject") && output.Length > 0; } } catch { // All methods failed } } return false; } catch { return false; } } public async Task StartVmAsync(VmConfiguration config) { try { if (_runningVms.ContainsKey(config.Name)) { throw new InvalidOperationException($"VM '{config.Name}' is already running"); } var virtualizationType = GetAvailableVirtualization(); var commandBuilder = new QemuCommandBuilder(config, virtualizationType); var command = commandBuilder.BuildCommand(); Console.WriteLine($"[{config.Name}] Starting QEMU with command:"); Console.WriteLine($"[{config.Name}] {command}"); var process = new Process { StartInfo = new ProcessStartInfo { FileName = "qemu-system-x86_64", Arguments = command.Replace("qemu-system-x86_64 ", ""), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = false }, EnableRaisingEvents = true }; var outputLines = new List(); var errorLines = new List(); // Set up event handlers for output capture process.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { outputLines.Add(e.Data); Console.WriteLine($"[{config.Name}] {e.Data}"); } }; process.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { errorLines.Add(e.Data); Console.WriteLine($"[{config.Name}] ERROR: {e.Data}"); } }; process.Exited += (sender, e) => OnVmExitedWithDetails(config.Name, process, outputLines, errorLines); var started = process.Start(); if (!started) { throw new InvalidOperationException("Failed to start QEMU process"); } // Start reading output process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Wait a moment to see if it starts successfully await Task.Delay(3000); if (process.HasExited) { var errorMessage = $"QEMU process exited immediately with code {process.ExitCode}"; if (errorLines.Any()) { errorMessage += $". Errors: {string.Join("; ", errorLines)}"; } if (outputLines.Any()) { errorMessage += $". Output: {string.Join("; ", outputLines)}"; } Console.WriteLine($"[{config.Name}] {errorMessage}"); UpdateVmStatus(config.Name, VmState.Error, -1, errorMessage); throw new InvalidOperationException(errorMessage); } _runningVms[config.Name] = process; UpdateVmStatus(config.Name, VmState.Running, process.Id); // Start monitoring in background _ = Task.Run(() => MonitorVmAsync(config.Name, process)); return true; } catch (Exception ex) { UpdateVmStatus(config.Name, VmState.Error, -1, ex.Message); throw; } } public async Task StopVmAsync(string vmName, bool force = false) { if (!_runningVms.TryGetValue(vmName, out var process)) { return false; } try { UpdateVmStatus(vmName, VmState.Stopping, process.Id); if (force) { process.Kill(); } else { // Try graceful shutdown first process.CloseMainWindow(); // Wait for graceful shutdown try { var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); await process.WaitForExitAsync(cts.Token); } catch (OperationCanceledException) { // Timeout occurred, kill the process process.Kill(); } } return true; } catch (Exception ex) { UpdateVmStatus(vmName, VmState.Error, process.Id, ex.Message); throw; } } public async Task PauseVmAsync(string vmName) { if (!_runningVms.TryGetValue(vmName, out var process)) { return false; } try { // Use QEMU monitor to pause VM await SendQemuCommandAsync(vmName, "stop"); UpdateVmStatus(vmName, VmState.Paused, process.Id); return true; } catch (Exception ex) { UpdateVmStatus(vmName, VmState.Error, process.Id, ex.Message); throw; } } public async Task ResumeVmAsync(string vmName) { if (!_runningVms.TryGetValue(vmName, out var process)) { return false; } try { // Use QEMU monitor to resume VM await SendQemuCommandAsync(vmName, "cont"); UpdateVmStatus(vmName, VmState.Running, process.Id); return true; } catch (Exception ex) { UpdateVmStatus(vmName, VmState.Error, process.Id, ex.Message); throw; } } public VmStatus? GetVmStatus(string vmName) { return _vmStatuses.TryGetValue(vmName, out var status) ? status : null; } public IEnumerable GetAllVmStatuses() { return _vmStatuses.Values; } public bool IsVmRunning(string vmName) { return _runningVms.ContainsKey(vmName) && !_runningVms[vmName].HasExited; } private async Task MonitorVmAsync(string vmName, Process process) { try { while (!process.HasExited) { await Task.Delay(5000); // Check every 5 seconds // Update resource usage if VM is running if (process.HasExited == false) { var resourceUsage = await GetVmResourceUsageAsync(vmName); UpdateVmResourceUsage(vmName, resourceUsage); } } } catch (Exception ex) { UpdateVmStatus(vmName, VmState.Error, process.Id, ex.Message); } } private Task GetVmResourceUsageAsync(string vmName) { try { // This is a simplified implementation // In a real scenario, you would use QEMU monitor commands or libvirt var usage = new VmResourceUsage(); // Get CPU usage from process if (_runningVms.TryGetValue(vmName, out var process)) { var startTime = process.StartTime; var totalProcessorTime = process.TotalProcessorTime; var realTime = DateTime.Now - startTime; usage.CpuUsage = (totalProcessorTime.TotalMilliseconds / (Environment.ProcessorCount * realTime.TotalMilliseconds)) * 100; // Get memory usage usage.MemoryUsage = process.WorkingSet64 / (1024 * 1024); // Convert to MB } return Task.FromResult(usage); } catch { return Task.FromResult(new VmResourceUsage()); } } private async Task SendQemuCommandAsync(string vmName, string command) { // This would require QEMU monitor socket or similar mechanism // For now, this is a placeholder await Task.Delay(100); } private void OnVmExited(string vmName) { if (_runningVms.TryGetValue(vmName, out var process)) { _runningVms.Remove(vmName); UpdateVmStatus(vmName, VmState.Stopped, -1); } } private void OnVmExitedWithDetails(string vmName, Process process, List outputLines, List errorLines) { if (_runningVms.TryGetValue(vmName, out var runningProcess)) { _runningVms.Remove(vmName); } var errorMessage = $"Process exited with code {process.ExitCode}"; if (errorLines.Any()) { errorMessage += $". Errors: {string.Join("; ", errorLines)}"; } if (outputLines.Any()) { errorMessage += $". Output: {string.Join("; ", outputLines)}"; } Console.WriteLine($"[{vmName}] {errorMessage}"); UpdateVmStatus(vmName, VmState.Stopped, -1, errorMessage); } private void UpdateVmStatus(string vmName, VmState state, int processId, string? errorMessage = null) { if (!_vmStatuses.TryGetValue(vmName, out var status)) { status = new VmStatus { Name = vmName }; _vmStatuses[vmName] = status; } var oldState = status.State; status.State = state; status.ProcessId = processId; status.ErrorMessage = errorMessage; switch (state) { case VmState.Running: status.StartedAt = DateTime.UtcNow; status.StoppedAt = null; break; case VmState.Stopped: case VmState.Error: status.StoppedAt = DateTime.UtcNow; break; } VmStatusChanged?.Invoke(this, new VmStatusChangedEventArgs(vmName, oldState, state)); } private void UpdateVmResourceUsage(string vmName, VmResourceUsage usage) { if (_vmStatuses.TryGetValue(vmName, out var status)) { status.ResourceUsage = usage; } } // Enhanced performance monitoring methods public async Task GetVmPerformanceMetricsAsync(string vmName) { if (!_performanceMonitors.TryGetValue(vmName, out var monitor)) { return new VmPerformanceMetrics(); } return await monitor.GetCurrentMetricsAsync(); } public Task StartPerformanceMonitoringAsync(string vmName) { if (_runningVms.TryGetValue(vmName, out var process)) { var monitor = new PerformanceMonitor(process); _performanceMonitors[vmName] = monitor; return monitor.StartMonitoringAsync(); } return Task.CompletedTask; } public void StopPerformanceMonitoring(string vmName) { if (_performanceMonitors.TryGetValue(vmName, out var monitor)) { monitor.StopMonitoring(); _performanceMonitors.Remove(vmName); } } public async Task> GetPerformanceHistoryAsync(string vmName, int maxSamples = 100) { if (_performanceMonitors.TryGetValue(vmName, out var monitor)) { return await monitor.GetPerformanceHistoryAsync(maxSamples); } return new List(); } } public class PerformanceMonitor { private readonly Process _process; private readonly List _history = new(); private readonly object _lock = new(); private bool _isMonitoring = false; private CancellationTokenSource? _cancellationTokenSource; public PerformanceMonitor(Process process) { _process = process; } public async Task StartMonitoringAsync() { if (_isMonitoring) return; _isMonitoring = true; _cancellationTokenSource = new CancellationTokenSource(); _ = Task.Run(async () => { while (_isMonitoring && !_process.HasExited) { try { var metrics = await GetCurrentMetricsAsync(); lock (_lock) { _history.Add(metrics); // Keep only last 1000 samples if (_history.Count > 1000) { _history.RemoveAt(0); } } await Task.Delay(2000, _cancellationTokenSource.Token); // Sample every 2 seconds } catch (OperationCanceledException) { break; } catch { // Continue monitoring even if there's an error } } }, _cancellationTokenSource.Token); } public void StopMonitoring() { _isMonitoring = false; _cancellationTokenSource?.Cancel(); } public Task GetCurrentMetricsAsync() { try { var metrics = new VmPerformanceMetrics { Timestamp = DateTime.UtcNow, ProcessId = _process.Id }; if (!_process.HasExited) { _process.Refresh(); // CPU metrics metrics.CpuUsagePercent = _process.TotalProcessorTime.TotalMilliseconds / (Environment.ProcessorCount * (DateTime.Now - _process.StartTime).TotalMilliseconds) * 100; // Memory metrics metrics.MemoryUsageMB = _process.WorkingSet64 / (1024 * 1024); metrics.PrivateMemoryMB = _process.PrivateMemorySize64 / (1024 * 1024); metrics.VirtualMemoryMB = _process.VirtualMemorySize64 / (1024 * 1024); // Process metrics metrics.ThreadCount = _process.Threads.Count; metrics.HandleCount = _process.HandleCount; // Performance counters (if available) try { using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); metrics.SystemCpuUsagePercent = cpuCounter.NextValue(); } catch { metrics.SystemCpuUsagePercent = 0; } } return Task.FromResult(metrics); } catch { return Task.FromResult(new VmPerformanceMetrics { Timestamp = DateTime.UtcNow }); } } public Task> GetPerformanceHistoryAsync(int maxSamples = 100) { lock (_lock) { return Task.FromResult(_history.TakeLast(maxSamples).ToList()); } } } public class VmStatusChangedEventArgs : EventArgs { public string VmName { get; } public VmState OldState { get; } public VmState NewState { get; } public VmStatusChangedEventArgs(string vmName, VmState oldState, VmState newState) { VmName = vmName; OldState = oldState; NewState = newState; } }