@@ -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,19 @@ 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 (errno=0)
235+ let _ = write ! ( pipe, "SubprocessError:0:{}" , ctx. as_msg( ) ) ;
236+ } else {
237+ // errno is written in hex format
238+ let _ = write ! ( pipe, "OSError:{:x}:{}" , e as i32 , ctx. as_msg( ) ) ;
239+ }
237240 std:: process:: exit ( 255 )
238241 }
239242 }
@@ -242,6 +245,7 @@ fn exec(args: &ForkExecArgs<'_>, procargs: ProcArgs<'_>) -> ! {
242245enum ExecErrorContext {
243246 NoExec ,
244247 ChDir ,
248+ PreExec ,
245249 Exec ,
246250}
247251
@@ -250,6 +254,7 @@ impl ExecErrorContext {
250254 match self {
251255 Self :: NoExec => "noexec" ,
252256 Self :: ChDir => "noexec:chdir" ,
257+ Self :: PreExec => "Exception occurred in preexec_fn." ,
253258 Self :: Exec => "" ,
254259 }
255260 }
@@ -259,6 +264,7 @@ fn exec_inner(
259264 args : & ForkExecArgs < ' _ > ,
260265 procargs : ProcArgs < ' _ > ,
261266 ctx : & mut ExecErrorContext ,
267+ vm : & VirtualMachine ,
262268) -> nix:: Result < Never > {
263269 for & fd in args. fds_to_keep . as_slice ( ) {
264270 if fd. as_raw_fd ( ) != args. errpipe_write . as_raw_fd ( ) {
@@ -345,6 +351,18 @@ fn exec_inner(
345351 nix:: Error :: result ( ret) ?;
346352 }
347353
354+ // Call preexec_fn after all process setup but before closing FDs
355+ if let Some ( ref preexec_fn) = args. preexec_fn {
356+ match preexec_fn. call ( ( ) , vm) {
357+ Ok ( _) => { }
358+ Err ( _e) => {
359+ // Cannot safely stringify exception after fork
360+ * ctx = ExecErrorContext :: PreExec ;
361+ return Err ( Errno :: UnknownErrno ) ;
362+ }
363+ }
364+ }
365+
348366 * ctx = ExecErrorContext :: Exec ;
349367
350368 if args. close_fds {
0 commit comments