@@ -33,9 +33,6 @@ mod _posixsubprocess {
3333
3434 #[ pyfunction]
3535 fn fork_exec ( args : ForkExecArgs < ' _ > , vm : & VirtualMachine ) -> PyResult < libc:: pid_t > {
36- if args. preexec_fn . is_some ( ) {
37- return Err ( vm. new_not_implemented_error ( "preexec_fn not supported yet" ) ) ;
38- }
3936 let extra_groups = args
4037 . groups_list
4138 . as_ref ( )
@@ -49,7 +46,7 @@ mod _posixsubprocess {
4946 extra_groups : extra_groups. as_deref ( ) ,
5047 } ;
5148 match unsafe { nix:: unistd:: fork ( ) } . map_err ( |err| err. into_pyexception ( vm) ) ? {
52- nix:: unistd:: ForkResult :: Child => exec ( & args, procargs) ,
49+ nix:: unistd:: ForkResult :: Child => exec ( & args, procargs, vm ) ,
5350 nix:: unistd:: ForkResult :: Parent { child } => Ok ( child. as_raw ( ) ) ,
5451 }
5552 }
@@ -227,13 +224,18 @@ struct ProcArgs<'a> {
227224 extra_groups : Option < & ' a [ Gid ] > ,
228225}
229226
230- fn exec ( args : & ForkExecArgs < ' _ > , procargs : ProcArgs < ' _ > ) -> ! {
227+ fn exec ( args : & ForkExecArgs < ' _ > , procargs : ProcArgs < ' _ > , vm : & VirtualMachine ) -> ! {
231228 let mut ctx = ExecErrorContext :: NoExec ;
232- match exec_inner ( args, procargs, & mut ctx) {
229+ match exec_inner ( args, procargs, & mut ctx, vm ) {
233230 Ok ( x) => match x { } ,
234231 Err ( e) => {
235232 let mut pipe = args. errpipe_write ;
236- let _ = write ! ( pipe, "OSError:{}:{}" , e as i32 , ctx. as_msg( ) ) ;
233+ if matches ! ( ctx, ExecErrorContext :: PreExec ) {
234+ // For preexec_fn errors, use SubprocessError format like CPython
235+ let _ = write ! ( pipe, "SubprocessError:0:{}" , ctx. as_msg( ) ) ;
236+ } else {
237+ let _ = write ! ( pipe, "OSError:{}:{}" , e as i32 , ctx. as_msg( ) ) ;
238+ }
237239 std:: process:: exit ( 255 )
238240 }
239241 }
@@ -242,6 +244,7 @@ fn exec(args: &ForkExecArgs<'_>, procargs: ProcArgs<'_>) -> ! {
242244enum ExecErrorContext {
243245 NoExec ,
244246 ChDir ,
247+ PreExec ,
245248 Exec ,
246249}
247250
@@ -250,6 +253,7 @@ impl ExecErrorContext {
250253 match self {
251254 Self :: NoExec => "noexec" ,
252255 Self :: ChDir => "noexec:chdir" ,
256+ Self :: PreExec => "Exception occurred in preexec_fn." ,
253257 Self :: Exec => "" ,
254258 }
255259 }
@@ -259,6 +263,7 @@ fn exec_inner(
259263 args : & ForkExecArgs < ' _ > ,
260264 procargs : ProcArgs < ' _ > ,
261265 ctx : & mut ExecErrorContext ,
266+ vm : & VirtualMachine ,
262267) -> nix:: Result < Never > {
263268 for & fd in args. fds_to_keep . as_slice ( ) {
264269 if fd. as_raw_fd ( ) != args. errpipe_write . as_raw_fd ( ) {
@@ -345,6 +350,18 @@ fn exec_inner(
345350 nix:: Error :: result ( ret) ?;
346351 }
347352
353+ // Call preexec_fn after all process setup but before closing FDs
354+ if let Some ( ref preexec_fn) = args. preexec_fn {
355+ match preexec_fn. call ( ( ) , vm) {
356+ Ok ( _) => { }
357+ Err ( _e) => {
358+ // Cannot safely stringify exception after fork
359+ * ctx = ExecErrorContext :: PreExec ;
360+ return Err ( Errno :: UnknownErrno ) ;
361+ }
362+ }
363+ }
364+
348365 * ctx = ExecErrorContext :: Exec ;
349366
350367 if args. close_fds {
0 commit comments