@@ -2,7 +2,7 @@ use std::process::ExitCode;
22use std:: time:: Duration ;
33
44use clap:: Parser ;
5- use tokio:: process:: Command ;
5+ use tokio:: process:: { Child , Command } ;
66use tokio:: time:: { sleep, Instant } ;
77
88#[ derive( Parser ) ]
@@ -88,15 +88,24 @@ async fn wait_for_server(url: &str, timeout: Duration) -> bool {
8888 false
8989}
9090
91- #[ cfg( unix) ]
92- fn kill_process_group ( id : u32 ) {
93- unsafe {
94- libc:: kill ( -( id as i32 ) , libc:: SIGTERM ) ;
91+ /// Terminates the server and all its descendant processes.
92+ ///
93+ /// The server is started via `sh -c "..."`, producing a process tree
94+ /// (sh -> cargo run -> server binary). `Child::kill()` alone would only
95+ /// kill `sh`, orphaning the actual server process on the port. On Unix we
96+ /// use process groups (set up via `process_group(0)` at spawn time) so a
97+ /// single `kill(-pgid, SIGTERM)` reaches the entire tree.
98+ fn shutdown_server ( server : & mut Child ) {
99+ #[ cfg( unix) ]
100+ if let Some ( id) = server. id ( ) {
101+ unsafe {
102+ libc:: kill ( -( id as i32 ) , libc:: SIGTERM ) ;
103+ }
104+ return ;
95105 }
96- }
97106
98- # [ cfg ( not ( unix ) ) ]
99- fn kill_process_group ( _id : u32 ) { }
107+ let _ = server . start_kill ( ) ;
108+ }
100109
101110#[ tokio:: main]
102111async fn main ( ) -> ExitCode {
@@ -116,17 +125,10 @@ async fn main() -> ExitCode {
116125 cmd. stdout ( std:: process:: Stdio :: inherit ( ) ) ;
117126 cmd. stderr ( std:: process:: Stdio :: inherit ( ) ) ;
118127 #[ cfg( unix) ]
119- unsafe {
120- cmd. pre_exec ( || {
121- libc:: setpgid ( 0 , 0 ) ;
122- Ok ( ( ) )
123- } ) ;
124- }
128+ cmd. process_group ( 0 ) ;
125129 cmd. spawn ( ) . expect ( "failed to start server process" )
126130 } ;
127131
128- let server_pid = server. id ( ) . expect ( "server has no pid" ) ;
129-
130132 eprintln ! ( "[ssr-e2e] Waiting for server at {} ..." , args. health_url) ;
131133
132134 let ready = wait_for_server ( & args. health_url , Duration :: from_secs ( args. timeout ) ) . await ;
@@ -135,7 +137,7 @@ async fn main() -> ExitCode {
135137 "[ssr-e2e] Server did not become ready within {}s" ,
136138 args. timeout
137139 ) ;
138- kill_process_group ( server_pid ) ;
140+ shutdown_server ( & mut server ) ;
139141 let _ = server. wait ( ) . await ;
140142 return ExitCode :: FAILURE ;
141143 }
@@ -159,7 +161,7 @@ async fn main() -> ExitCode {
159161 . await ;
160162
161163 eprintln ! ( "[ssr-e2e] Shutting down server ..." ) ;
162- kill_process_group ( server_pid ) ;
164+ shutdown_server ( & mut server ) ;
163165 let _ = server. wait ( ) . await ;
164166
165167 match test_result {
0 commit comments