| author | |
| committer | |
| log | c44e12dcd309ae4f18903d66e262c07f0a318296 |
| tree | e3955f148ac2630f7b595a846b14bd8bc6730743 |
| parent | 938efe4aab09b5806cd3e9903619b95a5f122f36 |
10 files changed, 106 insertions(+), 84 deletions(-)
doc/langref/wasi_preopens.zig+3-5| ... | ... | @@ -1,10 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn main(init: std.process.Init) !void { | |
| 4 | const preopens = try std.fs.wasi.preopensAlloc(init.arena.allocator()); | |
| 5 | ||
| 6 | for (preopens.names, 0..) |preopen, i| { | |
| 7 | std.debug.print("{d}: {s}\n", .{ i, preopen }); | |
| 3 | pub fn main(init: std.process.Init) void { | |
| 4 | for (init.preopens.map.keys(), 0..) |preopen, i| { | |
| 5 | std.log.info("{d}: {s}", .{ i, preopen }); | |
| 8 | 6 | } |
| 9 | 7 | } |
| 10 | 8 |
lib/std/fs.zig-1| ... | ... | @@ -4,7 +4,6 @@ const std = @import("std.zig"); |
| 4 | 4 | |
| 5 | 5 | /// Deprecated, use `std.Io.Dir.path`. |
| 6 | 6 | pub const path = @import("fs/path.zig"); |
| 7 | pub const wasi = @import("fs/wasi.zig"); | |
| 8 | 7 | |
| 9 | 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 | 288 | _: u12 = 0, |
| 289 | 289 | }; |
| 290 | 290 | |
| 291 | pub const preopentype_t = u8; | |
| 292 | pub const PREOPENTYPE_DIR: preopentype_t = 0; | |
| 291 | pub const preopentype_t = enum(u8) { | |
| 292 | DIR = 0, | |
| 293 | }; | |
| 293 | 294 | |
| 294 | 295 | pub const prestat_t = extern struct { |
| 295 | 296 | pr_type: preopentype_t, |
lib/std/process.zig+5| ... | ... | @@ -18,6 +18,7 @@ const max_path_bytes = std.fs.max_path_bytes; |
| 18 | 18 | pub const Child = @import("process/Child.zig"); |
| 19 | 19 | pub const Args = @import("process/Args.zig"); |
| 20 | 20 | pub const Environ = @import("process/Environ.zig"); |
| 21 | pub const Preopens = @import("process/Preopens.zig"); | |
| 21 | 22 | |
| 22 | 23 | /// This is the global, process-wide protection to coordinate stderr writes. |
| 23 | 24 | /// |
| ... | ... | @@ -48,6 +49,10 @@ pub const Init = struct { |
| 48 | 49 | io: Io, |
| 49 | 50 | /// Environment variables, initialized with `gpa`. Not threadsafe. |
| 50 | 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 | 57 | /// Alternative to `Init` as the first parameter of the main function. |
| 53 | 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 | 708 | std.process.fatal("failed to parse environment variables: {t}", .{err}); |
| 709 | 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 | 714 | return wrapMain(root.main(.{ |
| 712 | 715 | .minimal = .{ |
| 713 | 716 | .args = .{ .vector = args }, |
| ... | ... | @@ -717,6 +720,7 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B |
| 717 | 720 | .gpa = gpa, |
| 718 | 721 | .io = threaded.io(), |
| 719 | 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 | 758 | search, |
| 759 | 759 | global, |
| 760 | 760 | }, |
| 761 | wasi_preopens: switch (builtin.target.os.tag) { | |
| 762 | .wasi => fs.wasi.Preopens, | |
| 763 | else => void, | |
| 764 | }, | |
| 761 | preopens: std.process.Preopens, | |
| 765 | 762 | self_exe_path: switch (builtin.target.os.tag) { |
| 766 | 763 | .wasi => void, |
| 767 | 764 | else => []const u8, |
| ... | ... | @@ -776,7 +773,7 @@ pub const Directories = struct { |
| 776 | 773 | |
| 777 | 774 | const zig_lib: Cache.Directory = d: { |
| 778 | 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 | 777 | break :d introspect.findZigLibDirFromSelfExe(arena, io, cwd, self_exe_path) catch |err| { |
| 781 | 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 | 781 | |
| 785 | 782 | const global_cache: Cache.Directory = d: { |
| 786 | 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 | 785 | const path = introspect.resolveGlobalCacheDir(arena, environ_map) catch |err| { |
| 789 | 786 | fatal("unable to resolve zig cache directory: {t}", .{err}); |
| 790 | 787 | }; |
| ... | ... | @@ -817,11 +814,12 @@ pub const Directories = struct { |
| 817 | 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 | 818 | return .{ |
| 822 | 819 | .path = if (std.mem.eql(u8, name, ".")) null else name, |
| 823 | .handle = .{ | |
| 824 | .handle = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}), | |
| 820 | .handle = switch (preopens.get(name) orelse fatal("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 | 55 | pub const panic = crash_report.panic; |
| 56 | 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 | 59 | pub fn wasi_cwd() Io.Dir { |
| 60 | 60 | // Expect the first preopen to be current working directory. |
| 61 | 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 | 63 | return .{ .handle = cwd_fd }; |
| 64 | 64 | } |
| 65 | 65 | |
| ... | ... | @@ -210,7 +210,7 @@ pub fn main(init: std.process.Init.Minimal) anyerror!void { |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | 212 | if (native_os == .wasi) { |
| 213 | wasi_preopens = try fs.wasi.preopensAlloc(arena); | |
| 213 | preopens = try .init(arena); | |
| 214 | 214 | } |
| 215 | 215 | |
| 216 | 216 | return mainArgs(gpa, arena, io, args, &environ_map); |
| ... | ... | @@ -360,7 +360,7 @@ fn mainArgs( |
| 360 | 360 | io, |
| 361 | 361 | &stdout_writer.interface, |
| 362 | 362 | args, |
| 363 | if (native_os == .wasi) wasi_preopens, | |
| 363 | preopens, | |
| 364 | 364 | &host, |
| 365 | 365 | environ_map, |
| 366 | 366 | ); |
| ... | ... | @@ -3107,7 +3107,7 @@ fn buildOutputType( |
| 3107 | 3107 | else => .search, |
| 3108 | 3108 | }; |
| 3109 | 3109 | }, |
| 3110 | if (native_os == .wasi) wasi_preopens, | |
| 3110 | preopens, | |
| 3111 | 3111 | self_exe_path, |
| 3112 | 3112 | environ_map, |
| 3113 | 3113 | ); |
| ... | ... | @@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8, |
| 5141 | 5141 | if (override_local_cache_dir) |d| break :path d; |
| 5142 | 5142 | break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename}); |
| 5143 | 5143 | } }, |
| 5144 | {}, | |
| 5144 | .empty, | |
| 5145 | 5145 | self_exe_path, |
| 5146 | 5146 | environ_map, |
| 5147 | 5147 | ); |
| ... | ... | @@ -5556,7 +5556,7 @@ fn jitCmd( |
| 5556 | 5556 | override_lib_dir, |
| 5557 | 5557 | override_global_cache_dir, |
| 5558 | 5558 | .global, |
| 5559 | if (native_os == .wasi) wasi_preopens, | |
| 5559 | preopens, | |
| 5560 | 5560 | self_exe_path, |
| 5561 | 5561 | environ_map, |
| 5562 | 5562 | ); |
src/print_env.zig+2-5| ... | ... | @@ -14,10 +14,7 @@ pub fn cmdEnv( |
| 14 | 14 | io: Io, |
| 15 | 15 | out: *std.Io.Writer, |
| 16 | 16 | args: []const []const u8, |
| 17 | wasi_preopens: switch (builtin.target.os.tag) { | |
| 18 | .wasi => std.fs.wasi.Preopens, | |
| 19 | else => void, | |
| 20 | }, | |
| 17 | preopens: std.process.Preopens, | |
| 21 | 18 | host: *const std.Target, |
| 22 | 19 | environ_map: *std.process.Environ.Map, |
| 23 | 20 | ) !void { |
| ... | ... | @@ -37,7 +34,7 @@ pub fn cmdEnv( |
| 37 | 34 | override_lib_dir, |
| 38 | 35 | override_global_cache_dir, |
| 39 | 36 | .global, |
| 40 | if (builtin.target.os.tag == .wasi) wasi_preopens, | |
| 37 | preopens, | |
| 41 | 38 | if (builtin.target.os.tag != .wasi) self_exe_path, |
| 42 | 39 | environ_map, |
| 43 | 40 | ); |