PranitInitial commit439f83c3 months agoHistory
215 lines · 6.4 KB · Zig
RawDownload
const std = @import("std");const background_process_provider = @import(    "../execution/background_process_provider.zig",);const io_mod = @import("../shared/io.zig");const session_child_store = @import("../session/session_child_store.zig"); const Allocator = std.mem.Allocator; pub const Output = union(enum) {    managed_session: struct {        capability: *session_child_store.SessionChildCapability,        file: session_child_store.ManagedFile,        name: []u8,        display_path: []u8,    },    external: struct {        file: std.Io.File,        path: []u8,    },     pub fn childStdioFile(self: *const Output) std.Io.File {        return switch (self.*) {            .managed_session => |value| value.file.childStdioFile(),            .external => |value| value.file,        };    }     pub fn providerCapability(        self: *const Output,    ) background_process_provider.OutputCapability {        return .{ .context = self };    }     pub fn childStdioFileForProvider(        capability: background_process_provider.OutputCapability,    ) std.Io.File {        const self: *const Output = @ptrCast(@alignCast(capability.context));        return self.childStdioFile();    }     pub fn displayPath(self: *const Output) []const u8 {        return switch (self.*) {            .managed_session => |value| value.display_path,            .external => |value| value.path,        };    }     pub fn managedLogName(self: *const Output) ?[]const u8 {        return switch (self.*) {            .managed_session => |value| value.name,            .external => null,        };    }     pub fn deinit(self: *Output, alloc: Allocator, remove: bool) void {        switch (self.*) {            .managed_session => |*value| {                value.file.deinit();                if (remove) {                    value.capability.delete(                        .background_logs,                        value.name,                    ) catch {};                }                alloc.free(value.name);                alloc.free(value.display_path);            },            .external => |value| {                value.file.close(io_mod.getIo());                if (remove) {                    std.Io.Dir.deleteFileAbsolute(                        io_mod.getIo(),                        value.path,                    ) catch {};                }                alloc.free(value.path);            },        }        self.* = undefined;    }}; pub fn prepareManaged(    alloc: Allocator,    capability: *session_child_store.SessionChildCapability,) !Output {    var attempt: usize = 0;    while (attempt < 16) : (attempt += 1) {        const name = try randomLogName(alloc);        var file = capability.createExclusiveFile(            alloc,            .background_logs,            name,        ) catch |err| switch (err) {            error.PathAlreadyExists => {                alloc.free(name);                continue;            },            else => {                alloc.free(name);                return err;            },        };        errdefer file.deinit();        const display_path = try alloc.dupe(            u8,            file.displayPath() orelse return error.SessionChildStoreFailed,        );        return .{ .managed_session = .{            .capability = capability,            .file = file,            .name = name,            .display_path = display_path,        } };    }    return error.BackgroundIdentityUnavailable;} pub fn prepareExternal(alloc: Allocator) !Output {    const root = io_mod.getenv("TMPDIR") orelse "/tmp";    var attempt: usize = 0;    while (attempt < 16) : (attempt += 1) {        const name = try randomLogName(alloc);        defer alloc.free(name);        const path = try std.fs.path.join(alloc, &.{ root, name });        const file = std.Io.Dir.createFileAbsolute(            io_mod.getIo(),            path,            .{                .read = true,                .truncate = false,                .exclusive = true,                .permissions = std.Io.File.Permissions.fromMode(0o600),            },        ) catch |err| switch (err) {            error.PathAlreadyExists => {                alloc.free(path);                continue;            },            else => {                alloc.free(path);                return err;            },        };        return .{ .external = .{ .file = file, .path = path } };    }    return error.BackgroundIdentityUnavailable;} fn randomLogName(alloc: Allocator) ![]u8 {    var random: [16]u8 = undefined;    try std.Io.randomSecure(io_mod.getIo(), &random);    var encoded: [32]u8 = undefined;    const alphabet = "0123456789abcdef";    for (random, 0..) |byte, index| {        encoded[index * 2] = alphabet[byte >> 4];        encoded[index * 2 + 1] = alphabet[byte & 0x0f];    }    return std.fmt.allocPrint(        alloc,        "fx-cmd-{s}.log",        .{encoded[0..]},    );} test "background launch output removes external log on cancellation" {    const alloc = std.testing.allocator;    var output = try prepareExternal(alloc);    const path = try alloc.dupe(u8, output.displayPath());    defer alloc.free(path);     output.deinit(alloc, true);    try std.testing.expectError(        error.FileNotFound,        std.Io.Dir.openFileAbsolute(io_mod.getIo(), path, .{}),    );} test "background launch output removes managed log on cancellation" {    const alloc = std.testing.allocator;    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();     try tmp.dir.createDir(        io_mod.getIo(),        "session",        std.Io.File.Permissions.fromMode(0o700),    );    const display_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "session");    defer alloc.free(display_path);    var session_dir = try tmp.dir.openDir(io_mod.getIo(), "session", .{        .iterate = true,        .follow_symlinks = false,    });    defer session_dir.close(io_mod.getIo());    var capability = try session_child_store.SessionChildCapability.initForTesting(        alloc,        session_dir,        display_path,        .writable,        .{},    );    defer capability.deinit();     var output = try prepareManaged(alloc, &capability);    const name = try alloc.dupe(u8, output.managedLogName().?);    defer alloc.free(name);     output.deinit(alloc, true);    try std.testing.expectError(        error.FileNotFound,        capability.stat(.background_logs, name),    );}