@@ -57,6 +57,12 @@ pub const ChildProcess = struct {
5757
5858 expand_arg0 : Arg0Expand ,
5959
60+ /// Use this for additional posix spawn attributes.
61+ /// Do not set spawn attribute flags and do not modify stdin, stdout, stderr
62+ /// behavior, because those are set in the platform-specific spawn method.
63+ posix_attr : if (builtin .target .isDarwin ()) ? os .posix_spawn .Attr else void ,
64+ posix_action : if (builtin .target .isDarwin ()) ? os .posix_spawn .Actions else void ,
65+
6066 /// Darwin-only. Disable ASLR for the child process.
6167 disable_aslr : bool = false ,
6268
@@ -100,6 +106,16 @@ pub const ChildProcess = struct {
100106 Close ,
101107 };
102108
109+ pub const PipeDirection = enum {
110+ parent_to_child ,
111+ child_to_parent ,
112+ };
113+
114+ pub const PosixData = union (enum ) {
115+ Attribute : os.posix_spawn.Attr ,
116+ Actions : os.posix_spawn.Actions ,
117+ };
118+
103119 /// First argument in argv is the executable.
104120 pub fn init (argv : []const []const u8 , allocator : mem.Allocator ) ChildProcess {
105121 return .{
@@ -121,6 +137,8 @@ pub const ChildProcess = struct {
121137 .stdout_behavior = StdIo .Inherit ,
122138 .stderr_behavior = StdIo .Inherit ,
123139 .expand_arg0 = .no_expand ,
140+ .posix_attr = if (comptime builtin .target .isDarwin ()) null else undefined ,
141+ .posix_action = if (comptime builtin .target .isDarwin ()) null else undefined ,
124142 };
125143 }
126144
@@ -548,6 +566,10 @@ pub const ChildProcess = struct {
548566 }
549567
550568 fn spawnMacos (self : * ChildProcess ) SpawnError ! void {
569+ // cleanup user-initialization and initialization in this function
570+ defer if (self .posix_attr != null ) self .posix_attr .? .deinit ();
571+ defer if (self .posix_actions != null ) self .posix_actions .? .deinit ();
572+
551573 const pipe_flags = if (io .is_async ) os .O .NONBLOCK else 0 ;
552574 const stdin_pipe = if (self .stdin_behavior == StdIo .Pipe ) try os .pipe2 (pipe_flags ) else undefined ;
553575 errdefer if (self .stdin_behavior == StdIo .Pipe ) destroyPipe (stdin_pipe );
@@ -575,28 +597,28 @@ pub const ChildProcess = struct {
575597 undefined ;
576598 defer if (any_ignore ) os .close (dev_null_fd );
577599
578- var attr = try os . posix_spawn . Attr . init ();
579- defer attr . deinit ();
600+ if ( self . posix_attr == null )
601+ self . posix_attr = try os . posix_spawn . Attr . init ();
580602 var flags : u16 = os .darwin .POSIX_SPAWN_SETSIGDEF | os .darwin .POSIX_SPAWN_SETSIGMASK ;
581603 if (self .disable_aslr ) {
582604 flags |= os .darwin ._POSIX_SPAWN_DISABLE_ASLR ;
583605 }
584606 if (self .start_suspended ) {
585607 flags |= os .darwin .POSIX_SPAWN_START_SUSPENDED ;
586608 }
587- try attr .set (flags );
609+ try self . posix_attr .? .set (flags );
588610
589- var actions = try os . posix_spawn . Actions . init ();
590- defer actions . deinit ();
611+ if ( self . posix_actions == null )
612+ self . posix_actions = try os . posix_spawn . Actions . init ();
591613
592- try setUpChildIoPosixSpawn (self .stdin_behavior , & actions , stdin_pipe , os .STDIN_FILENO , dev_null_fd );
593- try setUpChildIoPosixSpawn (self .stdout_behavior , & actions , stdout_pipe , os .STDOUT_FILENO , dev_null_fd );
594- try setUpChildIoPosixSpawn (self .stderr_behavior , & actions , stderr_pipe , os .STDERR_FILENO , dev_null_fd );
614+ try setUpChildIoPosixSpawn (self .stdin_behavior , & self . posix_actions .? , stdin_pipe , os .STDIN_FILENO , dev_null_fd );
615+ try setUpChildIoPosixSpawn (self .stdout_behavior , & self . posix_actions .? , stdout_pipe , os .STDOUT_FILENO , dev_null_fd );
616+ try setUpChildIoPosixSpawn (self .stderr_behavior , & self . posix_actions .? , stderr_pipe , os .STDERR_FILENO , dev_null_fd );
595617
596618 if (self .cwd_dir ) | cwd | {
597- try actions .fchdir (cwd .fd );
619+ try self . posix_actions .? .fchdir (cwd .fd );
598620 } else if (self .cwd ) | cwd | {
599- try actions .chdir (cwd );
621+ try self . posix_actions .? .chdir (cwd );
600622 }
601623
602624 var arena_allocator = std .heap .ArenaAllocator .init (self .allocator );
@@ -611,7 +633,7 @@ pub const ChildProcess = struct {
611633 break :m envp_buf .ptr ;
612634 } else std .c .environ ;
613635
614- const pid = try os .posix_spawn .spawnp (self .argv [0 ], actions , attr , argv_buf , envp );
636+ const pid = try os .posix_spawn .spawnp (self .argv [0 ], self . posix_actions .? , self . posix_attr .? , argv_buf , envp );
615637
616638 if (self .stdin_behavior == StdIo .Pipe ) {
617639 self .stdin = File { .handle = stdin_pipe [1 ] };
@@ -876,7 +898,12 @@ pub const ChildProcess = struct {
876898 var g_hChildStd_OUT_Wr : ? windows.HANDLE = null ;
877899 switch (self .stdout_behavior ) {
878900 StdIo .Pipe = > {
879- try windowsMakeAsyncPipe (& g_hChildStd_OUT_Rd , & g_hChildStd_OUT_Wr , & saAttr );
901+ try windowsMakeAsyncPipe (
902+ & g_hChildStd_OUT_Rd ,
903+ & g_hChildStd_OUT_Wr ,
904+ & saAttr ,
905+ PipeDirection .child_to_parent ,
906+ );
880907 },
881908 StdIo .Ignore = > {
882909 g_hChildStd_OUT_Wr = nul_handle ;
@@ -896,7 +923,12 @@ pub const ChildProcess = struct {
896923 var g_hChildStd_ERR_Wr : ? windows.HANDLE = null ;
897924 switch (self .stderr_behavior ) {
898925 StdIo .Pipe = > {
899- try windowsMakeAsyncPipe (& g_hChildStd_ERR_Rd , & g_hChildStd_ERR_Wr , & saAttr );
926+ try windowsMakeAsyncPipe (
927+ & g_hChildStd_ERR_Rd ,
928+ & g_hChildStd_ERR_Wr ,
929+ & saAttr ,
930+ PipeDirection .child_to_parent ,
931+ );
900932 },
901933 StdIo .Ignore = > {
902934 g_hChildStd_ERR_Wr = nul_handle ;
@@ -1324,23 +1356,18 @@ fn windowsCreateProcessPathExt(
13241356}
13251357
13261358fn windowsCreateProcess (app_name : [* :0 ]u16 , cmd_line : [* :0 ]u16 , envp_ptr : ? [* ]u16 , cwd_ptr : ? [* :0 ]u16 , lpStartupInfo : * windows.STARTUPINFOW , lpProcessInformation : * windows.PROCESS_INFORMATION ) ! void {
1327- // TODO the docs for environment pointer say:
1328- // > A pointer to the environment block for the new process. If this parameter
1329- // > is NULL, the new process uses the environment of the calling process.
1330- // > ...
1331- // > An environment block can contain either Unicode or ANSI characters. If
1332- // > the environment block pointed to by lpEnvironment contains Unicode
1333- // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
1334- // > If this parameter is NULL and the environment block of the parent process
1335- // > contains Unicode characters, you must also ensure that dwCreationFlags
1336- // > includes CREATE_UNICODE_ENVIRONMENT.
1337- // This seems to imply that we have to somehow know whether our process parent passed
1338- // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
1339- // Since we do not know this information that would imply that we must not pass NULL
1340- // for the parameter.
1341- // However this would imply that programs compiled with -DUNICODE could not pass
1342- // environment variables to programs that were not, which seems unlikely.
1343- // More investigation is needed.
1359+ // See https://stackoverflow.com/a/4207169/9306292
1360+ // One can manually write in unicode even if one doesn't compile in unicode
1361+ // (-DUNICODE).
1362+ // Thus CREATE_UNICODE_ENVIRONMENT, according to how one constructed the
1363+ // environment block, is necessary, since CreateProcessA and CreateProcessW may
1364+ // work with either Ansi or Unicode.
1365+ // * The environment variables can still be inherited from parent process,
1366+ // if set to NULL
1367+ // * The OS can for an unspecified environment block not figure out,
1368+ // if it is Unicode or ANSI.
1369+ // * Applications may break without specification of the environment variable
1370+ // due to inability of Windows to check (+translate) the character encodings.
13441371 return windows .CreateProcessW (
13451372 app_name ,
13461373 cmd_line ,
@@ -1471,7 +1498,16 @@ fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const w
14711498
14721499var pipe_name_counter = std .atomic .Atomic (u32 ).init (1 );
14731500
1474- fn windowsMakeAsyncPipe (rd : * ? windows.HANDLE , wr : * ? windows.HANDLE , sattr : * const windows.SECURITY_ATTRIBUTES ) ! void {
1501+ /// Create asynchronous pipe.
1502+ /// To enable inheritance between parent and child process, set bInheritHandle = windows.TRUE
1503+ /// in the security attributes.
1504+ /// Direction defines which handle is updated (to enable inheritance by ChildProcess)
1505+ pub fn windowsMakeAsyncPipe (
1506+ rd : * ? windows.HANDLE ,
1507+ wr : * ? windows.HANDLE ,
1508+ sattr : * const windows.SECURITY_ATTRIBUTES ,
1509+ direction : ChildProcess.PipeDirection ,
1510+ ) ! void {
14751511 var tmp_bufw : [128 ]u16 = undefined ;
14761512
14771513 // Anonymous pipes are built upon Named pipes.
@@ -1526,8 +1562,10 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
15261562 }
15271563 errdefer os .close (write_handle );
15281564
1529- try windows .SetHandleInformation (read_handle , windows .HANDLE_FLAG_INHERIT , 0 );
1530-
1565+ switch (direction ) {
1566+ .child_to_parent = > try windows .SetHandleInformation (read_handle , windows .HANDLE_FLAG_INHERIT , windows .HANDLE_FLAG_INHERIT ),
1567+ .parent_to_child = > try windows .SetHandleInformation (write_handle , windows .HANDLE_FLAG_INHERIT , windows .HANDLE_FLAG_INHERIT ),
1568+ }
15311569 rd .* = read_handle ;
15321570 wr .* = write_handle ;
15331571}
0 commit comments