diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e5a71e30..214dc340f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index e304c5e9f..391af9924 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -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"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 508a2207f..9b7f3793c 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -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"); diff --git a/std/build.zig b/std/build.zig index 9ec3474db..98cc78e21 100644 --- a/std/build.zig +++ b/std/build.zig @@ -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); } }; diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 0dfdc08e8..73bf8463e 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -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 { diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig new file mode 100644 index 000000000..a5a9fe830 --- /dev/null +++ b/std/os/get_user_id.zig @@ -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; + } +} diff --git a/std/os/index.zig b/std/os/index.zig index 8817aabe1..5b47becf5 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -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, + }; +} diff --git a/std/os/linux.zig b/std/os/linux.zig index ed082e1e9..16a368ffb 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -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) } diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 01f9da4d2..044787dd4 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -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"); diff --git a/test/tests.zig b/test/tests.zig index 349cd3bf2..6a44a1e35 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -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| {