update std.os.ChildProcess API
* add std.os.ChildProcess.setUserName * add std.os.getUserId
This commit is contained in:
parent
79400bfdfd
commit
cba4a9ad4a
|
@ -548,6 +548,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
|
|||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/get_user_id.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
|
|
|
@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
|
|||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
||||
const run_cmd = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});
|
||||
const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
|
||||
run_cmd.step.dependOn(&exe.step);
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
|
|
|
@ -12,7 +12,7 @@ pub fn build(b: &Builder) {
|
|||
|
||||
b.default_step.dependOn(&exe.step);
|
||||
|
||||
const run_cmd = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});
|
||||
const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
|
||||
run_cmd.step.dependOn(&exe.step);
|
||||
|
||||
const test_step = b.step("test", "Test the program");
|
||||
|
|
|
@ -180,11 +180,11 @@ pub const Builder = struct {
|
|||
return LibExeObjStep.createCObject(self, name, src);
|
||||
}
|
||||
|
||||
/// ::args are copied.
|
||||
/// ::argv is copied.
|
||||
pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
|
||||
path: []const u8, args: []const []const u8) -> &CommandStep
|
||||
argv: []const []const u8) -> &CommandStep
|
||||
{
|
||||
return CommandStep.create(self, cwd, env_map, path, args);
|
||||
return CommandStep.create(self, cwd, env_map, argv);
|
||||
}
|
||||
|
||||
pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) -> &WriteFileStep {
|
||||
|
@ -528,42 +528,41 @@ pub const Builder = struct {
|
|||
return self.invalid_user_input;
|
||||
}
|
||||
|
||||
fn spawnChild(self: &Builder, exe_path: []const u8, args: []const []const u8) -> %void {
|
||||
return self.spawnChildEnvMap(null, &self.env_map, exe_path, args);
|
||||
fn spawnChild(self: &Builder, argv: []const []const u8) -> %void {
|
||||
return self.spawnChildEnvMap(null, &self.env_map, argv);
|
||||
}
|
||||
|
||||
fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
|
||||
exe_path: []const u8, args: []const []const u8) -> %void
|
||||
argv: []const []const u8) -> %void
|
||||
{
|
||||
if (self.verbose) {
|
||||
if (cwd) |yes_cwd| %%io.stderr.print("cd {}; ", yes_cwd);
|
||||
%%io.stderr.print("{}", exe_path);
|
||||
for (args) |arg| {
|
||||
%%io.stderr.print(" {}", arg);
|
||||
for (argv) |arg| {
|
||||
%%io.stderr.print("{} ", arg);
|
||||
}
|
||||
%%io.stderr.printf("\n");
|
||||
}
|
||||
|
||||
var child = os.ChildProcess.spawn(exe_path, args, cwd, env_map,
|
||||
StdIo.Inherit, StdIo.Inherit, StdIo.Inherit, null, self.allocator) %% |err|
|
||||
{
|
||||
%%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
|
||||
const child = %%os.ChildProcess.init(argv, self.allocator);
|
||||
defer child.deinit();
|
||||
|
||||
child.cwd = cwd;
|
||||
child.env_map = env_map;
|
||||
|
||||
const term = child.spawnAndWait() %% |err| {
|
||||
%%io.stderr.printf("Unable to spawn {}: {}\n", argv[0], @errorName(err));
|
||||
return err;
|
||||
};
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
%%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
%%io.stderr.printf("Process {} exited with error code {}\n", exe_path, code);
|
||||
%%io.stderr.printf("Process {} exited with error code {}\n", argv[0], code);
|
||||
return error.UncleanExit;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("Process {} terminated unexpectedly\n", exe_path);
|
||||
%%io.stderr.printf("Process {} terminated unexpectedly\n", argv[0]);
|
||||
return error.UncleanExit;
|
||||
},
|
||||
};
|
||||
|
@ -1063,6 +1062,8 @@ pub const LibExeObjStep = struct {
|
|||
var zig_args = ArrayList([]const u8).init(builder.allocator);
|
||||
defer zig_args.deinit();
|
||||
|
||||
%%zig_args.append(builder.zig_exe);
|
||||
|
||||
const cmd = switch (self.kind) {
|
||||
Kind.Lib => "build-lib",
|
||||
Kind.Exe => "build-exe",
|
||||
|
@ -1193,7 +1194,7 @@ pub const LibExeObjStep = struct {
|
|||
}
|
||||
}
|
||||
|
||||
%return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
|
||||
%return builder.spawnChild(zig_args.toSliceConst());
|
||||
|
||||
if (self.kind == Kind.Lib and !self.static) {
|
||||
%return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
|
||||
|
@ -1255,6 +1256,8 @@ pub const LibExeObjStep = struct {
|
|||
var cc_args = ArrayList([]const u8).init(builder.allocator);
|
||||
defer cc_args.deinit();
|
||||
|
||||
%%cc_args.append(cc);
|
||||
|
||||
const is_darwin = self.target.isDarwin();
|
||||
|
||||
switch (self.kind) {
|
||||
|
@ -1268,11 +1271,12 @@ pub const LibExeObjStep = struct {
|
|||
|
||||
self.appendCompileFlags(&cc_args);
|
||||
|
||||
%return builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
},
|
||||
Kind.Lib => {
|
||||
for (self.source_files.toSliceConst()) |source_file| {
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append(cc);
|
||||
|
||||
if (!self.static) {
|
||||
%%cc_args.append("-fPIC");
|
||||
|
@ -1291,7 +1295,7 @@ pub const LibExeObjStep = struct {
|
|||
|
||||
self.appendCompileFlags(&cc_args);
|
||||
|
||||
%return builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
|
||||
%%self.object_files.append(cache_o_file);
|
||||
}
|
||||
|
@ -1299,6 +1303,8 @@ pub const LibExeObjStep = struct {
|
|||
if (self.static) {
|
||||
// ar
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append("ar");
|
||||
|
||||
%%cc_args.append("qc");
|
||||
|
||||
const output_path = builder.pathFromRoot(self.getOutputPath());
|
||||
|
@ -1308,15 +1314,17 @@ pub const LibExeObjStep = struct {
|
|||
%%cc_args.append(builder.pathFromRoot(object_file));
|
||||
}
|
||||
|
||||
%return builder.spawnChild("ar", cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
|
||||
// ranlib
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append("ranlib");
|
||||
%%cc_args.append(output_path);
|
||||
|
||||
%return builder.spawnChild("ranlib", cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
} else {
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append(cc);
|
||||
|
||||
if (is_darwin) {
|
||||
%%cc_args.append("-dynamiclib");
|
||||
|
@ -1370,7 +1378,7 @@ pub const LibExeObjStep = struct {
|
|||
}
|
||||
}
|
||||
|
||||
%return builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
|
||||
%return doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename,
|
||||
self.name_only_filename);
|
||||
|
@ -1379,6 +1387,7 @@ pub const LibExeObjStep = struct {
|
|||
Kind.Exe => {
|
||||
for (self.source_files.toSliceConst()) |source_file| {
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append(cc);
|
||||
|
||||
const abs_source_file = builder.pathFromRoot(source_file);
|
||||
%%cc_args.append("-c");
|
||||
|
@ -1400,12 +1409,13 @@ pub const LibExeObjStep = struct {
|
|||
%%cc_args.append(builder.pathFromRoot(dir));
|
||||
}
|
||||
|
||||
%return builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
|
||||
%%self.object_files.append(cache_o_file);
|
||||
}
|
||||
|
||||
%%cc_args.resize(0);
|
||||
%%cc_args.append(cc);
|
||||
|
||||
for (self.object_files.toSliceConst()) |object_file| {
|
||||
%%cc_args.append(builder.pathFromRoot(object_file));
|
||||
|
@ -1441,7 +1451,7 @@ pub const LibExeObjStep = struct {
|
|||
}
|
||||
}
|
||||
|
||||
%return builder.spawnChild(cc, cc_args.toSliceConst());
|
||||
%return builder.spawnChild(cc_args.toSliceConst());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1518,6 +1528,8 @@ pub const TestStep = struct {
|
|||
var zig_args = ArrayList([]const u8).init(builder.allocator);
|
||||
defer zig_args.deinit();
|
||||
|
||||
%%zig_args.append(builder.zig_exe);
|
||||
|
||||
%%zig_args.append("test");
|
||||
%%zig_args.append(builder.pathFromRoot(self.root_src));
|
||||
|
||||
|
@ -1590,32 +1602,31 @@ pub const TestStep = struct {
|
|||
%%zig_args.append(lib_path);
|
||||
}
|
||||
|
||||
%return builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
|
||||
%return builder.spawnChild(zig_args.toSliceConst());
|
||||
}
|
||||
};
|
||||
|
||||
pub const CommandStep = struct {
|
||||
step: Step,
|
||||
builder: &Builder,
|
||||
exe_path: []const u8,
|
||||
args: [][]const u8,
|
||||
argv: [][]const u8,
|
||||
cwd: ?[]const u8,
|
||||
env_map: &const BufMap,
|
||||
|
||||
/// ::args are copied.
|
||||
/// ::argv is copied.
|
||||
pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
|
||||
exe_path: []const u8, args: []const []const u8) -> &CommandStep
|
||||
argv: []const []const u8) -> &CommandStep
|
||||
{
|
||||
const self = %%builder.allocator.create(CommandStep);
|
||||
*self = CommandStep {
|
||||
.builder = builder,
|
||||
.step = Step.init(exe_path, builder.allocator, make),
|
||||
.exe_path = exe_path,
|
||||
.args = %%builder.allocator.alloc([]u8, args.len),
|
||||
.step = Step.init(argv[0], builder.allocator, make),
|
||||
.argv = %%builder.allocator.alloc([]u8, argv.len),
|
||||
.cwd = cwd,
|
||||
.env_map = env_map,
|
||||
};
|
||||
mem.copy([]const u8, self.args, args);
|
||||
mem.copy([]const u8, self.argv, argv);
|
||||
self.step.name = self.argv[0];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -1623,7 +1634,7 @@ pub const CommandStep = struct {
|
|||
const self = @fieldParentPtr(CommandStep, "step", step);
|
||||
|
||||
const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else null;
|
||||
return self.builder.spawnChildEnvMap(cwd, self.env_map, self.exe_path, self.args);
|
||||
return self.builder.spawnChildEnvMap(cwd, self.env_map, self.argv);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,20 +16,35 @@ error ProcessNotFound;
|
|||
var children_nodes = LinkedList(&ChildProcess).init();
|
||||
|
||||
pub const ChildProcess = struct {
|
||||
pid: i32,
|
||||
pub pid: i32,
|
||||
pub allocator: &mem.Allocator,
|
||||
|
||||
pub stdin: ?&io.OutStream,
|
||||
pub stdout: ?&io.InStream,
|
||||
pub stderr: ?&io.InStream,
|
||||
|
||||
pub term: ?%Term,
|
||||
|
||||
pub argv: []const []const u8,
|
||||
|
||||
/// Possibly called from a signal handler. Must set this before calling `spawn`.
|
||||
pub onTerm: ?fn(&ChildProcess),
|
||||
|
||||
/// Leave as null to use the current env map using the supplied allocator.
|
||||
pub env_map: ?&const BufMap,
|
||||
|
||||
pub stdin_behavior: StdIo,
|
||||
pub stdout_behavior: StdIo,
|
||||
pub stderr_behavior: StdIo,
|
||||
|
||||
/// Set to change the user id when spawning the child process.
|
||||
pub uid: ?u32,
|
||||
|
||||
/// Set to change the current working directory when spawning the child process.
|
||||
pub cwd: ?[]const u8,
|
||||
|
||||
err_pipe: [2]i32,
|
||||
llnode: LinkedList(&ChildProcess).Node,
|
||||
allocator: &mem.Allocator,
|
||||
|
||||
stdin: ?&io.OutStream,
|
||||
stdout: ?&io.InStream,
|
||||
stderr: ?&io.InStream,
|
||||
|
||||
term: ?%Term,
|
||||
|
||||
/// Possibly called from a signal handler.
|
||||
onTerm: ?fn(&ChildProcess),
|
||||
|
||||
pub const Term = enum {
|
||||
Exited: i32,
|
||||
|
@ -45,18 +60,50 @@ pub const ChildProcess = struct {
|
|||
Close,
|
||||
};
|
||||
|
||||
/// First argument in argv is the executable.
|
||||
/// On success must call deinit.
|
||||
pub fn init(argv: []const []const u8, allocator: &Allocator) -> %&ChildProcess {
|
||||
const child = %return allocator.create(ChildProcess);
|
||||
%defer allocator.destroy(child);
|
||||
|
||||
*child = ChildProcess {
|
||||
.allocator = allocator,
|
||||
.argv = argv,
|
||||
.pid = undefined,
|
||||
.err_pipe = undefined,
|
||||
.llnode = undefined,
|
||||
.term = null,
|
||||
.onTerm = null,
|
||||
.env_map = null,
|
||||
.cwd = null,
|
||||
.uid = null,
|
||||
.stdin = null,
|
||||
.stdout = null,
|
||||
.stderr = null,
|
||||
.stdin_behavior = StdIo.Inherit,
|
||||
.stdout_behavior = StdIo.Inherit,
|
||||
.stderr_behavior = StdIo.Inherit,
|
||||
};
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
pub fn setUserName(self: &ChildProcess, name: []const u8) -> %void {
|
||||
self.uid = %return os.getUserId(name);
|
||||
}
|
||||
|
||||
/// onTerm can be called before `spawn` returns.
|
||||
pub fn spawn(exe_path: []const u8, args: []const []const u8,
|
||||
cwd: ?[]const u8, env_map: &const BufMap,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo,
|
||||
onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess
|
||||
{
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios, Os.darwin => {
|
||||
return spawnPosix(exe_path, args, cwd, env_map, stdin, stdout, stderr, onTerm, allocator);
|
||||
},
|
||||
/// On success must call `kill` or `wait`.
|
||||
pub fn spawn(self: &ChildProcess) -> %void {
|
||||
return switch (builtin.os) {
|
||||
Os.linux, Os.macosx, Os.ios, Os.darwin => self.spawnPosix(),
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn spawnAndWait(self: &ChildProcess) -> %Term {
|
||||
%return self.spawn();
|
||||
return self.wait();
|
||||
}
|
||||
|
||||
/// Forcibly terminates child process and then cleans up all resources.
|
||||
|
@ -96,6 +143,10 @@ pub const ChildProcess = struct {
|
|||
return ??self.term;
|
||||
}
|
||||
|
||||
pub fn deinit(self: &ChildProcess) {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
fn waitUnwrapped(self: &ChildProcess) {
|
||||
var status: i32 = undefined;
|
||||
while (true) {
|
||||
|
@ -162,50 +213,56 @@ pub const ChildProcess = struct {
|
|||
};
|
||||
}
|
||||
|
||||
fn spawnPosix(exe_path: []const u8, args: []const []const u8,
|
||||
maybe_cwd: ?[]const u8, env_map: &const BufMap,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo,
|
||||
onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess
|
||||
{
|
||||
fn spawnPosix(self: &ChildProcess) -> %void {
|
||||
// TODO atomically set a flag saying that we already did this
|
||||
install_SIGCHLD_handler();
|
||||
|
||||
const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
|
||||
const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); };
|
||||
|
||||
const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
|
||||
const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (self.stdout_behavior == StdIo.Pipe) { destroyPipe(stdout_pipe); };
|
||||
|
||||
const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
|
||||
const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) %return makePipe() else undefined;
|
||||
%defer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
|
||||
|
||||
const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
|
||||
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
|
||||
const dev_null_fd = if (any_ignore) {
|
||||
%return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
|
||||
} else {
|
||||
undefined
|
||||
};
|
||||
defer { if (any_ignore) os.posixClose(dev_null_fd); };
|
||||
|
||||
var env_map_owned: BufMap = undefined;
|
||||
var we_own_env_map: bool = undefined;
|
||||
const env_map = if (self.env_map) |env_map| {
|
||||
we_own_env_map = false;
|
||||
env_map
|
||||
} else {
|
||||
we_own_env_map = true;
|
||||
env_map_owned = %return os.getEnvMap(self.allocator);
|
||||
&env_map_owned
|
||||
};
|
||||
defer { if (we_own_env_map) env_map_owned.deinit(); }
|
||||
|
||||
// This pipe is used to communicate errors between the time of fork
|
||||
// and execve from the child process to the parent process.
|
||||
const err_pipe = %return makePipe();
|
||||
%defer destroyPipe(err_pipe);
|
||||
|
||||
const child = %return allocator.create(ChildProcess);
|
||||
%defer allocator.destroy(child);
|
||||
|
||||
const stdin_ptr = if (stdin == StdIo.Pipe) {
|
||||
%return allocator.create(io.OutStream)
|
||||
const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.OutStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
const stdout_ptr = if (stdout == StdIo.Pipe) {
|
||||
%return allocator.create(io.InStream)
|
||||
const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
const stderr_ptr = if (stderr == StdIo.Pipe) {
|
||||
%return allocator.create(io.InStream)
|
||||
const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
|
@ -224,19 +281,23 @@ pub const ChildProcess = struct {
|
|||
// we are the child
|
||||
restore_SIGCHLD();
|
||||
|
||||
setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
|
||||
setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
|
||||
setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|
||||
setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
|
||||
if (maybe_cwd) |cwd| {
|
||||
os.changeCurDir(allocator, cwd) %%
|
||||
if (self.cwd) |cwd| {
|
||||
os.changeCurDir(self.allocator, cwd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
}
|
||||
|
||||
os.posixExecve(exe_path, args, env_map, allocator) %%
|
||||
if (self.uid) |uid| {
|
||||
os.posix_setuid(uid) %% |err| forkChildErrReport(err_pipe[1], err);
|
||||
}
|
||||
|
||||
os.posixExecve(self.argv, env_map, self.allocator) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
}
|
||||
|
||||
|
@ -266,28 +327,22 @@ pub const ChildProcess = struct {
|
|||
};
|
||||
}
|
||||
|
||||
*child = ChildProcess {
|
||||
.allocator = allocator,
|
||||
.pid = pid,
|
||||
.err_pipe = err_pipe,
|
||||
.llnode = LinkedList(&ChildProcess).Node.init(child),
|
||||
.term = null,
|
||||
.onTerm = onTerm,
|
||||
.stdin = stdin_ptr,
|
||||
.stdout = stdout_ptr,
|
||||
.stderr = stderr_ptr,
|
||||
};
|
||||
self.pid = pid;
|
||||
self.err_pipe = err_pipe;
|
||||
self.llnode = LinkedList(&ChildProcess).Node.init(self);
|
||||
self.term = null;
|
||||
self.stdin = stdin_ptr;
|
||||
self.stdout = stdout_ptr;
|
||||
self.stderr = stderr_ptr;
|
||||
|
||||
children_nodes.prepend(&child.llnode);
|
||||
// TODO make this atomic so it works even with threads
|
||||
children_nodes.prepend(&self.llnode);
|
||||
|
||||
restore_SIGCHLD();
|
||||
|
||||
if (stdin == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
|
||||
if (stdout == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
|
||||
if (stderr == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
|
||||
if (any_ignore) { os.posixClose(dev_null_fd); }
|
||||
|
||||
return child;
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
|
||||
if (self.stdout_behavior == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
|
||||
}
|
||||
|
||||
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
|
||||
|
|
78
std/os/get_user_id.zig
Normal file
78
std/os/get_user_id.zig
Normal file
|
@ -0,0 +1,78 @@
|
|||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const os = @import("index.zig");
|
||||
const io = @import("../io.zig");
|
||||
|
||||
/// POSIX function which gets a uid from username.
|
||||
pub fn getUserId(name: []const u8) -> %u32 {
|
||||
return switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => posixGetUserId(name),
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
}
|
||||
|
||||
const State = enum {
|
||||
Start,
|
||||
WaitForNextLine,
|
||||
SkipPassword,
|
||||
ReadId,
|
||||
};
|
||||
|
||||
error UserNotFound;
|
||||
error CorruptPasswordFile;
|
||||
|
||||
pub fn posixGetUserId(name: []const u8) -> %u32 {
|
||||
var in_stream = %return io.InStream.open("/etc/passwd", null);
|
||||
defer in_stream.close();
|
||||
|
||||
var buf: [os.page_size]u8 = undefined;
|
||||
var name_index: usize = 0;
|
||||
var state = State.Start;
|
||||
var uid: u32 = 0;
|
||||
|
||||
while (true) {
|
||||
const amt_read = %return in_stream.read(buf[0..]);
|
||||
for (buf[0..amt_read]) |byte| {
|
||||
switch (state) {
|
||||
State.Start => switch (byte) {
|
||||
':' => {
|
||||
state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine;
|
||||
},
|
||||
'\n' => return error.CorruptPasswordFile,
|
||||
else => {
|
||||
if (name_index == name.len or name[name_index] != byte) {
|
||||
state = State.WaitForNextLine;
|
||||
}
|
||||
name_index += 1;
|
||||
},
|
||||
},
|
||||
State.WaitForNextLine => switch (byte) {
|
||||
'\n' => {
|
||||
name_index = 0;
|
||||
state = State.Start;
|
||||
},
|
||||
else => continue,
|
||||
},
|
||||
State.SkipPassword => switch (byte) {
|
||||
'\n' => return error.CorruptPasswordFile,
|
||||
':' => {
|
||||
state = State.ReadId;
|
||||
},
|
||||
else => continue,
|
||||
},
|
||||
State.ReadId => switch (byte) {
|
||||
'\n', ':' => return uid,
|
||||
else => {
|
||||
const digit = switch (byte) {
|
||||
'0' ... '9' => byte - '0',
|
||||
else => return error.CorruptPasswordFile,
|
||||
};
|
||||
if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile;
|
||||
if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if (amt_read < buf.len) return error.UserNotFound;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ pub const line_sep = switch (builtin.os) {
|
|||
|
||||
pub const page_size = 4 * 1024;
|
||||
|
||||
pub const getUserId = @import("get_user_id.zig").getUserId;
|
||||
|
||||
const debug = @import("../debug.zig");
|
||||
const assert = debug.assert;
|
||||
|
||||
|
@ -321,12 +323,12 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
|
|||
/// This function must allocate memory to add a null terminating bytes on path and each arg.
|
||||
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
|
||||
/// pointers after the args and after the environment variables.
|
||||
/// Also make the first arg equal to exe_path.
|
||||
/// `argv[0]` is the executable path.
|
||||
/// This function also uses the PATH environment variable to get the full path to the executable.
|
||||
pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap,
|
||||
pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
|
||||
allocator: &Allocator) -> %void
|
||||
{
|
||||
const argv_buf = %return allocator.alloc(?&u8, argv.len + 2);
|
||||
const argv_buf = %return allocator.alloc(?&u8, argv.len + 1);
|
||||
mem.set(?&u8, argv_buf, null);
|
||||
defer {
|
||||
for (argv_buf) |arg| {
|
||||
|
@ -335,22 +337,14 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
|
|||
}
|
||||
allocator.free(argv_buf);
|
||||
}
|
||||
{
|
||||
// Add exe_path to the first argument.
|
||||
const arg_buf = %return allocator.alloc(u8, exe_path.len + 1);
|
||||
@memcpy(&arg_buf[0], exe_path.ptr, exe_path.len);
|
||||
arg_buf[exe_path.len] = 0;
|
||||
|
||||
argv_buf[0] = arg_buf.ptr;
|
||||
}
|
||||
for (argv) |arg, i| {
|
||||
const arg_buf = %return allocator.alloc(u8, arg.len + 1);
|
||||
@memcpy(&arg_buf[0], arg.ptr, arg.len);
|
||||
arg_buf[arg.len] = 0;
|
||||
|
||||
argv_buf[i + 1] = arg_buf.ptr;
|
||||
argv_buf[i] = arg_buf.ptr;
|
||||
}
|
||||
argv_buf[argv.len + 1] = null;
|
||||
argv_buf[argv.len] = null;
|
||||
|
||||
const envp_count = env_map.count();
|
||||
const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
|
||||
|
@ -378,14 +372,9 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
|
|||
}
|
||||
envp_buf[envp_count] = null;
|
||||
|
||||
|
||||
const exe_path = argv[0];
|
||||
if (mem.indexOfScalar(u8, exe_path, '/') != null) {
|
||||
// +1 for the null terminating byte
|
||||
const path_buf = %return allocator.alloc(u8, exe_path.len + 1);
|
||||
defer allocator.free(path_buf);
|
||||
@memcpy(&path_buf[0], &exe_path[0], exe_path.len);
|
||||
path_buf[exe_path.len] = 0;
|
||||
return posixExecveErrnoToErr(posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)));
|
||||
return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr)));
|
||||
}
|
||||
|
||||
const PATH = getEnv("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";
|
||||
|
@ -434,6 +423,7 @@ fn posixExecveErrnoToErr(err: usize) -> error {
|
|||
|
||||
pub var environ_raw: []&u8 = undefined;
|
||||
|
||||
/// Caller must free result when done.
|
||||
pub fn getEnvMap(allocator: &Allocator) -> %BufMap {
|
||||
var result = BufMap.init(allocator);
|
||||
%defer result.deinit();
|
||||
|
@ -840,7 +830,7 @@ pub const Dir = struct {
|
|||
start_over:
|
||||
if (self.index >= self.end_index) {
|
||||
if (self.buf.len == 0) {
|
||||
self.buf = %return self.allocator.alloc(u8, 2); //page_size);
|
||||
self.buf = %return self.allocator.alloc(u8, page_size);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
@ -992,3 +982,20 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) {
|
|||
test "os.sleep" {
|
||||
sleep(0, 1);
|
||||
}
|
||||
|
||||
|
||||
error ResourceLimitReached;
|
||||
error InvalidUserId;
|
||||
error PermissionDenied;
|
||||
error Unexpected;
|
||||
|
||||
pub fn posix_setuid(uid: u32) -> %void {
|
||||
const err = posix.getErrno(posix.setuid(uid));
|
||||
if (err == 0) return;
|
||||
return switch (err) {
|
||||
posix.EAGAIN => error.ResourceLimitReached,
|
||||
posix.EINVAL => error.InvalidUserId,
|
||||
posix.EPERM => error.PermissionDenied,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -480,6 +480,10 @@ pub fn nanosleep(req: &const timespec, rem: ?×pec) -> usize {
|
|||
arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem))
|
||||
}
|
||||
|
||||
pub fn setuid(uid: u32) -> usize {
|
||||
arch.syscall1(arch.SYS_setuid, uid)
|
||||
}
|
||||
|
||||
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
|
||||
arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ pub fn build(b: &Builder) {
|
|||
exe.setBuildMode(b.standardReleaseOptions());
|
||||
exe.setBuildMode(b.standardReleaseOptions());
|
||||
|
||||
const run = b.addCommand(".", b.env_map, exe.getOutputPath(), [][]const u8{});
|
||||
const run = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
|
||||
run.step.dependOn(&exe.step);
|
||||
|
||||
const test_step = b.step("test", "Test it");
|
||||
|
|
|
@ -241,11 +241,15 @@ pub const CompareOutputContext = struct {
|
|||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,
|
||||
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|
|
||||
{
|
||||
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
|
||||
};
|
||||
const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
|
||||
defer child.deinit();
|
||||
|
||||
child.stdin_behavior = StdIo.Ignore;
|
||||
child.stdout_behavior = StdIo.Pipe;
|
||||
child.stderr_behavior = StdIo.Pipe;
|
||||
child.env_map = &b.env_map;
|
||||
|
||||
child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
|
||||
|
||||
var stdout = Buffer.initNull(b.allocator);
|
||||
var stderr = Buffer.initNull(b.allocator);
|
||||
|
@ -316,13 +320,15 @@ pub const CompareOutputContext = struct {
|
|||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map,
|
||||
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|
|
||||
{
|
||||
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
|
||||
};
|
||||
const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
|
||||
defer child.deinit();
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
child.env_map = &b.env_map;
|
||||
child.stdin_behavior = StdIo.Ignore;
|
||||
child.stdout_behavior = StdIo.Ignore;
|
||||
child.stderr_behavior = StdIo.Ignore;
|
||||
|
||||
const term = child.spawnAndWait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
|
||||
};
|
||||
|
||||
|
@ -539,6 +545,8 @@ pub const CompileErrorContext = struct {
|
|||
const obj_path = %%os.path.join(b.allocator, b.cache_root, "test.o");
|
||||
|
||||
var zig_args = ArrayList([]const u8).init(b.allocator);
|
||||
%%zig_args.append(b.zig_exe);
|
||||
|
||||
%%zig_args.append(if (self.case.is_exe) "build-exe" else "build-obj");
|
||||
%%zig_args.append(b.pathFromRoot(root_src));
|
||||
|
||||
|
@ -560,11 +568,15 @@ pub const CompileErrorContext = struct {
|
|||
printInvocation(b.zig_exe, zig_args.toSliceConst());
|
||||
}
|
||||
|
||||
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,
|
||||
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|
|
||||
{
|
||||
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
|
||||
};
|
||||
const child = %%os.ChildProcess.init(zig_args.toSliceConst(), b.allocator);
|
||||
defer child.deinit();
|
||||
|
||||
child.env_map = &b.env_map;
|
||||
child.stdin_behavior = StdIo.Ignore;
|
||||
child.stdout_behavior = StdIo.Pipe;
|
||||
child.stderr_behavior = StdIo.Pipe;
|
||||
|
||||
child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
|
||||
|
||||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
@ -573,7 +585,7 @@ pub const CompileErrorContext = struct {
|
|||
%%(??child.stderr).readAll(&stderr_buf);
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
|
||||
debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
|
||||
};
|
||||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
|
@ -712,6 +724,7 @@ pub const BuildExamplesContext = struct {
|
|||
}
|
||||
|
||||
var zig_args = ArrayList([]const u8).init(b.allocator);
|
||||
%%zig_args.append(b.zig_exe);
|
||||
%%zig_args.append("build");
|
||||
|
||||
%%zig_args.append("--build-file");
|
||||
|
@ -723,7 +736,7 @@ pub const BuildExamplesContext = struct {
|
|||
%%zig_args.append("--verbose");
|
||||
}
|
||||
|
||||
const run_cmd = b.addCommand(null, b.env_map, b.zig_exe, zig_args.toSliceConst());
|
||||
const run_cmd = b.addCommand(null, b.env_map, zig_args.toSliceConst());
|
||||
|
||||
const log_step = b.addLog("PASS {}\n", annotated_case_name);
|
||||
log_step.step.dependOn(&run_cmd.step);
|
||||
|
@ -813,6 +826,8 @@ pub const ParseCContext = struct {
|
|||
const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
|
||||
|
||||
var zig_args = ArrayList([]const u8).init(b.allocator);
|
||||
%%zig_args.append(b.zig_exe);
|
||||
|
||||
%%zig_args.append("parsec");
|
||||
%%zig_args.append(b.pathFromRoot(root_src));
|
||||
|
||||
|
@ -822,11 +837,15 @@ pub const ParseCContext = struct {
|
|||
printInvocation(b.zig_exe, zig_args.toSliceConst());
|
||||
}
|
||||
|
||||
var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map,
|
||||
StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err|
|
||||
{
|
||||
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
|
||||
};
|
||||
const child = %%os.ChildProcess.init(zig_args.toSliceConst(), b.allocator);
|
||||
defer child.deinit();
|
||||
|
||||
child.env_map = &b.env_map;
|
||||
child.stdin_behavior = StdIo.Ignore;
|
||||
child.stdout_behavior = StdIo.Pipe;
|
||||
child.stderr_behavior = StdIo.Pipe;
|
||||
|
||||
child.spawn() %% |err| debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
|
||||
|
||||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
@ -835,7 +854,7 @@ pub const ParseCContext = struct {
|
|||
%%(??child.stderr).readAll(&stderr_buf);
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
|
||||
debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
|
||||
};
|
||||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
|
|
Loading…
Reference in New Issue
Block a user