PranitInitial commit439f83c3 months agoHistory
1362 lines · 47.8 KB · Zig
RawDownload
const std = @import("std");const io_mod = @import("../shared/io.zig");const types = @import("../shared/types.zig");const session_child_store = @import("../session/session_child_store.zig"); pub const BackgroundLaunchPolicy = enum {    process_local_long_lived,    durable_long_lived,    saved_headless,}; pub const StableBackgroundRecordId = types.StableBackgroundRecordId; pub const ProcessInstanceToken = struct {    bytes: [128]u8 = undefined,    len: u8 = 0,     pub fn parse(text: []const u8) !ProcessInstanceToken {        if (text.len == 0 or text.len > 128) {            return error.InvalidProcessInstanceToken;        }        for (text) |byte| {            if (!std.ascii.isAscii(byte) or std.ascii.isUpper(byte) or                std.ascii.isWhitespace(byte) or                std.ascii.isControl(byte))            {                return error.InvalidProcessInstanceToken;            }        }        var parts = std.mem.splitScalar(u8, text, ':');        const platform = parts.next() orelse            return error.InvalidProcessInstanceToken;        const boot_id = parts.next() orelse            return error.InvalidProcessInstanceToken;        if (!isLowerHex(boot_id, 32)) {            return error.InvalidProcessInstanceToken;        }        if (std.mem.eql(u8, platform, "linux")) {            const start_ticks = parts.next() orelse                return error.InvalidProcessInstanceToken;            if (parts.next() != null or                !isCanonicalDecimal(start_ticks))            {                return error.InvalidProcessInstanceToken;            }        } else if (std.mem.eql(u8, platform, "macos")) {            const start_sec = parts.next() orelse                return error.InvalidProcessInstanceToken;            const start_usec = parts.next() orelse                return error.InvalidProcessInstanceToken;            if (parts.next() != null or                !isCanonicalDecimal(start_sec) or                !isCanonicalDecimal(start_usec))            {                return error.InvalidProcessInstanceToken;            }        } else {            return error.InvalidProcessInstanceToken;        }        var token = ProcessInstanceToken{};        @memcpy(token.bytes[0..text.len], text);        token.len = @intCast(text.len);        return token;    }     pub fn view(self: *const ProcessInstanceToken) []const u8 {        return self.bytes[0..self.len];    }     pub fn eql(self: ProcessInstanceToken, other: ProcessInstanceToken) bool {        return std.mem.eql(u8, self.view(), other.view());    }}; fn isLowerHex(value: []const u8, expected_len: usize) bool {    if (value.len != expected_len) return false;    for (value) |byte| {        if (!std.ascii.isDigit(byte) and            (byte < 'a' or byte > 'f'))        {            return false;        }    }    return true;} fn isCanonicalDecimal(value: []const u8) bool {    if (value.len == 0) return false;    if (value.len > 1 and value[0] == '0') return false;    for (value) |byte| {        if (!std.ascii.isDigit(byte)) return false;    }    _ = std.fmt.parseInt(u64, value, 10) catch return false;    return true;} pub const TokenMatch = enum {    matched,    missing,    mismatched,    unavailable,}; pub var process_token_match_for_test: ?*const fn ([]const u8, ProcessInstanceToken) TokenMatch = null;pub var process_token_capture_for_test: ?*const fn (    std.mem.Allocator,    []const u8,) anyerror!ProcessInstanceToken = null; pub const RecordAuthority = union(enum) {    none,    read_only: *session_child_store.SessionChildCapability,    writable: *session_child_store.SessionChildCapability,}; pub fn captureProcessInstanceToken(    alloc: std.mem.Allocator,    pid_text: []const u8,) !ProcessInstanceToken {    if (process_token_capture_for_test) |callback| {        return callback(alloc, pid_text);    }    return error.ProcessIdentityUnsupported;} pub fn matchProcessInstanceToken(    alloc: std.mem.Allocator,    pid_text: []const u8,    expected: ProcessInstanceToken,) TokenMatch {    if (process_token_match_for_test) |callback| {        return callback(pid_text, expected);    }    const actual = captureProcessInstanceToken(alloc, pid_text) catch |err| {        return switch (err) {            error.ProcessNotFound => .missing,            else => .unavailable,        };    };    return if (actual.eql(expected)) .matched else .mismatched;} pub const BackgroundRegistration = struct {    display_id: ?u64 = null,    pid: []const u8,    process_token: ?ProcessInstanceToken = null,    policy: BackgroundLaunchPolicy = .process_local_long_lived,    source_session_id: ?[]const u8 = null,    background_record_id: ?StableBackgroundRecordId = null,    durable_record_id: ?u64 = null,    record_authority: RecordAuthority = .none,    managed_log_name: ?[]const u8 = null,    command: []const u8,    cwd: []const u8,    log_path: []const u8,    expect_url: bool,    url: ?[]const u8 = null,}; pub const TaskState = enum {    running,    exited,    failed,    stopped,    dead,    stale,}; pub const RecordPersistenceState = enum {    not_applicable,    confirmed,    initial_record_degraded,    record_update_degraded,}; pub const TaskCompletion = struct {    id: u64,    state: TaskState,    exit_code: ?i32,}; pub const TaskRecord = struct {    id: u64,    pid: []u8,    process_token: ?ProcessInstanceToken = null,    policy: BackgroundLaunchPolicy = .process_local_long_lived,    source_session_id: ?[]u8 = null,    background_record_id: ?StableBackgroundRecordId = null,    durable_record_id: ?u64 = null,    record_authority: RecordAuthority = .none,    record_persistence: RecordPersistenceState = .not_applicable,    record_warning_emitted: bool = false,    managed_log_name: ?[]u8 = null,    command: []u8,    cwd: []u8,    log_path: []u8,    expect_url: bool,    server_url: ?[]u8 = null,    started_at_ms: i64,    exit_code: ?i32 = null,    state: TaskState = .running,     pub fn deinit(self: *TaskRecord, alloc: std.mem.Allocator) void {        alloc.free(self.pid);        if (self.source_session_id) |source_session_id| {            alloc.free(source_session_id);        }        if (self.managed_log_name) |managed_log_name| {            alloc.free(managed_log_name);        }        alloc.free(self.command);        alloc.free(self.cwd);        alloc.free(self.log_path);        if (self.server_url) |url| alloc.free(url);    }}; pub const RuntimeContextSnapshot = struct {    process_id: ?u64 = null,    background_log_path: ?[]u8 = null,    background_expect_url: bool = false,    server_url: ?[]u8 = null,    tasks: []TaskSnapshot = &.{},     pub fn deinit(self: RuntimeContextSnapshot, alloc: std.mem.Allocator) void {        if (self.background_log_path) |log_path| alloc.free(log_path);        if (self.server_url) |url| alloc.free(url);        for (self.tasks) |task| task.deinit(alloc);        if (self.tasks.len > 0) alloc.free(self.tasks);    }}; pub const PublishServerUrlResult = enum {    updated,    stale,    duplicate,}; pub const TaskSnapshot = struct {    id: u64,    pid: []u8,    process_token: ?ProcessInstanceToken = null,    policy: BackgroundLaunchPolicy = .process_local_long_lived,    source_session_id: ?[]u8 = null,    background_record_id: ?StableBackgroundRecordId = null,    durable_record_id: ?u64 = null,    record_authority: RecordAuthority = .none,    record_persistence: RecordPersistenceState = .not_applicable,    managed_log_name: ?[]u8 = null,    command: []u8,    cwd: []u8,    log_path: []u8,    expect_url: bool,    server_url: ?[]u8 = null,    started_at_ms: i64,    exit_code: ?i32 = null,    state: TaskState,     pub fn deinit(self: TaskSnapshot, alloc: std.mem.Allocator) void {        alloc.free(self.pid);        if (self.source_session_id) |source_session_id| {            alloc.free(source_session_id);        }        if (self.managed_log_name) |managed_log_name| {            alloc.free(managed_log_name);        }        alloc.free(self.command);        alloc.free(self.cwd);        alloc.free(self.log_path);        if (self.server_url) |url| alloc.free(url);    }}; pub const TaskListSnapshot = struct {    items: []TaskSnapshot,     pub fn deinit(self: TaskListSnapshot, alloc: std.mem.Allocator) void {        for (self.items) |item| item.deinit(alloc);        alloc.free(self.items);    }}; pub const TaskSelection = union(enum) {    last,    id: u64,}; pub const StopSelection = TaskSelection; pub const StopCandidate = struct {    id: u64,    pid: []u8,    process_token: ?ProcessInstanceToken,}; pub const ProcessSupervisor = struct {    next_background_process_id: u64 = 1,    display_id_exhausted: bool = false,    reserved_display_ids: std.ArrayList(u64) = .empty,    tasks: std.ArrayList(TaskRecord) = .empty,     pub fn deinit(self: *ProcessSupervisor, alloc: std.mem.Allocator) void {        for (self.tasks.items) |*task| task.deinit(alloc);        self.tasks.deinit(alloc);        self.reserved_display_ids.deinit(std.heap.c_allocator);        self.* = .{};    }     pub fn reserveDisplayId(self: *ProcessSupervisor) !u64 {        if (self.display_id_exhausted) return error.BackgroundIdentityUnavailable;        var candidate = self.next_background_process_id;        while (self.displayIdReserved(candidate)) {            if (candidate == std.math.maxInt(u64)) {                self.display_id_exhausted = true;                return error.BackgroundIdentityUnavailable;            }            candidate += 1;        }        try self.reserved_display_ids.append(std.heap.c_allocator, candidate);        if (candidate == std.math.maxInt(u64)) {            self.display_id_exhausted = true;        } else {            self.next_background_process_id = candidate + 1;        }        return candidate;    }     pub fn reservePreferredDisplayId(        self: *ProcessSupervisor,        preferred: u64,    ) !u64 {        if (!self.displayIdReserved(preferred)) {            try self.reserved_display_ids.append(std.heap.c_allocator, preferred);            if (!self.display_id_exhausted and                preferred >= self.next_background_process_id)            {                if (preferred == std.math.maxInt(u64)) {                    self.display_id_exhausted = true;                } else {                    self.next_background_process_id = preferred + 1;                }            }            return preferred;        }        return self.reserveDisplayId();    }     pub fn releaseDisplayId(self: *ProcessSupervisor, display_id: u64) void {        for (self.reserved_display_ids.items, 0..) |reserved, index| {            if (reserved != display_id) continue;            _ = self.reserved_display_ids.orderedRemove(index);            return;        }    }     pub fn retainDisplayIdReservation(        self: *ProcessSupervisor,        display_id: u64,    ) void {        if (self.displayIdReserved(display_id)) return;        self.reserved_display_ids.appendAssumeCapacity(display_id);    }     fn displayIdReserved(self: *const ProcessSupervisor, display_id: u64) bool {        for (self.reserved_display_ids.items) |reserved| {            if (reserved == display_id) return true;        }        for (self.tasks.items) |task| {            if (task.id == display_id) return true;        }        return false;    }     pub fn registerBackground(self: *ProcessSupervisor, alloc: std.mem.Allocator, registration: BackgroundRegistration) !u64 {        const process_id = if (registration.display_id) |reserved| blk: {            var found = false;            for (self.reserved_display_ids.items) |id| {                if (id == reserved) found = true;            }            if (!found) return error.BackgroundIdentityUnavailable;            break :blk reserved;        } else try self.reserveDisplayId();        errdefer self.releaseDisplayId(process_id);         const pid = try alloc.dupe(u8, registration.pid);        errdefer alloc.free(pid);        const command = try alloc.dupe(u8, registration.command);        errdefer alloc.free(command);        const cwd = try alloc.dupe(u8, registration.cwd);        errdefer alloc.free(cwd);        const log_path = try alloc.dupe(u8, registration.log_path);        errdefer alloc.free(log_path);         var server_url: ?[]u8 = null;        errdefer if (server_url) |url| alloc.free(url);        if (registration.url) |url| {            server_url = try alloc.dupe(u8, url);        }         var source_session_id: ?[]u8 = null;        errdefer if (source_session_id) |value| alloc.free(value);        if (registration.source_session_id) |value| {            source_session_id = try alloc.dupe(u8, value);        }        var managed_log_name: ?[]u8 = null;        errdefer if (managed_log_name) |value| alloc.free(value);        if (registration.managed_log_name) |value| {            managed_log_name = try alloc.dupe(u8, value);        }         try self.tasks.append(alloc, .{            .id = process_id,            .pid = pid,            .process_token = registration.process_token,            .policy = registration.policy,            .source_session_id = source_session_id,            .background_record_id = registration.background_record_id,            .durable_record_id = registration.durable_record_id orelse                if (registration.background_record_id != null)                    process_id                else                    null,            .record_authority = registration.record_authority,            .record_persistence = if (registration.background_record_id != null)                .confirmed            else                .not_applicable,            .managed_log_name = managed_log_name,            .command = command,            .cwd = cwd,            .log_path = log_path,            .expect_url = registration.expect_url,            .server_url = server_url,            .started_at_ms = io_mod.milliTimestamp(),        });        self.releaseDisplayId(process_id);         return process_id;    }     pub fn restoreBackground(        self: *ProcessSupervisor,        alloc: std.mem.Allocator,        task_snapshot: TaskSnapshot,    ) !u64 {        const process_id = try self.reservePreferredDisplayId(            task_snapshot.id,        );        errdefer self.releaseDisplayId(process_id);         const pid = try alloc.dupe(u8, task_snapshot.pid);        errdefer alloc.free(pid);        const command = try alloc.dupe(u8, task_snapshot.command);        errdefer alloc.free(command);        const cwd = try alloc.dupe(u8, task_snapshot.cwd);        errdefer alloc.free(cwd);        const log_path = try alloc.dupe(u8, task_snapshot.log_path);        errdefer alloc.free(log_path);         var server_url: ?[]u8 = null;        errdefer if (server_url) |url| alloc.free(url);        if (task_snapshot.server_url) |url| {            server_url = try alloc.dupe(u8, url);        }        var source_session_id: ?[]u8 = null;        errdefer if (source_session_id) |value| alloc.free(value);        if (task_snapshot.source_session_id) |value| {            source_session_id = try alloc.dupe(u8, value);        }        var managed_log_name: ?[]u8 = null;        errdefer if (managed_log_name) |value| alloc.free(value);        if (task_snapshot.managed_log_name) |value| {            managed_log_name = try alloc.dupe(u8, value);        }         try self.tasks.append(alloc, .{            .id = process_id,            .pid = pid,            .process_token = task_snapshot.process_token,            .policy = task_snapshot.policy,            .source_session_id = source_session_id,            .background_record_id = task_snapshot.background_record_id,            .durable_record_id = task_snapshot.durable_record_id,            .record_authority = task_snapshot.record_authority,            .record_persistence = task_snapshot.record_persistence,            .managed_log_name = managed_log_name,            .command = command,            .cwd = cwd,            .log_path = log_path,            .expect_url = task_snapshot.expect_url,            .server_url = server_url,            .started_at_ms = task_snapshot.started_at_ms,            .exit_code = task_snapshot.exit_code,            .state = task_snapshot.state,        });        self.releaseDisplayId(process_id);        return process_id;    }     pub fn restoreBackgroundAsNew(self: *ProcessSupervisor, alloc: std.mem.Allocator, task_snapshot: TaskSnapshot) !u64 {

Lines 1–500 of 1362.