summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 12:44:57 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit6fc9f416938b59b665b1fddc052402770a21cc3f (patch)
tree632e83ae787b6e1d231683514663188d3e73e4b9 /src/ninep
parentc8a3ecc3bcb36f0259f4d8ad2e0cdfcdc3183c9f (diff)
downloadpardes-6fc9f416938b59b665b1fddc052402770a21cc3f.tar.gz
pardes-6fc9f416938b59b665b1fddc052402770a21cc3f.zip
A Restore answers its writer before hanging up, and the log records dumps and restores
A client that wrote Restore saw its connection cut with no answer, and could not tell a Restore from a crash. The listener now lets the writer's answer out before the cut, and cuts only the old editor's connections, refusing their requests meanwhile; a client that dials during it is the new editor's and stays. Dump logs 'dump <path>' and the restored editor's log 'restore <path>'. Keeping connections across a Restore was weighed and left: the fids name the old editor's panes and opens, so it would mean carrying serials and open records into the new one, where acme's Load only adds windows. tty's Restore also closed its shells' ptys without reaping them; it retires them now. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/events.zig23
1 files changed, 23 insertions, 0 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 69dc271d..1cab5605 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -7,6 +7,7 @@ const look = @import("../look.zig");
const cloud9 = @import("cloud9");
const tree = @import("tree.zig");
const pane_files = @import("pane.zig");
+const dump = @import("../dump.zig");
const Pardes = pardes.Pardes;
const Pane = pardes.Pane;
@@ -155,6 +156,13 @@ pub fn noteMessage(p: *Pardes, serial: u32, text: []const u8) void {
/// kernel mapped the reply to (`Invalid argument`), and here is the reason.
/// The log is the one place for it, as acme's `errors` file takes text and
/// answers nothing: a per-pane readable error file would be a second.
+/// `dump <path>` when a Dump is written, `restore <path>` first in a
+/// Restore's replacement.
+pub fn notePath(p: *Pardes, what: []const u8, path: []const u8) void {
+ var buf: [pardes.memory.limits.host_path_cap + 16]u8 = undefined;
+ pushLog(p, std.fmt.bufPrint(&buf, "{s} {s}\n", .{ what, path }) catch return);
+}
+
pub fn noteError(p: *Pardes, req: Req, reply: Reply) void {
const why = if (reply.ename.len > 0) reply.ename else cloud9.fs.errString(reply.errno);
var name: [16]u8 = undefined;
@@ -870,6 +878,21 @@ test "a refused or failed write is an err record in the log, saying which file a
_ = call(p, .{ .tag = 9, .op = .release, .node = log, .handle = g });
}
+test "a Dump written and a Restore made are in the log, with their files" {
+ const p = try withFile(testing.allocator, "one\n");
+ defer p.deinit();
+ p.setLastDump("/tmp/pardes.dump.zon");
+ try dump.dumpState(p);
+ const restored = try dump.restore(p, p.dump_out.?, "/tmp/pardes.dump.zon");
+ defer restored.deinit();
+ for ([_]*Pardes{ p, restored }, [_][]const u8{ "dump", "restore" }) |core, what| {
+ const log = try freezeLog(core);
+ defer core.gpa.free(log.bytes);
+ var want: [64]u8 = undefined;
+ try testing.expect(std.mem.endsWith(u8, log.bytes, try std.fmt.bufPrint(&want, "{s} /tmp/pardes.dump.zon\n", .{what})));
+ }
+}
+
test "opens of the log share the open records, and a closed one frees its record" {
const gpa = testing.allocator;
const p = try withFile(gpa, "x\n");