Skip to content

Commit b8b6875

Browse files
committed
more cleanups, windows allows an explicit list of file handles
Thus use the allocating posix spawn logic within fork to close the child end of the pipe.
1 parent 8c14bb8 commit b8b6875

File tree

4 files changed

+49
-45
lines changed

4 files changed

+49
-45
lines changed

lib/std/child_process.zig

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ pub const ChildProcess = struct {
6060
/// Use this for additional posix spawn attributes.
6161
/// Do not set spawn attribute flags and do not modify stdin, stdout, stderr
6262
/// 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_actions: if (builtin.target.isDarwin()) ?os.posix_spawn.Actions else void,
63+
posix_attr: if (os.hasPosixSpawn) ?os.posix_spawn.Attr else void,
64+
posix_actions: if (os.hasPosixSpawn) ?os.posix_spawn.Actions else void,
6565

6666
/// Darwin-only. Disable ASLR for the child process.
6767
disable_aslr: bool = false,
@@ -111,11 +111,6 @@ pub const ChildProcess = struct {
111111
child_to_parent,
112112
};
113113

114-
pub const PosixData = union(enum) {
115-
Attribute: os.posix_spawn.Attr,
116-
Actions: os.posix_spawn.Actions,
117-
};
118-
119114
/// First argument in argv is the executable.
120115
pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
121116
return .{
@@ -137,8 +132,8 @@ pub const ChildProcess = struct {
137132
.stdout_behavior = StdIo.Inherit,
138133
.stderr_behavior = StdIo.Inherit,
139134
.expand_arg0 = .no_expand,
140-
.posix_attr = if (comptime builtin.target.isDarwin()) null else undefined,
141-
.posix_actions = if (comptime builtin.target.isDarwin()) null else undefined,
135+
.posix_attr = if (os.hasPosixSpawn) null else undefined,
136+
.posix_actions = if (os.hasPosixSpawn) null else undefined,
142137
};
143138
}
144139

@@ -154,7 +149,7 @@ pub const ChildProcess = struct {
154149
@compileError("the target operating system cannot spawn processes");
155150
}
156151

157-
if (comptime builtin.target.isDarwin()) {
152+
if (os.hasPosixSpawn) {
158153
return self.spawnMacos();
159154
}
160155

@@ -486,7 +481,7 @@ pub const ChildProcess = struct {
486481
}
487482

488483
fn waitUnwrapped(self: *ChildProcess) !void {
489-
const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
484+
const res: os.WaitPidResult = if (os.hasPosixSpawn)
490485
try os.posix_spawn.waitpid(self.pid, 0)
491486
else
492487
os.waitpid(self.pid, 0);
@@ -566,9 +561,9 @@ pub const ChildProcess = struct {
566561
}
567562

568563
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();
564+
// dont cleanup structure owned == initialized by user
565+
const user_attr: bool = self.posix_attr != null;
566+
const user_actions: bool = self.posix_actions != null;
572567

573568
const pipe_flags = if (io.is_async) os.O.NONBLOCK else 0;
574569
const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
@@ -597,8 +592,8 @@ pub const ChildProcess = struct {
597592
undefined;
598593
defer if (any_ignore) os.close(dev_null_fd);
599594

600-
if (self.posix_attr == null)
601-
self.posix_attr = try os.posix_spawn.Attr.init();
595+
if (user_attr == false) self.posix_attr = try os.posix_spawn.Attr.init();
596+
defer if (user_attr == false) self.posix_attr.?.deinit();
602597
var flags: u16 = os.darwin.POSIX_SPAWN_SETSIGDEF | os.darwin.POSIX_SPAWN_SETSIGMASK;
603598
if (self.disable_aslr) {
604599
flags |= os.darwin._POSIX_SPAWN_DISABLE_ASLR;
@@ -608,8 +603,8 @@ pub const ChildProcess = struct {
608603
}
609604
try self.posix_attr.?.set(flags);
610605

611-
if (self.posix_actions == null)
612-
self.posix_actions = try os.posix_spawn.Actions.init();
606+
if (user_actions == false) self.posix_actions = try os.posix_spawn.Actions.init();
607+
defer if (user_actions == false) self.posix_actions.?.deinit();
613608

614609
try setUpChildIoPosixSpawn(self.stdin_behavior, &self.posix_actions.?, stdin_pipe, os.STDIN_FILENO, dev_null_fd);
615610
try setUpChildIoPosixSpawn(self.stdout_behavior, &self.posix_actions.?, stdout_pipe, os.STDOUT_FILENO, dev_null_fd);
@@ -947,6 +942,9 @@ pub const ChildProcess = struct {
947942
const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
948943
defer self.allocator.free(cmd_line);
949944

945+
// TODO use explicit list of file handles
946+
// https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873
947+
950948
var siStartInfo = windows.STARTUPINFOW{
951949
.cb = @sizeOf(windows.STARTUPINFOW),
952950
.hStdError = g_hChildStd_ERR_Wr,

lib/std/os.zig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub const windows = @import("os/windows.zig");
4444
pub const posix_spawn = @import("os/posix_spawn.zig");
4545
pub const ptrace = @import("os/ptrace.zig");
4646

47+
// TODO where should we add our own target infos for things that we have bindings for?
48+
pub const hasPosixSpawn = builtin.target.isDarwin();
49+
4750
comptime {
4851
assert(@import("std") == std); // std lib tests require --zig-lib-dir
4952
}
@@ -4843,6 +4846,18 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
48434846
}
48444847
}
48454848

4849+
const EnableFileInheritanceError = FcntlError || windows.SetHandleInformationError;
4850+
4851+
pub inline fn enableFileInheritance(file_handle: fd_t) EnableFileInheritanceError!void {
4852+
if (builtin.os.tag == .windows) {
4853+
try windows.SetHandleInformation(file_handle, windows.HANDLE_FLAG_INHERIT, 1);
4854+
} else {
4855+
var flags = fcntl(file_handle, F.GETFD, 0);
4856+
flags &= ~@as(u32, FD_CLOEXEC);
4857+
_ = try fcntl(file_handle, F.SETFD, flags);
4858+
}
4859+
}
4860+
48464861
const DisableFileInheritanceError = FcntlError || windows.SetHandleInformationError;
48474862

48484863
pub inline fn disableFileInheritance(file_handle: fd_t) DisableFileInheritanceError!void {

test/standalone/childprocess_extrapipe/child.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub fn main() !void {
1919
break :file_handle try std.fmt.parseInt(std.os.fd_t, s_handle, 10);
2020
}
2121
};
22+
// TODO: Is there a way on Windows to let the Kernel disable inheritance
23+
// after it is inherited in CreateProcess???
2224
if (builtin.target.os.tag == .windows) {
2325
// windows.HANDLE_FLAG_INHERIT is enabled
2426
var handle_flags: windows.DWORD = undefined;

test/standalone/childprocess_extrapipe/parent.zig

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ pub fn main() !void {
3939
.bInheritHandle = windows.TRUE,
4040
.lpSecurityDescriptor = null,
4141
};
42+
// create pipe and enable inheritance for the read end, which will be given to the child
4243
try child_process.windowsMakeAsyncPipe(&pipe[0], &pipe[1], &saAttr, .parent_to_child);
4344
} else {
44-
pipe = try os.pipe(); // leaks on default
45+
pipe = try os.pipe(); // leaks on default, but more portable, TODO: use pip2 and close earlier?
4546
}
4647

47-
// write pipe to string + add to command
48+
// write read side of pipe to string + add to spawn command
4849
var buf: [handleCharSize]u8 = comptime [_]u8{0} ** handleCharSize;
49-
5050
const s_handle =
5151
if (builtin.os.tag == .windows)
52-
try handleToString(pipe[0].?, &buf) // read side of pipe
52+
try handleToString(pipe[0].?, &buf)
5353
else
5454
try handleToString(pipe[0], &buf);
5555

@@ -58,34 +58,24 @@ pub fn main() !void {
5858
gpa,
5959
);
6060
{
61-
// close read side of pipe
62-
if (comptime builtin.target.isDarwin()) {
63-
{
64-
// less time for leaking the file descriptor, if it is closed immediately
65-
child_proc.posix_actions = try os.posix_spawn.Actions.init();
66-
errdefer child_proc.posix_actions.?.deinit();
67-
try child_proc.posix_actions.?.close(pipe[1]);
68-
errdefer child_proc.posix_actions.?.deinit();
69-
}
70-
}
71-
defer if (comptime !builtin.target.isDarwin()) {
72-
if (builtin.os.tag == .windows) {
73-
os.close(pipe[0].?);
74-
} else {
75-
os.close(pipe[0]);
76-
}
77-
};
61+
// close read side of pipe, less time for leaking, if closed immediately
62+
if (os.hasPosixSpawn) child_proc.posix_actions = try os.posix_spawn.Actions.init();
63+
defer if (os.hasPosixSpawn) child_proc.posix_actions.?.deinit();
64+
if (os.hasPosixSpawn) try child_proc.posix_actions.?.close(pipe[1]); // TODO: This is not closed in child
65+
defer if (builtin.os.tag == .windows)
66+
os.close(pipe[0].?)
67+
else
68+
os.close(pipe[0]);
7869

7970
try child_proc.spawn();
8071
}
8172

82-
if (builtin.os.tag == .windows) {
83-
try std.os.disableFileInheritance(pipe[1].?);
84-
} else {
73+
// call fcntl on Unixes to disable handle inheritance
74+
if (builtin.os.tag != .windows) {
8575
try std.os.disableFileInheritance(pipe[1]);
8676
}
8777

88-
// check that disableFileInheritance was successful
78+
// windows does have inheritance disabled on default, but we check to be sure
8979
if (builtin.target.os.tag == .windows) {
9080
var handle_flags: windows.DWORD = undefined;
9181
try windows.GetHandleInformation(pipe[1].?, &handle_flags);
@@ -95,13 +85,12 @@ pub fn main() !void {
9585
try std.testing.expect((fcntl_flags & os.FD_CLOEXEC) != 0);
9686
}
9787

98-
// do we want another extra prong for darwin?
9988
var file_out = if (builtin.target.os.tag == .windows)
10089
std.fs.File{ .handle = pipe[1].? }
10190
else
10291
std.fs.File{ .handle = pipe[1] };
10392

104-
defer if (comptime !builtin.target.isDarwin()) file_out.close();
93+
defer file_out.close();
10594
const file_out_writer = file_out.writer();
10695
try file_out_writer.writeAll("test123\x17"); // ETB = \x17
10796
const ret_val = try child_proc.wait();

0 commit comments

Comments
 (0)