| author | |
| committer | |
| log | 6a5bb3ede36ab9dd7a5ce95e1339ca4e138886fc |
| tree | 95c2341b00e3adf9e9ea29ccdd391d8a5898c4e6 |
| parent | d2d8b969a1674a6583292631ca7decc94cb56145 |
10 files changed, 106 insertions(+), 84 deletions(-)
doc/langref/wasi_preopens.zig+3-5| ... | @@ -1,10 +1,8 @@ | ... | @@ -1,10 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main(init: std.process.Init) !void { | 3 | pub fn main(init: std.process.Init) void { |
| 4 | const preopens = try std.fs.wasi.preopensAlloc(init.arena.allocator()); | 4 | for (init.preopens.map.keys(), 0..) |preopen, i| { |
| 5 | 5 | std.log.info("{d}: {s}", .{ i, preopen }); | |
| 6 | for (preopens.names, 0..) |preopen, i| { | ||
| 7 | std.debug.print("{d}: {s}\n", .{ i, preopen }); | ||
| 8 | } | 6 | } |
| 9 | } | 7 | } |
| 10 | 8 |
lib/std/fs.zig-1| ... | @@ -4,7 +4,6 @@ const std = @import("std.zig"); | ... | @@ -4,7 +4,6 @@ const std = @import("std.zig"); |
| 4 | 4 | ||
| 5 | /// Deprecated, use `std.Io.Dir.path`. | 5 | /// Deprecated, use `std.Io.Dir.path`. |
| 6 | pub const path = @import("fs/path.zig"); | 6 | pub const path = @import("fs/path.zig"); |
| 7 | pub const wasi = @import("fs/wasi.zig"); | ||
| 8 | 7 | ||
| 9 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; | 8 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; |
| 10 | 9 |
lib/std/fs/wasi.zig deleted-55| ... | @@ -1,55 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const mem = std.mem; | ||
| 4 | const math = std.math; | ||
| 5 | const fs = std.fs; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const Allocator = mem.Allocator; | ||
| 8 | const wasi = std.os.wasi; | ||
| 9 | const fd_t = wasi.fd_t; | ||
| 10 | const prestat_t = wasi.prestat_t; | ||
| 11 | |||
| 12 | pub const Preopens = struct { | ||
| 13 | // Indexed by file descriptor number. | ||
| 14 | names: []const []const u8, | ||
| 15 | |||
| 16 | pub fn find(p: Preopens, name: []const u8) ?std.posix.fd_t { | ||
| 17 | for (p.names, 0..) |elem_name, i| { | ||
| 18 | if (mem.eql(u8, elem_name, name)) { | ||
| 19 | return @intCast(i); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | return null; | ||
| 23 | } | ||
| 24 | }; | ||
| 25 | |||
| 26 | pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { | ||
| 27 | var names: std.ArrayList([]const u8) = .empty; | ||
| 28 | defer names.deinit(gpa); | ||
| 29 | |||
| 30 | try names.ensureUnusedCapacity(gpa, 3); | ||
| 31 | |||
| 32 | names.appendAssumeCapacity("stdin"); // 0 | ||
| 33 | names.appendAssumeCapacity("stdout"); // 1 | ||
| 34 | names.appendAssumeCapacity("stderr"); // 2 | ||
| 35 | while (true) { | ||
| 36 | const fd = @as(wasi.fd_t, @intCast(names.items.len)); | ||
| 37 | var prestat: prestat_t = undefined; | ||
| 38 | switch (wasi.fd_prestat_get(fd, &prestat)) { | ||
| 39 | .SUCCESS => {}, | ||
| 40 | .OPNOTSUPP, .BADF => return .{ .names = try names.toOwnedSlice(gpa) }, | ||
| 41 | else => @panic("fd_prestat_get: unexpected error"), | ||
| 42 | } | ||
| 43 | try names.ensureUnusedCapacity(gpa, 1); | ||
| 44 | // This length does not include a null byte. Let's keep it this way to | ||
| 45 | // gently encourage WASI implementations to behave properly. | ||
| 46 | const name_len = prestat.u.dir.pr_name_len; | ||
| 47 | const name = try gpa.alloc(u8, name_len); | ||
| 48 | errdefer gpa.free(name); | ||
| 49 | switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) { | ||
| 50 | .SUCCESS => {}, | ||
| 51 | else => @panic("fd_prestat_dir_name: unexpected error"), | ||
| 52 | } | ||
| 53 | names.appendAssumeCapacity(name); | ||
| 54 | } | ||
| 55 | } | ||
lib/std/os/wasi.zig+3-2| ... | @@ -288,8 +288,9 @@ pub const oflags_t = packed struct(u16) { | ... | @@ -288,8 +288,9 @@ pub const oflags_t = packed struct(u16) { |
| 288 | _: u12 = 0, | 288 | _: u12 = 0, |
| 289 | }; | 289 | }; |
| 290 | 290 | ||
| 291 | pub const preopentype_t = u8; | 291 | pub const preopentype_t = enum(u8) { |
| 292 | pub const PREOPENTYPE_DIR: preopentype_t = 0; | 292 | DIR = 0, |
| 293 | }; | ||
| 293 | 294 | ||
| 294 | pub const prestat_t = extern struct { | 295 | pub const prestat_t = extern struct { |
| 295 | pr_type: preopentype_t, | 296 | pr_type: preopentype_t, |
lib/std/process.zig+5| ... | @@ -18,6 +18,7 @@ const max_path_bytes = std.fs.max_path_bytes; | ... | @@ -18,6 +18,7 @@ const max_path_bytes = std.fs.max_path_bytes; |
| 18 | pub const Child = @import("process/Child.zig"); | 18 | pub const Child = @import("process/Child.zig"); |
| 19 | pub const Args = @import("process/Args.zig"); | 19 | pub const Args = @import("process/Args.zig"); |
| 20 | pub const Environ = @import("process/Environ.zig"); | 20 | pub const Environ = @import("process/Environ.zig"); |
| 21 | pub const Preopens = @import("process/Preopens.zig"); | ||
| 21 | 22 | ||
| 22 | /// This is the global, process-wide protection to coordinate stderr writes. | 23 | /// This is the global, process-wide protection to coordinate stderr writes. |
| 23 | /// | 24 | /// |
| ... | @@ -48,6 +49,10 @@ pub const Init = struct { | ... | @@ -48,6 +49,10 @@ pub const Init = struct { |
| 48 | io: Io, | 49 | io: Io, |
| 49 | /// Environment variables, initialized with `gpa`. Not threadsafe. | 50 | /// Environment variables, initialized with `gpa`. Not threadsafe. |
| 50 | environ_map: *Environ.Map, | 51 | environ_map: *Environ.Map, |
| 52 | /// Named files that have been provided by the parent process. This is | ||
| 53 | /// mainly useful on WASI, but can be used on other systems to mimic the | ||
| 54 | /// behavior with respect to stdio. | ||
| 55 | preopens: Preopens, | ||
| 51 | 56 | ||
| 52 | /// Alternative to `Init` as the first parameter of the main function. | 57 | /// Alternative to `Init` as the first parameter of the main function. |
| 53 | pub const Minimal = struct { | 58 | pub const Minimal = struct { |
lib/std/process/Preopens.zig created+75| ... | @@ -0,0 +1,75 @@ | ||
| 1 | const Preopens = @This(); | ||
| 2 | |||
| 3 | const builtin = @import("builtin"); | ||
| 4 | const native_os = builtin.os.tag; | ||
| 5 | |||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const Io = std.Io; | ||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | |||
| 10 | map: Map, | ||
| 11 | |||
| 12 | pub const empty: Preopens = switch (native_os) { | ||
| 13 | .wasi => .{ .map = .empty }, | ||
| 14 | else => .{ .map = {} }, | ||
| 15 | }; | ||
| 16 | |||
| 17 | pub const Map = switch (native_os) { | ||
| 18 | // Indexed by file descriptor number. | ||
| 19 | .wasi => std.StringArrayHashMapUnmanaged(void), | ||
| 20 | else => void, | ||
| 21 | }; | ||
| 22 | |||
| 23 | pub const Resource = union(enum) { | ||
| 24 | file: Io.File, | ||
| 25 | dir: Io.Dir, | ||
| 26 | }; | ||
| 27 | |||
| 28 | pub fn get(p: *const Preopens, name: []const u8) ?Resource { | ||
| 29 | switch (native_os) { | ||
| 30 | .wasi => { | ||
| 31 | const index = p.map.getIndex(name) orelse return null; | ||
| 32 | if (index <= 2) return .{ .file = .{ .handle = @intCast(index) } }; | ||
| 33 | return .{ .dir = .{ .handle = @intCast(index) } }; | ||
| 34 | }, | ||
| 35 | else => { | ||
| 36 | if (std.mem.eql(u8, name, "stdin")) return .{ .file = .stdin() }; | ||
| 37 | if (std.mem.eql(u8, name, "stdout")) return .{ .file = .stdout() }; | ||
| 38 | if (std.mem.eql(u8, name, "stderr")) return .{ .file = .stderr() }; | ||
| 39 | return null; | ||
| 40 | }, | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub const InitError = Allocator.Error || error{Unexpected}; | ||
| 45 | |||
| 46 | pub fn init(arena: Allocator) InitError!Preopens { | ||
| 47 | if (native_os != .wasi) return .{ .map = {} }; | ||
| 48 | const wasi = std.os.wasi; | ||
| 49 | var map: Map = .empty; | ||
| 50 | |||
| 51 | try map.ensureUnusedCapacity(arena, 3); | ||
| 52 | |||
| 53 | map.putAssumeCapacityNoClobber("stdin", {}); // 0 | ||
| 54 | map.putAssumeCapacityNoClobber("stdout", {}); // 1 | ||
| 55 | map.putAssumeCapacityNoClobber("stderr", {}); // 2 | ||
| 56 | while (true) { | ||
| 57 | const fd: wasi.fd_t = @intCast(map.entries.len); | ||
| 58 | var prestat: wasi.prestat_t = undefined; | ||
| 59 | switch (wasi.fd_prestat_get(fd, &prestat)) { | ||
| 60 | .SUCCESS => {}, | ||
| 61 | .OPNOTSUPP, .BADF => return .{ .map = map }, | ||
| 62 | else => return error.Unexpected, | ||
| 63 | } | ||
| 64 | try map.ensureUnusedCapacity(arena, 1); | ||
| 65 | // This length does not include a null byte. Let's keep it this way to | ||
| 66 | // gently encourage WASI implementations to behave properly. | ||
| 67 | const name_len = prestat.u.dir.pr_name_len; | ||
| 68 | const name = try arena.alloc(u8, name_len); | ||
| 69 | switch (wasi.fd_prestat_dir_name(fd, name.ptr, name.len)) { | ||
| 70 | .SUCCESS => {}, | ||
| 71 | else => return error.Unexpected, | ||
| 72 | } | ||
| 73 | map.putAssumeCapacityNoClobber(name, {}); | ||
| 74 | } | ||
| 75 | } | ||
lib/std/start.zig+4| ... | @@ -708,6 +708,9 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B | ... | @@ -708,6 +708,9 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B |
| 708 | std.process.fatal("failed to parse environment variables: {t}", .{err}); | 708 | std.process.fatal("failed to parse environment variables: {t}", .{err}); |
| 709 | defer environ_map.deinit(); | 709 | defer environ_map.deinit(); |
| 710 | 710 | ||
| 711 | const preopens = std.process.Preopens.init(arena_allocator.allocator()) catch |err| | ||
| 712 | std.process.fatal("failed to init preopens: {t}", .{err}); | ||
| 713 | |||
| 711 | return wrapMain(root.main(.{ | 714 | return wrapMain(root.main(.{ |
| 712 | .minimal = .{ | 715 | .minimal = .{ |
| 713 | .args = .{ .vector = args }, | 716 | .args = .{ .vector = args }, |
| ... | @@ -717,6 +720,7 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B | ... | @@ -717,6 +720,7 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B |
| 717 | .gpa = gpa, | 720 | .gpa = gpa, |
| 718 | .io = threaded.io(), | 721 | .io = threaded.io(), |
| 719 | .environ_map = &environ_map, | 722 | .environ_map = &environ_map, |
| 723 | .preopens = preopens, | ||
| 720 | })); | 724 | })); |
| 721 | } | 725 | } |
| 722 | 726 |
src/Compilation.zig+7-9| ... | @@ -758,10 +758,7 @@ pub const Directories = struct { | ... | @@ -758,10 +758,7 @@ pub const Directories = struct { |
| 758 | search, | 758 | search, |
| 759 | global, | 759 | global, |
| 760 | }, | 760 | }, |
| 761 | wasi_preopens: switch (builtin.target.os.tag) { | 761 | preopens: std.process.Preopens, |
| 762 | .wasi => fs.wasi.Preopens, | ||
| 763 | else => void, | ||
| 764 | }, | ||
| 765 | self_exe_path: switch (builtin.target.os.tag) { | 762 | self_exe_path: switch (builtin.target.os.tag) { |
| 766 | .wasi => void, | 763 | .wasi => void, |
| 767 | else => []const u8, | 764 | else => []const u8, |
| ... | @@ -776,7 +773,7 @@ pub const Directories = struct { | ... | @@ -776,7 +773,7 @@ pub const Directories = struct { |
| 776 | 773 | ||
| 777 | const zig_lib: Cache.Directory = d: { | 774 | const zig_lib: Cache.Directory = d: { |
| 778 | if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib"); | 775 | if (override_zig_lib) |path| break :d openUnresolved(arena, io, cwd, path, .@"zig lib"); |
| 779 | if (wasi) break :d openWasiPreopen(wasi_preopens, "/lib"); | 776 | if (wasi) break :d getPreopen(preopens, "/lib"); |
| 780 | break :d introspect.findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| { | 777 | break :d introspect.findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| { |
| 781 | fatal("unable to find zig installation directory '{s}': {t}", .{ self_exe_path, err }); | 778 | fatal("unable to find zig installation directory '{s}': {t}", .{ self_exe_path, err }); |
| 782 | }; | 779 | }; |
| ... | @@ -784,7 +781,7 @@ pub const Directories = struct { | ... | @@ -784,7 +781,7 @@ pub const Directories = struct { |
| 784 | 781 | ||
| 785 | const global_cache: Cache.Directory = d: { | 782 | const global_cache: Cache.Directory = d: { |
| 786 | if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); | 783 | if (override_global_cache) |path| break :d openUnresolved(arena, io, cwd, path, .@"global cache"); |
| 787 | if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache"); | 784 | if (wasi) break :d getPreopen(preopens, "/cache"); |
| 788 | const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| { | 785 | const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| { |
| 789 | fatal("unable to resolve zig cache directory: {t}", .{err}); | 786 | fatal("unable to resolve zig cache directory: {t}", .{err}); |
| 790 | }; | 787 | }; |
| ... | @@ -817,11 +814,12 @@ pub const Directories = struct { | ... | @@ -817,11 +814,12 @@ pub const Directories = struct { |
| 817 | .local_cache = local_cache, | 814 | .local_cache = local_cache, |
| 818 | }; | 815 | }; |
| 819 | } | 816 | } |
| 820 | fn openWasiPreopen(preopens: fs.wasi.Preopens, name: []const u8) Cache.Directory { | 817 | fn getPreopen(preopens: std.process.Preopens, name: []const u8) Cache.Directory { |
| 821 | return .{ | 818 | return .{ |
| 822 | .path = if (std.mem.eql(u8, name, ".")) null else name, | 819 | .path = if (std.mem.eql(u8, name, ".")) null else name, |
| 823 | .handle = .{ | 820 | .handle = switch (preopens.get(name) orelse fatal("preopen not found: '{s}'", .{name})) { |
| 824 | .handle = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}), | 821 | .file => fatal("preopen {s} is not a directory", .{name}), |
| 822 | .dir => |d| d, | ||
| 825 | }, | 823 | }, |
| 826 | }; | 824 | }; |
| 827 | } | 825 | } |
src/main.zig+7-7| ... | @@ -55,11 +55,11 @@ pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null; | ... | @@ -55,11 +55,11 @@ pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null; |
| 55 | pub const panic = crash_report.panic; | 55 | pub const panic = crash_report.panic; |
| 56 | pub const debug = crash_report.debug; | 56 | pub const debug = crash_report.debug; |
| 57 | 57 | ||
| 58 | var wasi_preopens: fs.wasi.Preopens = undefined; | 58 | var preopens: std.process.Preopens = .empty; |
| 59 | pub fn wasi_cwd() Io.Dir { | 59 | pub fn wasi_cwd() Io.Dir { |
| 60 | // Expect the first preopen to be current working directory. | 60 | // Expect the first preopen to be current working directory. |
| 61 | const cwd_fd: std.posix.fd_t = 3; | 61 | const cwd_fd: std.posix.fd_t = 3; |
| 62 | assert(mem.eql(u8, wasi_preopens.names[cwd_fd], ".")); | 62 | assert(mem.eql(u8, preopens.map.keys()[cwd_fd], ".")); |
| 63 | return .{ .handle = cwd_fd }; | 63 | return .{ .handle = cwd_fd }; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| ... | @@ -210,7 +210,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void { | ... | @@ -210,7 +210,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void { |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | if (native_os == .wasi) { | 212 | if (native_os == .wasi) { |
| 213 | wasi_preopens = try fs.wasi.preopensAlloc(arena); | 213 | preopens = try .init(arena); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | return mainArgs(gpa, arena, io, args, &environ_map); | 216 | return mainArgs(gpa, arena, io, args, &environ_map); |
| ... | @@ -360,7 +360,7 @@ fn mainArgs( | ... | @@ -360,7 +360,7 @@ fn mainArgs( |
| 360 | io, | 360 | io, |
| 361 | &stdout_writer.interface, | 361 | &stdout_writer.interface, |
| 362 | args, | 362 | args, |
| 363 | if (native_os == .wasi) wasi_preopens, | 363 | preopens, |
| 364 | &host, | 364 | &host, |
| 365 | environ_map, | 365 | environ_map, |
| 366 | ); | 366 | ); |
| ... | @@ -3107,7 +3107,7 @@ fn buildOutputType( | ... | @@ -3107,7 +3107,7 @@ fn buildOutputType( |
| 3107 | else => .search, | 3107 | else => .search, |
| 3108 | }; | 3108 | }; |
| 3109 | }, | 3109 | }, |
| 3110 | if (native_os == .wasi) wasi_preopens, | 3110 | preopens, |
| 3111 | self_exe_path, | 3111 | self_exe_path, |
| 3112 | environ_map, | 3112 | environ_map, |
| 3113 | ); | 3113 | ); |
| ... | @@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, | ... | @@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5141 | if (override_local_cache_dir) |d| break :path d; | 5141 | if (override_local_cache_dir) |d| break :path d; |
| 5142 | break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); | 5142 | break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); |
| 5143 | } }, | 5143 | } }, |
| 5144 | {}, | 5144 | .empty, |
| 5145 | self_exe_path, | 5145 | self_exe_path, |
| 5146 | environ_map, | 5146 | environ_map, |
| 5147 | ); | 5147 | ); |
| ... | @@ -5556,7 +5556,7 @@ fn jitCmd( | ... | @@ -5556,7 +5556,7 @@ fn jitCmd( |
| 5556 | override_lib_dir, | 5556 | override_lib_dir, |
| 5557 | override_global_cache_dir, | 5557 | override_global_cache_dir, |
| 5558 | .global, | 5558 | .global, |
| 5559 | if (native_os == .wasi) wasi_preopens, | 5559 | preopens, |
| 5560 | self_exe_path, | 5560 | self_exe_path, |
| 5561 | environ_map, | 5561 | environ_map, |
| 5562 | ); | 5562 | ); |
src/print_env.zig+2-5| ... | @@ -14,10 +14,7 @@ pub fn cmdEnv( | ... | @@ -14,10 +14,7 @@ pub fn cmdEnv( |
| 14 | io: Io, | 14 | io: Io, |
| 15 | out: *std.Io.Writer, | 15 | out: *std.Io.Writer, |
| 16 | args: []const []const u8, | 16 | args: []const []const u8, |
| 17 | wasi_preopens: switch (builtin.target.os.tag) { | 17 | preopens: std.process.Preopens, |
| 18 | .wasi => std.fs.wasi.Preopens, | ||
| 19 | else => void, | ||
| 20 | }, | ||
| 21 | host: *const std.Target, | 18 | host: *const std.Target, |
| 22 | environ_map: *std.process.Environ.Map, | 19 | environ_map: *std.process.Environ.Map, |
| 23 | ) !void { | 20 | ) !void { |
| ... | @@ -37,7 +34,7 @@ pub fn cmdEnv( | ... | @@ -37,7 +34,7 @@ pub fn cmdEnv( |
| 37 | override_lib_dir, | 34 | override_lib_dir, |
| 38 | override_global_cache_dir, | 35 | override_global_cache_dir, |
| 39 | .global, | 36 | .global, |
| 40 | if (builtin.target.os.tag == .wasi) wasi_preopens, | 37 | preopens, |
| 41 | if (builtin.target.os.tag != .wasi) self_exe_path, | 38 | if (builtin.target.os.tag != .wasi) self_exe_path, |
| 42 | environ_map, | 39 | environ_map, |
| 43 | ); | 40 | ); |