mirror of
https://github.com/ziglang/zig.git
synced 2025-02-16 09:30:17 +00:00
std.process.Child: return any pre-exec errors from spawn
This commit is contained in:
parent
bb8776dd69
commit
647f76ccd6
@ -233,13 +233,25 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
|
||||
};
|
||||
}
|
||||
|
||||
/// Call this if you have no intention of calling `kill` or `wait` to properly
|
||||
/// dispose of any resources related to the child process.
|
||||
pub fn deinit(self: *ChildProcess) void {
|
||||
if (native_os == .windows) {
|
||||
posix.close(self.thread_handle);
|
||||
posix.close(self.id);
|
||||
}
|
||||
self.cleanupStreams();
|
||||
}
|
||||
|
||||
pub fn setUserName(self: *ChildProcess, name: []const u8) !void {
|
||||
const user_info = try process.getUserInfo(name);
|
||||
self.uid = user_info.uid;
|
||||
self.gid = user_info.gid;
|
||||
}
|
||||
|
||||
/// On success must call `kill` or `wait`.
|
||||
/// On success must call `kill` or `wait`. In the case of a detached process,
|
||||
/// consider using `deinit` instead if you have no intention of synchronizing
|
||||
/// with the child.
|
||||
/// After spawning the `id` is available.
|
||||
pub fn spawn(self: *ChildProcess) SpawnError!void {
|
||||
if (!process.can_spawn) {
|
||||
@ -693,6 +705,8 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
|
||||
posix.setpgid(0, pid) catch |err| forkChildErrReport(err_pipe[1], err);
|
||||
}
|
||||
|
||||
try writeIntFd(err_pipe[1], maxInt(ErrInt));
|
||||
|
||||
const err = switch (self.expand_arg0) {
|
||||
.expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
|
||||
.no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
|
||||
@ -701,6 +715,14 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
|
||||
}
|
||||
|
||||
// we are the parent
|
||||
|
||||
// we perform a blocking read on the err_pipe that gets us either an error
|
||||
// that occured between fork and exec or a maxInt(ErrInt) if there wasn't any
|
||||
const err_int = try readIntFd(err_pipe[0]);
|
||||
if (err_int != maxInt(ErrInt)) {
|
||||
return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
|
||||
}
|
||||
|
||||
const pid: i32 = @intCast(pid_result);
|
||||
if (self.stdin_behavior == .Pipe) {
|
||||
self.stdin = .{ .handle = stdin_pipe[1] };
|
||||
|
@ -65,6 +65,9 @@
|
||||
.detached_child = .{
|
||||
.path = "detached_child",
|
||||
},
|
||||
.child_spawn_fail = .{
|
||||
.path = "child_spawn_fail",
|
||||
},
|
||||
.embed_generated_file = .{
|
||||
.path = "embed_generated_file",
|
||||
},
|
||||
|
32
test/standalone/child_spawn_fail/build.zig
Normal file
32
test/standalone/child_spawn_fail/build.zig
Normal file
@ -0,0 +1,32 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const test_step = b.step("test", "Test it");
|
||||
b.default_step = test_step;
|
||||
|
||||
const optimize: std.builtin.OptimizeMode = .Debug;
|
||||
const target = b.graph.host;
|
||||
|
||||
if (builtin.os.tag == .wasi) return;
|
||||
|
||||
const child = b.addExecutable(.{
|
||||
.name = "child",
|
||||
.root_source_file = b.path("child.zig"),
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const main = b.addExecutable(.{
|
||||
.name = "main",
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const run = b.addRunArtifact(main);
|
||||
run.addArtifactArg(child);
|
||||
run.expectExitCode(0);
|
||||
|
||||
test_step.dependOn(&run.step);
|
||||
}
|
19
test/standalone/child_spawn_fail/child.zig
Normal file
19
test/standalone/child_spawn_fail/child.zig
Normal file
@ -0,0 +1,19 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa_state = std.heap.GeneralPurposeAllocator(.{ .safety = true }){};
|
||||
defer if (gpa_state.deinit() == .leak) @panic("leaks were detected");
|
||||
const gpa = gpa_state.allocator();
|
||||
var args = try std.process.argsWithAllocator(gpa);
|
||||
defer args.deinit();
|
||||
_ = args.next() orelse unreachable; // skip executable name
|
||||
const sleep_seconds = try std.fmt.parseInt(u32, args.next() orelse unreachable, 0);
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
_ = try stdout.write("started");
|
||||
|
||||
const end_time = std.time.timestamp() + sleep_seconds;
|
||||
while (std.time.timestamp() < end_time) {
|
||||
std.time.sleep(@max(end_time - std.time.timestamp(), 0) * 1_000_000_000);
|
||||
}
|
||||
}
|
40
test/standalone/child_spawn_fail/main.zig
Normal file
40
test/standalone/child_spawn_fail/main.zig
Normal file
@ -0,0 +1,40 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa_state = std.heap.GeneralPurposeAllocator(.{ .safety = true }){};
|
||||
defer if (gpa_state.deinit() == .leak) @panic("memory leak detected");
|
||||
const gpa = gpa_state.allocator();
|
||||
|
||||
var args = try std.process.argsWithAllocator(gpa);
|
||||
defer args.deinit();
|
||||
_ = args.next() orelse unreachable; // skip executable name
|
||||
const child_path = args.next() orelse unreachable;
|
||||
|
||||
var child = std.process.Child.init(&.{ child_path, "30" }, gpa);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stderr_behavior = .Ignore;
|
||||
child.stdout_behavior = .Pipe;
|
||||
child.detached = true;
|
||||
|
||||
// try to put the child in the same process group as the current session
|
||||
// leader, which should fail since the child will be in a different session
|
||||
child.pgid = try std.posix.getsid(0);
|
||||
defer {
|
||||
_ = child.kill() catch {};
|
||||
child.deinit();
|
||||
}
|
||||
|
||||
if (child.spawn()) {
|
||||
return error.SpawnSilencedError;
|
||||
} else |_| {}
|
||||
|
||||
child.deinit();
|
||||
child = std.process.Child.init(&.{ child_path, "30" }, gpa);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = .Inherit;
|
||||
|
||||
// this spawn should succeed and return without an error
|
||||
try child.spawn();
|
||||
}
|
Loading…
Reference in New Issue
Block a user