| author | |
| committer | |
| log | c01cfde6882840980cbf63593eafb5e1090b1ff0 |
| tree | fab6d65ab9f4552e66f6e61f9867a5ccb5d9cdfa |
| parent | 3a768bd6ce9e891ad258b32fb2dd0ed912ef9e9b |
and properly dup2 the file descriptor to make it handle the case when
other files are already open2 files changed, 138 insertions(+), 66 deletions(-)
lib/std/process.zig+107-33| ... | ... | @@ -1814,58 +1814,132 @@ test raiseFileDescriptorLimit { |
| 1814 | 1814 | } |
| 1815 | 1815 | |
| 1816 | 1816 | pub const CreateEnvironOptions = struct { |
| 1817 | env_map: ?*const EnvMap = null, | |
| 1818 | existing: ?[*:null]const ?[*:0]const u8 = null, | |
| 1819 | extra_usizes: []const ExtraUsize = &.{}, | |
| 1820 | ||
| 1821 | pub const ExtraUsize = struct { | |
| 1822 | name: []const u8, | |
| 1823 | value: usize, | |
| 1824 | }; | |
| 1817 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | |
| 1818 | /// If non-null, negative means to remove the environment variable, and >= 0 | |
| 1819 | /// means to provide it with the given integer. | |
| 1820 | zig_progress_fd: ?i32 = null, | |
| 1825 | 1821 | }; |
| 1826 | 1822 | |
| 1827 | 1823 | /// Creates a null-deliminated environment variable block in the format |
| 1828 | /// expected by POSIX, by combining all the sources of key-value pairs together | |
| 1829 | /// from `options`. | |
| 1830 | pub fn createEnviron(arena: Allocator, options: CreateEnvironOptions) Allocator.Error![:null]?[*:0]u8 { | |
| 1831 | const envp_count = c: { | |
| 1832 | var count: usize = 0; | |
| 1833 | if (options.existing) |env| { | |
| 1834 | while (env[count]) |_| : (count += 1) {} | |
| 1835 | } | |
| 1836 | if (options.env_map) |env_map| { | |
| 1837 | count += env_map.count(); | |
| 1824 | /// expected by POSIX, from a hash map plus options. | |
| 1825 | pub fn createEnvironFromMap( | |
| 1826 | arena: Allocator, | |
| 1827 | map: *const EnvMap, | |
| 1828 | options: CreateEnvironOptions, | |
| 1829 | ) Allocator.Error![:null]?[*:0]u8 { | |
| 1830 | const ZigProgressAction = enum { nothing, edit, delete, add }; | |
| 1831 | const zig_progress_action: ZigProgressAction = a: { | |
| 1832 | const fd = options.zig_progress_fd orelse break :a .nothing; | |
| 1833 | const contains = map.get("ZIG_PROGRESS") != null; | |
| 1834 | if (fd >= 0) { | |
| 1835 | break :a if (contains) .edit else .add; | |
| 1836 | } else { | |
| 1837 | if (contains) break :a .delete; | |
| 1838 | 1838 | } |
| 1839 | count += options.extra_usizes.len; | |
| 1840 | break :c count; | |
| 1839 | break :a .nothing; | |
| 1841 | 1840 | }; |
| 1841 | ||
| 1842 | const envp_count: usize = @intCast(@as(isize, map.count()) + @as(isize, switch (zig_progress_action) { | |
| 1843 | .add => 1, | |
| 1844 | .delete => -1, | |
| 1845 | .nothing, .edit => 0, | |
| 1846 | })); | |
| 1847 | ||
| 1842 | 1848 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); |
| 1843 | 1849 | var i: usize = 0; |
| 1844 | 1850 | |
| 1845 | if (options.existing) |env| { | |
| 1846 | while (env[i]) |line| : (i += 1) { | |
| 1847 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | |
| 1848 | } | |
| 1851 | if (zig_progress_action == .add) { | |
| 1852 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); | |
| 1853 | i += 1; | |
| 1849 | 1854 | } |
| 1850 | 1855 | |
| 1851 | for (options.extra_usizes, envp_buf[i..][0..options.extra_usizes.len]) |extra_usize, *out| { | |
| 1852 | out.* = try std.fmt.allocPrintZ(arena, "{s}={d}", .{ extra_usize.name, extra_usize.value }); | |
| 1853 | } | |
| 1854 | i += options.extra_usizes.len; | |
| 1856 | { | |
| 1857 | var it = map.iterator(); | |
| 1858 | while (it.next()) |pair| { | |
| 1859 | if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) { | |
| 1860 | .add => unreachable, | |
| 1861 | .delete => continue, | |
| 1862 | .edit => { | |
| 1863 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={d}", .{ | |
| 1864 | pair.key_ptr.*, options.zig_progress_fd.?, | |
| 1865 | }); | |
| 1866 | i += 1; | |
| 1867 | continue; | |
| 1868 | }, | |
| 1869 | .nothing => {}, | |
| 1870 | }; | |
| 1855 | 1871 | |
| 1856 | if (options.env_map) |env_map| { | |
| 1857 | var it = env_map.iterator(); | |
| 1858 | while (it.next()) |pair| : (i += 1) { | |
| 1859 | 1872 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }); |
| 1873 | i += 1; | |
| 1874 | } | |
| 1875 | } | |
| 1876 | ||
| 1877 | assert(i == envp_count); | |
| 1878 | return envp_buf; | |
| 1879 | } | |
| 1880 | ||
| 1881 | /// Creates a null-deliminated environment variable block in the format | |
| 1882 | /// expected by POSIX, from a hash map plus options. | |
| 1883 | pub fn createEnvironFromExisting( | |
| 1884 | arena: Allocator, | |
| 1885 | existing: [*:null]const ?[*:0]const u8, | |
| 1886 | options: CreateEnvironOptions, | |
| 1887 | ) Allocator.Error![:null]?[*:0]u8 { | |
| 1888 | const existing_count, const contains_zig_progress = c: { | |
| 1889 | var count: usize = 0; | |
| 1890 | var contains = false; | |
| 1891 | while (existing[count]) |line| : (count += 1) { | |
| 1892 | contains = contains or mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS"); | |
| 1860 | 1893 | } |
| 1894 | break :c .{ count, contains }; | |
| 1895 | }; | |
| 1896 | const ZigProgressAction = enum { nothing, edit, delete, add }; | |
| 1897 | const zig_progress_action: ZigProgressAction = a: { | |
| 1898 | const fd = options.zig_progress_fd orelse break :a .nothing; | |
| 1899 | if (fd >= 0) { | |
| 1900 | break :a if (contains_zig_progress) .edit else .add; | |
| 1901 | } else { | |
| 1902 | if (contains_zig_progress) break :a .delete; | |
| 1903 | } | |
| 1904 | break :a .nothing; | |
| 1905 | }; | |
| 1906 | ||
| 1907 | const envp_count: usize = @intCast(@as(isize, @intCast(existing_count)) + @as(isize, switch (zig_progress_action) { | |
| 1908 | .add => 1, | |
| 1909 | .delete => -1, | |
| 1910 | .nothing, .edit => 0, | |
| 1911 | })); | |
| 1912 | ||
| 1913 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | |
| 1914 | var i: usize = 0; | |
| 1915 | var existing_index: usize = 0; | |
| 1916 | ||
| 1917 | if (zig_progress_action == .add) { | |
| 1918 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); | |
| 1919 | i += 1; | |
| 1920 | } | |
| 1921 | ||
| 1922 | while (existing[existing_index]) |line| : (existing_index += 1) { | |
| 1923 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | |
| 1924 | .add => unreachable, | |
| 1925 | .delete => continue, | |
| 1926 | .edit => { | |
| 1927 | envp_buf[i] = try std.fmt.allocPrintZ(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}); | |
| 1928 | i += 1; | |
| 1929 | continue; | |
| 1930 | }, | |
| 1931 | .nothing => {}, | |
| 1932 | }; | |
| 1933 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | |
| 1934 | i += 1; | |
| 1861 | 1935 | } |
| 1862 | 1936 | |
| 1863 | 1937 | assert(i == envp_count); |
| 1864 | 1938 | return envp_buf; |
| 1865 | 1939 | } |
| 1866 | 1940 | |
| 1867 | pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 { | |
| 1868 | return createEnviron(arena, .{ .env_map = env_map }); | |
| 1941 | pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) Allocator.Error![:null]?[*:0]u8 { | |
| 1942 | return createEnvironFromMap(arena, env_map, .{}); | |
| 1869 | 1943 | } |
| 1870 | 1944 | |
| 1871 | 1945 | test createNullDelimitedEnvMap { |
lib/std/process/Child.zig+31-33| ... | ... | @@ -216,9 +216,9 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess { |
| 216 | 216 | .stdin = null, |
| 217 | 217 | .stdout = null, |
| 218 | 218 | .stderr = null, |
| 219 | .stdin_behavior = StdIo.Inherit, | |
| 220 | .stdout_behavior = StdIo.Inherit, | |
| 221 | .stderr_behavior = StdIo.Inherit, | |
| 219 | .stdin_behavior = .Inherit, | |
| 220 | .stdout_behavior = .Inherit, | |
| 221 | .stderr_behavior = .Inherit, | |
| 222 | 222 | .expand_arg0 = .no_expand, |
| 223 | 223 | }; |
| 224 | 224 | } |
| ... | ... | @@ -549,22 +549,22 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 549 | 549 | // turns out, we `dup2` everything anyway, so there's no need! |
| 550 | 550 | const pipe_flags: posix.O = .{ .CLOEXEC = true }; |
| 551 | 551 | |
| 552 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 553 | errdefer if (self.stdin_behavior == StdIo.Pipe) { | |
| 552 | const stdin_pipe = if (self.stdin_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 553 | errdefer if (self.stdin_behavior == .Pipe) { | |
| 554 | 554 | destroyPipe(stdin_pipe); |
| 555 | 555 | }; |
| 556 | 556 | |
| 557 | const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 558 | errdefer if (self.stdout_behavior == StdIo.Pipe) { | |
| 557 | const stdout_pipe = if (self.stdout_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 558 | errdefer if (self.stdout_behavior == .Pipe) { | |
| 559 | 559 | destroyPipe(stdout_pipe); |
| 560 | 560 | }; |
| 561 | 561 | |
| 562 | const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 563 | errdefer if (self.stderr_behavior == StdIo.Pipe) { | |
| 562 | const stderr_pipe = if (self.stderr_behavior == .Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 563 | errdefer if (self.stderr_behavior == .Pipe) { | |
| 564 | 564 | destroyPipe(stderr_pipe); |
| 565 | 565 | }; |
| 566 | 566 | |
| 567 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | |
| 567 | const any_ignore = (self.stdin_behavior == .Ignore or self.stdout_behavior == .Ignore or self.stderr_behavior == .Ignore); | |
| 568 | 568 | const dev_null_fd = if (any_ignore) |
| 569 | 569 | posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) { |
| 570 | 570 | error.PathAlreadyExists => unreachable, |
| ... | ... | @@ -609,35 +609,24 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 609 | 609 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null); |
| 610 | 610 | for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; |
| 611 | 611 | |
| 612 | const prog_fileno = 3; | |
| 613 | ||
| 612 | 614 | const envp: [*:null]const ?[*:0]const u8 = m: { |
| 613 | const extra_usizes: []const process.CreateEnvironOptions.ExtraUsize = if (prog_pipe[1] == -1) &.{} else &.{ | |
| 614 | .{ .name = "ZIG_PROGRESS", .value = @intCast(prog_pipe[1]) }, | |
| 615 | }; | |
| 615 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; | |
| 616 | 616 | if (self.env_map) |env_map| { |
| 617 | break :m (try process.createEnviron(arena, .{ | |
| 618 | .env_map = env_map, | |
| 619 | .extra_usizes = extra_usizes, | |
| 617 | break :m (try process.createEnvironFromMap(arena, env_map, .{ | |
| 618 | .zig_progress_fd = prog_fd, | |
| 620 | 619 | })).ptr; |
| 621 | 620 | } else if (builtin.link_libc) { |
| 622 | if (extra_usizes.len == 0) { | |
| 623 | break :m std.c.environ; | |
| 624 | } else { | |
| 625 | break :m (try process.createEnviron(arena, .{ | |
| 626 | .existing = std.c.environ, | |
| 627 | .extra_usizes = extra_usizes, | |
| 628 | })).ptr; | |
| 629 | } | |
| 621 | break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{ | |
| 622 | .zig_progress_fd = prog_fd, | |
| 623 | })).ptr; | |
| 630 | 624 | } else if (builtin.output_mode == .Exe) { |
| 631 | 625 | // Then we have Zig start code and this works. |
| 632 | if (extra_usizes.len == 0) { | |
| 633 | break :m @ptrCast(std.os.environ.ptr); | |
| 634 | } else { | |
| 635 | break :m (try process.createEnviron(arena, .{ | |
| 636 | // TODO type-safety for null-termination of `os.environ`. | |
| 637 | .existing = @ptrCast(std.os.environ.ptr), | |
| 638 | .extra_usizes = extra_usizes, | |
| 639 | })).ptr; | |
| 640 | } | |
| 626 | // TODO type-safety for null-termination of `os.environ`. | |
| 627 | break :m (try process.createEnvironFromExisting(arena, @ptrCast(std.os.environ.ptr), .{ | |
| 628 | .zig_progress_fd = prog_fd, | |
| 629 | })).ptr; | |
| 641 | 630 | } else { |
| 642 | 631 | // TODO come up with a solution for this. |
| 643 | 632 | @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process"); |
| ... | ... | @@ -664,6 +653,12 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 664 | 653 | setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); |
| 665 | 654 | setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); |
| 666 | 655 | setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); |
| 656 | if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 657 | ||
| 658 | if (prog_pipe[1] != -1) { | |
| 659 | if (prog_pipe[0] != prog_fileno) posix.close(prog_pipe[0]); | |
| 660 | if (prog_pipe[1] != prog_fileno) posix.close(prog_pipe[1]); | |
| 661 | } | |
| 667 | 662 | |
| 668 | 663 | if (self.cwd_dir) |cwd| { |
| 669 | 664 | posix.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err); |
| ... | ... | @@ -718,6 +713,9 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 718 | 713 | posix.close(stderr_pipe[1]); |
| 719 | 714 | } |
| 720 | 715 | |
| 716 | if (prog_pipe[1] != -1) { | |
| 717 | posix.close(prog_pipe[1]); | |
| 718 | } | |
| 721 | 719 | self.progress_node.setIpcFd(prog_pipe[0]); |
| 722 | 720 | } |
| 723 | 721 |