@@ -12,14 +12,18 @@ public class QemuBridgeConnection : IBridgeConnection
1212{
1313 private readonly string _imagePath ;
1414 private Process ? _vmProcess ;
15- private int _forwardedPort = 5000 ;
1615 private readonly QemuOptions ? _options ;
1716
1817 public QemuBridgeConnection ( QemuOptions options )
1918 {
2019 _options = options ;
2120 _imagePath = options . ImagePath ?? throw new ArgumentNullException ( nameof ( options . ImagePath ) ) ;
22- _forwardedPort = options . Port ;
21+ if ( _options . Port == 0 )
22+ {
23+ _options . Port = FindFreePort ( _options . StartPort ) ;
24+ KernelLog . Info ( $ "[QEMU] Puerto libre asignado dinámicamente: { _options . Port } ") ;
25+ }
26+
2327 }
2428
2529 public Task SendAsync ( string path ) // No lo voy a usar por ahora.
@@ -31,27 +35,101 @@ public Task SendAsync(string path) // No lo voy a usar por ahora.
3135 ) ;
3236 }
3337
38+ private void PreflightCheck ( ) // Realiza chequeos previos antes de iniciar la VM
39+ {
40+ KernelLog . Warn ( "[Preflight] Iniciando healthcheck previos para QEMU" ) ;
41+
42+ if ( _options == null )
43+ {
44+ KernelLog . Panic ( "[Preflight] Opciones de QEMU no definidas." ) ;
45+ throw new ArgumentNullException ( nameof ( _options ) ) ;
46+ }
47+
48+ // 1. Validación de imagen
49+ if ( ! File . Exists ( _options . ImagePath ) || _options . ImagePath . Length == 0 )
50+ {
51+ KernelLog . Panic ( $ "[Preflight] Imagen no encontrada: { _options . ImagePath } ") ;
52+ throw new FileNotFoundException ( "La imagen de disco no existe." , _options . ImagePath ) ;
53+ }
54+
55+ // 2. Validación y creación del folder compartido
56+ if ( ! string . IsNullOrEmpty ( _options . SharedFolder ) )
57+ {
58+ if ( ! Directory . Exists ( _options . SharedFolder ) )
59+ {
60+ Directory . CreateDirectory ( _options . SharedFolder ) ;
61+ KernelLog . Info ( $ "[Preflight] Carpeta compartida creada: { _options . SharedFolder } ") ;
62+ }
63+ }
64+
65+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
66+ {
67+ opts . SharedFolder = "C:\\ Users\\ Public\\ SharpCoreShare" ;
68+ }
69+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
70+ {
71+ opts . SharedFolder = "/home/tuusuario/sharpcore-share" ;
72+ }
73+ else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
74+ {
75+ opts . SharedFolder = "/Users/tuusuario/sharpcore-share" ;
76+ }
77+
78+
79+ // 3. Validación de puerto libre
80+ if ( _options . Port == 0 )
81+ {
82+ _options . Port = FindFreePort ( ) ;
83+ KernelLog . Info ( $ "[Preflight] Puerto libre asignado dinámicamente: { _options . Port } ") ;
84+ }
85+
86+ // 4. Limpieza de procesos zombie de QEMU
87+ var qemuProcs = Process . GetProcessesByName ( "qemu-system-x86_64" ) ;
88+ foreach ( var proc in qemuProcs )
89+ {
90+ try
91+ {
92+ proc . Kill ( true ) ;
93+ proc . WaitForExit ( 1500 ) ; // Espera hasta 1.5 segundos para que el proceso termine
94+ KernelLog . Info ( $ "[Preflight] Proceso QEMU colgado eliminado: PID { proc . Id } ") ;
95+ }
96+ catch
97+ {
98+ KernelLog . Info ( $ "[Preflight] No hay procesos QEMU en paralelo, Preflight OK.") ;
99+ }
100+ }
101+ }
102+
103+
34104 public void Start ( )
35105 {
36106 if ( _options == null ) throw new ArgumentNullException ( nameof ( _options ) ) ;
37107
38108 if ( _options . Port == 0 )
39109 {
40- _forwardedPort = FindFreePort ( ) ;
41- KernelLog . Info ( $ "[QEMU] Puerto libre asignado dinámicamente: { _forwardedPort } ") ;
110+ _options . Port = FindFreePort ( ) ;
111+ KernelLog . Info ( $ "[QEMU] Puerto libre asignado dinámicamente: { _options . Port } ") ;
42112 }
43- else
113+
114+ PreflightCheck ( ) ;
115+
116+ KernelLog . Info ( $ "[QEMU] Iniciando VM desde { _options . ImagePath } ") ;
117+
118+
119+ string virtfsArg = string . Empty ;
120+ if ( ! string . IsNullOrEmpty ( _options . SharedFolder ) )
44121 {
45- _forwardedPort = _options . Port ;
122+ virtfsArg = $ "-virtfs local,path= { _options . SharedFolder } ,mount_tag=hostshare,security_model=passthrough,id=hostshare " ;
46123 }
47124
48- KernelLog . Info ( $ "[QEMU] Iniciando VM desde { _options . ImagePath } ") ;
125+ string args =
126+ $ "-hda { _options . ImagePath } -m { _options . MemoryMb } " +
127+ $ "-net nic -net user,hostfwd=tcp::{ _options . Port } -:{ _options . Port } " +
128+ virtfsArg +
129+ $ "{ ( _options . UseSnapshot ? "-snapshot " : "" ) } " +
130+ $ "{ ( _options . UseNographic ? "-nographic " : "" ) } " +
131+ $ "{ _options . ExtraArgs } ";
49132
50- string args = $ "-hda { _options . ImagePath } -m { _options . MemoryMb } " +
51- $ "-net nic -net user,hostfwd=tcp::{ _forwardedPort } -:{ _forwardedPort } " +
52- $ "{ ( _options . UseSnapshot ? "-snapshot " : "" ) } " +
53- $ "{ ( _options . UseNographic ? "-nographic" : "" ) } " +
54- $ "{ _options . ExtraArgs } ";
55133
56134 var startInfo = new ProcessStartInfo
57135 {
@@ -60,18 +138,42 @@ public void Start()
60138 RedirectStandardOutput = true ,
61139 RedirectStandardError = true ,
62140 UseShellExecute = false ,
63- CreateNoWindow = true
141+ CreateNoWindow = false // true si querés ocultar ventana, false para debug
142+ } ;
143+
144+ _vmProcess = new Process { StartInfo = startInfo } ;
145+
146+ _vmProcess . OutputDataReceived += ( sender , e ) =>
147+ {
148+ if ( ! string . IsNullOrEmpty ( e . Data ) )
149+ KernelLog . Info ( $ "[QEMU stdout] { e . Data } ") ;
150+ } ;
151+ _vmProcess . ErrorDataReceived += ( sender , e ) =>
152+ {
153+ if ( ! string . IsNullOrEmpty ( e . Data ) )
154+ KernelLog . Panic ( $ "[QEMU stderr] { e . Data } ") ;
64155 } ;
65156
66- _vmProcess = Process . Start ( startInfo ) ;
157+ _vmProcess . Start ( ) ;
158+ _vmProcess . BeginOutputReadLine ( ) ;
159+ _vmProcess . BeginErrorReadLine ( ) ;
160+
161+ KernelLog . Info ( $ "[QEMU] Esperando a que el puerto { _options . Port } esté disponible...") ;
162+ WaitForPort ( "127.0.0.1" , _options . Port ) ;
163+ _vmProcess . WaitForExit ( ) ;
164+ KernelLog . Info ( "[QEMU] VM finalizó correctamente." ) ;
67165
68- KernelLog . Info ( $ "[QEMU] Esperando a que el puerto { _forwardedPort } esté disponible...") ;
69- WaitForPort ( "127.0.0.1" , _forwardedPort ) ;
70166 }
71167
72168 public void Send ( string command )
73169 {
74- var remote = new RemoteLinuxBridgeConnection ( $ "http://127.0.0.1:{ _forwardedPort } /exec") ;
170+ if ( _options == null )
171+ {
172+ KernelLog . Panic ( "[Preflight] Opciones de QEMU no definidas." ) ;
173+ throw new ArgumentNullException ( nameof ( _options ) ) ;
174+ }
175+
176+ var remote = new RemoteLinuxBridgeConnection ( $ "http://127.0.0.1:{ _options . Port } /exec") ;
75177 remote . Send ( command ) ;
76178 }
77179
@@ -93,7 +195,14 @@ private void WaitForPort(string host, int port, int timeoutSeconds = 15)
93195 {
94196 using var client = new TcpClient ( ) ;
95197 client . Connect ( host , port ) ;
96- KernelLog . Info ( $ "[QEMU] VM escuchando en http://127.0.0.1:{ _forwardedPort } ") ;
198+
199+ if ( _options == null )
200+ {
201+ KernelLog . Panic ( "[Preflight] Opciones de QEMU no definidas." ) ;
202+ throw new ArgumentNullException ( nameof ( _options ) ) ;
203+ }
204+
205+ KernelLog . Info ( $ "[QEMU] VM escuchando en http://127.0.0.1:{ _options . Port } ") ;
97206 return ;
98207 }
99208 catch
@@ -106,9 +215,9 @@ private void WaitForPort(string host, int port, int timeoutSeconds = 15)
106215 throw new Exception ( "QEMU VM did not start in time." ) ;
107216 }
108217
109- private int FindFreePort ( int startPort = 5000 )
218+ private int FindFreePort ( int startPort = 5000 ) // Busca un puerto libre a partir del puerto especificado
110219 {
111- for ( int port = startPort ; port < startPort + 100 ; port ++ )
220+ for ( int port = 0 ; port < startPort + 100 ; port ++ )
112221 {
113222 try
114223 {
@@ -134,5 +243,6 @@ public class QemuOptions
134243 public bool UseNographic { get ; set ; } = true ;
135244 public string ExtraArgs { get ; set ; } = string . Empty ;
136245 public int Port { get ; set ; } = 0 ; // 0 = buscar uno libre
137-
246+ public int StartPort { get ; set ; } = 5000 ;
247+ public string SharedFolder { get ; set ; } = "/path/to/share" ; // ¡Personalizable!
138248}
0 commit comments