@@ -35,7 +35,7 @@ use crate::tunnels::shutdown_signal::ShutdownRequest;
3535use crate :: update_service:: {
3636 unzip_downloaded_release, Platform , Release , TargetKind , UpdateService ,
3737} ;
38- use crate :: util:: command:: new_script_command;
38+ use crate :: util:: command:: { kill_tree , new_script_command} ;
3939use crate :: util:: errors:: AnyError ;
4040use crate :: util:: http:: { self , ReqwestSimpleHttp } ;
4141use crate :: util:: io:: SilentCopyProgress ;
@@ -938,7 +938,22 @@ impl ConnectionManager {
938938 }
939939 _ = & mut kill_timer => {
940940 info!( args. log, "[{} process]: idle timeout reached, ending" , commit_prefix) ;
941- let _ = child. kill( ) . await ;
941+ // The entrypoint is a shell/cmd shim, so kill the full tree
942+ // to avoid orphaning the Node server it launches.
943+ if let Some ( pid) = child. id( ) {
944+ let _ = kill_tree( pid) . await ;
945+ }
946+ const REAP_TIMEOUT : Duration = Duration :: from_secs( 5 ) ;
947+ if tokio:: time:: timeout( REAP_TIMEOUT , child. wait( ) ) . await . is_err( ) {
948+ warning!(
949+ args. log,
950+ "[{} process]: server did not exit within {}s after kill_tree; escalating to force kill" ,
951+ commit_prefix,
952+ REAP_TIMEOUT . as_secs( )
953+ ) ;
954+ let _ = child. kill( ) . await ;
955+ let _ = child. wait( ) . await ;
956+ }
942957 break ;
943958 }
944959 e = child. wait( ) => {
0 commit comments