update std.os.ChildProcess API

* add std.os.ChildProcess.setUserName
 * add std.os.getUserId
master
Andrew Kelley 2017-09-26 01:01:49 -04:00
parent 79400bfdfd
commit cba4a9ad4a
10 changed files with 326 additions and 151 deletions

View File

@ -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")

View File

@ -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");

View File

@ -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");

View File

@ -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);
}
};

View File

@ -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
View 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;
}
}

View File

@ -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,
};
}

View File

@ -480,6 +480,10 @@ pub fn nanosleep(req: &const timespec, rem: ?&timespec) -> 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)
}

View File

@ -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");

View File

@ -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| {