@@ -143,122 +143,123 @@ private void CleanupFailedExecution(Process? process, StreamWriter? writer)
143143 }
144144 }
145145
146- private static void GracefullyTerminateProcessInternal ( Process ? processToTerminate , ILogger log )
146+ private void GracefullyTerminateProcessInternal ( Process ? processToTerminate , ILogger log )
147147 {
148- if ( processToTerminate == null )
148+ if ( processToTerminate == null ) { log . LogDebug ( "Terminate: Process is null." ) ; return ; }
149+
150+ int processId = - 1 ;
151+ try { processId = processToTerminate . Id ; } catch { /* Handle case where Id access fails */ }
152+
153+ bool alreadyExited = false ;
154+ try { alreadyExited = processToTerminate . HasExited ; }
155+ catch ( Exception ex ) when ( ex is InvalidOperationException || ex is System . ComponentModel . Win32Exception )
156+ {
157+ log . LogWarning ( ex , "Terminate: Error checking HasExited for process {ProcessId}. Assuming exited." , processId ) ;
158+ alreadyExited = true ;
159+ }
160+
161+ if ( alreadyExited )
149162 {
150- log . LogDebug ( "GracefullyTerminateProcessInternal called with null process." ) ;
163+ log . LogDebug ( "Terminate: Process {ProcessId} already exited." , processId ) ;
151164 return ;
152165 }
153166
167+ log . LogDebug ( "Attempting graceful termination for process {ProcessId}" , processId ) ;
168+
154169 try
155170 {
156- bool alreadyExited = false ;
157- try
171+ if ( processToTerminate . StartInfo . RedirectStandardInput )
158172 {
159- alreadyExited = processToTerminate . HasExited ;
160- }
161- catch ( InvalidOperationException )
162- {
163- log . LogWarning ( "Error checking HasExited for process {ProcessId} (may already be disposed or inaccessible). Assuming exited." , processToTerminate . Id ) ;
164- alreadyExited = true ;
173+ log . LogDebug ( "Terminate: Attempting to send 'q' to stdin for process {ProcessId}" , processId ) ;
174+ try
175+ {
176+ processToTerminate . StandardInput . WriteLine ( "q" ) ;
177+ processToTerminate . StandardInput . Close ( ) ;
178+
179+ if ( processToTerminate . WaitForExit ( 1000 ) )
180+ {
181+ log . LogInformation ( "Process {ProcessId} terminated gracefully after sending 'q'." , processId ) ;
182+ return ;
183+ }
184+ log . LogDebug ( "Terminate: Process {ProcessId} did not exit after sending 'q' and 1s wait." , processId ) ;
185+ }
186+ catch ( InvalidOperationException ioex )
187+ {
188+ log . LogWarning ( ioex , "Terminate: Error accessing StandardInput for process {ProcessId} (likely already closed/exited)." , processId ) ;
189+ if ( processToTerminate . WaitForExit ( 50 ) )
190+ {
191+ log . LogInformation ( "Process {ProcessId} terminated shortly after stdin error." , processId ) ;
192+ return ;
193+ }
194+ }
195+ catch ( Exception ex )
196+ {
197+ log . LogError ( ex , "Terminate: Error sending 'q' to process {ProcessId}" , processId ) ;
198+ }
165199 }
166- catch ( System . ComponentModel . Win32Exception ex )
200+ else
167201 {
168- log . LogWarning ( ex , "Error checking HasExited for process {ProcessId}. Assuming exited." , processToTerminate . Id ) ;
169- alreadyExited = true ;
202+ log . LogWarning ( "Terminate: Cannot send 'q' to process {ProcessId}, StandardInput not redirected." , processId ) ;
170203 }
171204
172- if ( alreadyExited )
205+ if ( processToTerminate . HasExited )
173206 {
174- log . LogDebug ( "GracefullyTerminateProcessInternal : Process {ProcessId} already exited." , processToTerminate . Id ) ;
207+ log . LogDebug ( "Terminate : Process {ProcessId} exited before signal attempt ." , processId ) ;
175208 return ;
176209 }
177210
178- log . LogDebug ( "Attempting to gracefully terminate process {ProcessId}" , processToTerminate . Id ) ;
179-
180211 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
181212 {
182- log . LogDebug ( "Waiting for process {ProcessId} to exit (Windows)..." , processToTerminate . Id ) ;
183- if ( ! processToTerminate . WaitForExit ( 3000 ) )
184- {
185- log . LogWarning ( "Process {ProcessId} did not terminate gracefully after wait, forcing kill" , processToTerminate . Id ) ;
186- bool exitedBeforeKill = false ;
187- try { exitedBeforeKill = processToTerminate . HasExited ; } catch { }
188- if ( ! exitedBeforeKill )
189- {
190- processToTerminate . Kill ( true ) ;
191- }
192- }
193- else
213+ log . LogDebug ( "Terminate: Waiting up to 3s for process {ProcessId} to exit (Windows)..." , processId ) ;
214+ if ( processToTerminate . WaitForExit ( 3000 ) )
194215 {
195- log . LogDebug ( "Process {ProcessId} exited gracefully after wait (Windows)." , processToTerminate . Id ) ;
216+ log . LogInformation ( "Process {ProcessId} exited gracefully after wait (Windows)." , processId ) ;
217+ return ;
196218 }
219+ log . LogWarning ( "Process {ProcessId} did not terminate after wait, forcing kill (Windows)" , processId ) ;
220+ if ( ! processToTerminate . HasExited ) processToTerminate . Kill ( true ) ;
197221 }
198222 else
199223 {
200- log . LogDebug ( "Sending SIGTERM to process {ProcessId}..." , processToTerminate . Id ) ;
224+ log . LogDebug ( "Terminate: Sending SIGTERM to process {ProcessId}..." , processId ) ;
201225 Process . Start ( "kill" , $ "-TERM { processToTerminate . Id } ") ;
202- if ( ! processToTerminate . WaitForExit ( 3000 ) )
226+ if ( processToTerminate . WaitForExit ( 3000 ) )
203227 {
204- log . LogWarning ( "Process {ProcessId} did not terminate after SIGTERM, sending SIGKILL" , processToTerminate . Id ) ;
205- bool exitedBeforeKill = false ;
206- try { exitedBeforeKill = processToTerminate . HasExited ; } catch { }
207- if ( ! exitedBeforeKill )
208- {
209- log . LogDebug ( "Sending SIGKILL to process {ProcessId}..." , processToTerminate . Id ) ;
210- Process . Start ( "kill" , $ "-KILL { processToTerminate . Id } ") ;
211- processToTerminate . WaitForExit ( 500 ) ;
212- }
228+ log . LogInformation ( "Process {ProcessId} terminated after SIGTERM." , processId ) ;
229+ return ;
213230 }
214- else
231+ log . LogWarning ( "Process {ProcessId} did not terminate after SIGTERM, sending SIGKILL" , processId ) ;
232+ if ( ! processToTerminate . HasExited )
215233 {
216- log . LogDebug ( "Process {ProcessId} terminated after SIGTERM." , processToTerminate . Id ) ;
234+ log . LogDebug ( "Terminate: Sending SIGKILL to process {ProcessId}..." , processId ) ;
235+ Process . Start ( "kill" , $ "-KILL { processToTerminate . Id } ") ;
236+ processToTerminate . WaitForExit ( 500 ) ;
217237 }
218238 }
219239 }
220- catch ( InvalidOperationException ex )
221- {
222- log . LogWarning ( ex , "Error terminating process {ProcessId}. It might have already exited." , processToTerminate . Id ) ;
223- }
224- catch ( System . ComponentModel . Win32Exception ex )
225- {
226- log . LogError ( ex , "Win32Error during termination of process {ProcessId}" , processToTerminate . Id ) ;
227- }
228240 catch ( Exception ex )
229241 {
230- log . LogError ( ex , "Error gracefully terminating process {ProcessId}" , processToTerminate . Id ) ;
242+ log . LogError ( ex , "Error during signal/kill phase for process {ProcessId}" , processId ) ;
231243 try
232244 {
233- bool exitedBeforeKill = false ;
234- try { exitedBeforeKill = processToTerminate . HasExited ; } catch { }
235- if ( ! exitedBeforeKill )
245+ if ( ! processToTerminate . HasExited )
236246 {
237- log . LogWarning ( "Forcing kill on process {ProcessId} due to prior termination errors." , processToTerminate . Id ) ;
247+ log . LogWarning ( "Forcing final kill on process {ProcessId} due to termination errors." , processId ) ;
238248 processToTerminate . Kill ( true ) ;
239249 }
240250 }
241- catch ( Exception killEx )
242- {
243- log . LogError ( killEx , "Failed to force kill process {ProcessId}" , processToTerminate . Id ) ;
244- }
251+ catch ( Exception killEx ) { log . LogError ( killEx , "Failed final force kill for process {ProcessId}" , processId ) ; }
245252 }
246253 finally
247254 {
248255 try
249256 {
250257 bool finalExitCheck = false ;
251258 try { finalExitCheck = processToTerminate . HasExited ; } catch { }
252- if ( ! finalExitCheck )
253- {
254- log . LogWarning ( "Process {ProcessId} termination logic completed, but HasExited is still false." , processToTerminate . Id ) ;
255- }
256- else
257- {
258- log . LogDebug ( "Process {ProcessId} confirmed exited after termination logic." , processToTerminate . Id ) ;
259- }
259+ if ( ! finalExitCheck ) { log . LogWarning ( "Process {ProcessId} termination logic completed, but HasExited is still false." , processId ) ; }
260+ else { log . LogDebug ( "Process {ProcessId} confirmed exited after termination logic." , processId ) ; }
260261 }
261- catch { /* Ignore final state check errors */ }
262+ catch { }
262263 }
263264 }
264265
@@ -320,7 +321,6 @@ private void Process_Exited(Process? process, StreamWriter? writer)
320321
321322 private static string BuildCommand ( string command , string clientUserAgent , string streamUrl , int ? secondsIn )
322323 {
323- // (Implementation as before)
324324 string s = secondsIn . HasValue ? $ "-ss { secondsIn } " : "" ;
325325 command = command . Replace ( "{clientUserAgent}" , '"' + clientUserAgent + '"' )
326326 . Replace ( "{streamUrl}" , '"' + streamUrl + '"' ) ;
@@ -343,6 +343,7 @@ private static void ConfigureProcess(Process process, string commandExec, string
343343 process . StartInfo . UseShellExecute = false ;
344344 process . StartInfo . RedirectStandardOutput = true ;
345345 process . StartInfo . RedirectStandardError = true ;
346+ process . StartInfo . RedirectStandardInput = true ;
346347 process . StartInfo . StandardOutputEncoding = Encoding . UTF8 ;
347348 process . StartInfo . StandardErrorEncoding = Encoding . UTF8 ;
348349 process . StartInfo . WindowStyle = ProcessWindowStyle . Hidden ;
0 commit comments