diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/9p_io.zig | 10 | ||||
| -rw-r--r-- | src/Messages.zig | 17 | ||||
| -rw-r--r-- | src/builtins.zig | 15 | ||||
| -rw-r--r-- | src/look.zig | 4 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 16 |
5 files changed, 60 insertions, 2 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig index bda1ad36..2130db27 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -1761,6 +1761,16 @@ pub const Client = struct { return fetchBytes(gpa, sock, names[0..n], &remote, null, display_path, @min(max_bytes, limits.max_file_bytes)); } + /// Whether a peer answers at `dial`: a version and attach, and its root + /// read. Any answer from the peer, an error of its own included, is one. + pub fn probe(gpa: std.mem.Allocator, dial: []const u8) !void { + const got = readLimit(gpa, dial, "", "", 0) catch |err| switch (err) { + error.IsDirectory, error.NotFound, error.Remote, error.FileTooLarge => return, + else => return err, + }; + gpa.free(got); + } + pub fn write(gpa: std.mem.Allocator, dial: []const u8, path: []const u8, bytes: []const u8) !void { if (comptime !supported) return error.Unsupported; var names: [max_depth][]const u8 = undefined; diff --git a/src/Messages.zig b/src/Messages.zig index c0838fb2..482ad64d 100644 --- a/src/Messages.zig +++ b/src/Messages.zig @@ -450,10 +450,27 @@ pub const no_pane_slots = "no space for a pane: {d} max"; pub const no_pane_room = "no space for a pane in that column: each keeps its tag and {d} rows"; /// `<operation>: <the error in words>`: FileNotFound reads "file not found". +/// Why a peer could not be reached, in words, for an error of a 9P dial +/// (src/9p_io.zig, Client); null for any other error. +pub fn dialReason(err: anyerror) ?[]const u8 { + return switch (err) { + error.Dial => "no answer", + error.Timeout => "timed out", + error.Hangup => "hung up", + error.Botch => "protocol botch", + else => null, + }; +} + pub fn reportError(p: *Pardes, id: usize, operation: []const u8, err: anyerror) void { var buf: [256]u8 = undefined; var w = std.Io.Writer.fixed(&buf); w.print("{s}: ", .{operation}) catch {}; + // A peer out of reach says so, not the error's name (`dial`). + if (dialReason(err)) |why| { + w.print("dial failed: {s}", .{why}) catch {}; + return reportFailure(p, id, w.buffered()); + } // Every route that opens a pane says the cap the same way. if (err == error.NoPaneSlots or err == error.NoPaneRoom) { if (err == error.NoPaneSlots) diff --git a/src/builtins.zig b/src/builtins.zig index 404e78f4..3f4a680c 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -676,6 +676,21 @@ pub const Mount = struct { const dial = std.mem.trim(u8, args.rest(), " \t"); if (dial.len == 0) return c.p.reportError(c.id, "Mount name dial", error.MissingArgument); c.p.fs.mount(c.p.gpa, name, dial) catch |err| return c.p.reportError(c.id, "Mount", err); + // A mount that cannot be reached is said now, and taken back, not + // found out by the first look through it. + const reached = reached: { + pardes.turn.yield(); + defer pardes.turn.back(); + break :reached @import("9p_io.zig").Client.probe(c.p.gpa, dial); + }; + reached catch |err| { + if (c.p.fs.mounts.pop()) |gone| { + c.p.gpa.free(gone.name); + c.p.gpa.free(gone.dial); + } + var what: [160]u8 = undefined; + return c.p.reportError(c.id, std.fmt.bufPrint(&what, "Mount {s} {s}", .{ name, dial[0..@min(dial.len, 100)] }) catch "Mount", err); + }; } }; diff --git a/src/look.zig b/src/look.zig index 2f712fae..4ee5a638 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1458,7 +1458,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void { if (!focusPaneByPath(p, target.path, target.at)) { const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); const nt = panes.File.open(p, free, target.path, target.at.line) catch |err| - return p.reportError(id, "look", err); + return p.reportError(id, if (@import("Messages.zig").dialReason(err) != null) target.path else "look", err); if (target.at.col > 0) nt.body.cur_col = @intCast(target.at.col - 1); exec.placeNew(p, id, id, free, .doc); layout.compute(p); @@ -1476,7 +1476,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void { if (focusPaneByPath(p, target.path, .{})) return; const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); _ = panes.Image.create(p, free, target.path, &.{}) catch |err| - return p.reportError(id, "look", err); + return p.reportError(id, if (@import("Messages.zig").dialReason(err) != null) target.path else "look", err); exec.placeNew(p, id, id, free, .doc); }, } diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index b8f73c6f..7c68210a 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -1712,6 +1712,22 @@ test "size sets the screen of a session no frontend is attached to, and only of try testing.expectEqual(@as(u16, 40), p.screen_h); } +test "Mount dials its peer and fails, mounting nothing, when none answers; a look through a peer gone says dial failed" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const root_ctl = @intFromEnum(tree.TopFile.ctl); + const dead = "/tmp/pardes-test-no-such-peer.sock"; + const refused = wr(p, root_ctl, "Mount dead " ++ dead ++ "\n"); + try testing.expectEqual(E.IO, refused.errno()); + try testing.expectEqualStrings("Mount dead " ++ dead ++ ": dial failed: no answer \"Mount dead " ++ dead ++ "\"", refused.reply.ename); + try testing.expectEqual(@as(usize, 0), p.fs.mounts.items.len); + // Mounted when it answered, gone since: a look through it says so. + try p.fs.mounts.append(p.gpa, .{ .name = try p.gpa.dupe(u8, "gone"), .dial = try p.gpa.dupe(u8, dead) }); + const looked = wr(p, @intFromEnum(tree.TopFile.look), "/n/gone/f.txt\n"); + try testing.expect(looked.errno() != 0); + try testing.expect(th.logHas(p, "look: /n/gone/f.txt: dial failed: no answer")); +} + test "size is monotonic: growing is never refused, and a size once taken is taken again" { const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 60 }); defer p.deinit(); |
