File tree Expand file tree Collapse file tree 2 files changed +38
-0
lines changed Expand file tree Collapse file tree 2 files changed +38
-0
lines changed Original file line number Diff line number Diff line change @@ -24,6 +24,19 @@ pub trait CapStdExtCommandExt {
24
24
25
25
/// Use the given directory as the current working directory for the process.
26
26
fn cwd_dir ( & mut self , dir : Dir ) -> & mut Self ;
27
+
28
+ /// On Linux, arrange for [`SIGTERM`] to be delivered to the child if the
29
+ /// parent *thread* exits. This helps avoid leaking child processes if
30
+ /// the parent crashes for example.
31
+ ///
32
+ /// # IMPORTANT
33
+ ///
34
+ /// Due to the semantics of <https://man7.org/linux/man-pages/man2/prctl.2.html> this
35
+ /// will cause the child to exit when the parent *thread* (not process) exits. In
36
+ /// particular this can become problematic when used with e.g. a threadpool such
37
+ /// as Tokio's <https://kobzol.github.io/rust/2025/02/23/tokio-plus-prctl-equals-nasty-bug.html>.
38
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
39
+ fn lifecycle_bind_to_parent_thread ( & mut self ) -> & mut Self ;
27
40
}
28
41
29
42
#[ allow( unsafe_code) ]
@@ -58,6 +71,20 @@ impl CapStdExtCommandExt for std::process::Command {
58
71
}
59
72
self
60
73
}
74
+
75
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
76
+ fn lifecycle_bind_to_parent_thread ( & mut self ) -> & mut Self {
77
+ // SAFETY: This API is safe to call in a forked child.
78
+ unsafe {
79
+ self . pre_exec ( || {
80
+ rustix:: process:: set_parent_process_death_signal ( Some (
81
+ rustix:: process:: Signal :: TERM ,
82
+ ) )
83
+ . map_err ( Into :: into)
84
+ } ) ;
85
+ }
86
+ self
87
+ }
61
88
}
62
89
63
90
#[ cfg( test) ]
Original file line number Diff line number Diff line change @@ -791,3 +791,14 @@ fn test_big_xattr() -> Result<()> {
791
791
792
792
Ok ( ( ) )
793
793
}
794
+
795
+ #[ test]
796
+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
797
+ fn test_lifecycle_bind_to_parent_thread ( ) -> Result < ( ) > {
798
+ let status = Command :: new ( "true" )
799
+ . lifecycle_bind_to_parent_thread ( )
800
+ . status ( ) ?;
801
+ assert ! ( status. success( ) ) ;
802
+
803
+ Ok ( ( ) )
804
+ }
You can’t perform that action at this time.
0 commit comments