From 4e166246761474c3c524def1c993bd23a0d33fce Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 10 Sep 2024 21:14:55 -0700 Subject: [PATCH] crash: add directory listing, allocation free --- src/crash/dir.zig | 66 ++++++++++++++++++++++++++++++++++++++++++++ src/crash/main.zig | 5 ++++ src/crash/sentry.zig | 48 ++------------------------------ 3 files changed, 74 insertions(+), 45 deletions(-) create mode 100644 src/crash/dir.zig diff --git a/src/crash/dir.zig b/src/crash/dir.zig new file mode 100644 index 000000000..e5a8f8a0c --- /dev/null +++ b/src/crash/dir.zig @@ -0,0 +1,66 @@ +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const internal_os = @import("../os/main.zig"); + +/// Returns a Dir for the default directory. The Dir.path field must be +/// freed with the given allocator. +pub fn defaultDir(alloc: Allocator) !Dir { + const crash_dir = try internal_os.xdg.state(alloc, .{ .subdir = "ghostty/crash" }); + errdefer alloc.free(crash_dir); + return .{ .path = crash_dir }; +} + +pub const Dir = struct { + /// The directory where crash reports are stored. This memory is owned + /// by the caller. + path: []const u8, + + /// Returns an iterator over the crash reports in this directory. This + /// iterator must be freed with `ReportIterator.deinit`. The iterator + /// may have no reports. + pub fn iterator(self: *const Dir) !ReportIterator { + var dir = std.fs.openDirAbsolute( + self.path, + .{ .iterate = true }, + ) catch return .{}; + errdefer dir.close(); + + return .{ + .dir = dir, + .it = dir.iterate(), + }; + } +}; + +pub const ReportIterator = struct { + dir: ?std.fs.Dir = null, + it: std.fs.Dir.Iterator = undefined, + + pub fn deinit(self: *ReportIterator) void { + if (self.dir) |dir| dir.close(); + } + + pub fn next(self: *ReportIterator) !?Report { + // If we have no dir then we failed to open the directory. + const dir = self.dir orelse return null; + + // Get the next file entry, if any. + const entry = entry: while (true) { + const entry = try self.it.next() orelse return null; + if (entry.kind != .file) continue; + break :entry entry; + }; + + const stat = try dir.statFile(entry.name); + return .{ + .name = entry.name, + .mtime = stat.mtime, + }; + } +}; + +pub const Report = struct { + name: []const u8, + mtime: i128, +}; diff --git a/src/crash/main.zig b/src/crash/main.zig index 1e6044c4b..1ac971851 100644 --- a/src/crash/main.zig +++ b/src/crash/main.zig @@ -2,10 +2,15 @@ //! whether that's setting up the system to catch crashes (Sentry client), //! introspecting crash reports, writing crash reports to disk, etc. +const dir = @import("dir.zig"); const sentry_envelope = @import("sentry_envelope.zig"); pub const sentry = @import("sentry.zig"); pub const Envelope = sentry_envelope.Envelope; +pub const defaultDir = dir.defaultDir; +pub const Dir = dir.Dir; +pub const ReportIterator = dir.ReportIterator; +pub const Report = dir.Report; // The main init/deinit functions for global state. pub const init = sentry.init; diff --git a/src/crash/sentry.zig b/src/crash/sentry.zig index 1df311599..afff83f50 100644 --- a/src/crash/sentry.zig +++ b/src/crash/sentry.zig @@ -253,12 +253,12 @@ pub const Transport = struct { // Get our XDG state directory where we'll store the crash reports. // This directory must exist for writing to work. - const crash_dir = try internal_os.xdg.state(alloc, .{ .subdir = "ghostty/crash" }); - try std.fs.cwd().makePath(crash_dir); + const dir = try crash.defaultDir(alloc); + try std.fs.cwd().makePath(dir.path); // Build our final path and write to it. const path = try std.fs.path.join(alloc, &.{ - crash_dir, + dir.path, try std.fmt.allocPrint(alloc, "{s}.ghosttycrash", .{uuid.string()}), }); const file = try std.fs.cwd().createFile(path, .{}); @@ -277,45 +277,3 @@ pub const Transport = struct { return true; } }; - -pub const CrashReport = struct { - name: []const u8, - mtime: i128, -}; - -pub fn listCrashReports(alloc: std.mem.Allocator) !?[]CrashReport { - const crash_dir = try internal_os.xdg.state(alloc, .{ .subdir = "ghostty/crash" }); - defer alloc.free(crash_dir); - - var dir = std.fs.openDirAbsolute(crash_dir, .{ .iterate = true }) catch return null; - - defer dir.close(); - - var list = std.ArrayList(CrashReport).init(alloc); - errdefer { - for (list.items) |item| { - alloc.free(item.name); - } - list.deinit(); - } - - var it = dir.iterate(); - while (try it.next()) |entry| { - switch (entry.kind) { - .file => { - if (std.mem.endsWith(u8, entry.name, ".ghosttycrash")) { - const stat = dir.statFile(entry.name) catch continue; - try list.append(.{ - .name = try alloc.dupe(u8, entry.name), - .mtime = stat.mtime, - }); - } - }, - else => {}, - } - } - - if (list.items.len == 0) return null; - - return try list.toOwnedSlice(); -}