1+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
2+
13use portable_pty:: { Child , MasterPty } ;
24
35use super :: types:: TerminalServiceError ;
46
57pub struct ProcessTreeGuard {
8+ terminated : AtomicBool ,
69 #[ cfg( windows) ]
710 job : windows_sys:: Win32 :: Foundation :: HANDLE ,
811 #[ cfg( unix) ]
@@ -25,7 +28,10 @@ impl ProcessTreeGuard {
2528 let process_group = master
2629 . process_group_leader ( )
2730 . ok_or ( TerminalServiceError :: SpawnFailed ( "process_group" ) ) ?;
28- Ok ( Self { process_group } )
31+ Ok ( Self {
32+ terminated : AtomicBool :: new ( false ) ,
33+ process_group,
34+ } )
2935 }
3036 #[ cfg( not( any( windows, unix) ) ) ]
3137 {
@@ -69,18 +75,32 @@ impl ProcessTreeGuard {
6975 unsafe { CloseHandle ( job) } ;
7076 return Err ( TerminalServiceError :: SpawnFailed ( "job_assign" ) ) ;
7177 }
72- Ok ( Self { job } )
78+ Ok ( Self {
79+ terminated : AtomicBool :: new ( false ) ,
80+ job,
81+ } )
7382 }
7483
7584 pub fn terminate ( & self ) {
85+ if self . terminated . swap ( true , Ordering :: AcqRel ) {
86+ return ;
87+ }
7688 #[ cfg( windows) ]
7789 unsafe {
7890 windows_sys:: Win32 :: System :: JobObjects :: TerminateJobObject ( self . job , 1 ) ;
7991 }
8092 #[ cfg( unix) ]
81- unsafe {
82- libc:: kill ( -self . process_group , libc:: SIGHUP ) ;
83- libc:: kill ( -self . process_group , libc:: SIGKILL ) ;
93+ {
94+ // Interactive shells can place background jobs in their own process
95+ // groups within the PTY session. Give the shell a brief chance to
96+ // forward SIGHUP to those jobs before force-killing its own group.
97+ unsafe {
98+ libc:: kill ( -self . process_group , libc:: SIGHUP ) ;
99+ }
100+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 150 ) ) ;
101+ unsafe {
102+ libc:: kill ( -self . process_group , libc:: SIGKILL ) ;
103+ }
84104 }
85105 }
86106}
0 commit comments