summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/cols.zig24
-rw-r--r--src/ninep/ctl.zig2
2 files changed, 24 insertions, 2 deletions
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig
index ef2e6cbd..ebe3279a 100644
--- a/src/ninep/cols.zig
+++ b/src/ninep/cols.zig
@@ -136,7 +136,12 @@ pub fn writeCtl(p: *Pardes, req: Req, serial: u32) Reply {
const takes = for ([_][]const u8{ "Delcol", "Joincol", "New", "Tty" }) |w| {
if (std.mem.eql(u8, w, word)) break true;
} else false;
- if (!takes) return tree.failText(req.tag, E.INVAL, "a column's ctl takes Delcol, Joincol, New or Tty");
+ // Plan 9's words for it, so a mount's errno is EINVAL as the root's
+ // and a pane's ctl give (9ns reads "control message").
+ if (!takes) return tree.failText(req.tag, E.INVAL, std.fmt.bufPrint(&p.fs.ename, "unknown control message \"{s}\": a column's ctl takes Delcol, Joincol, New or Tty", .{line[0..@min(line.len, 64)]}) catch "unknown control message");
+ // Only Tty takes a word (its shell); `New extra` is no New.
+ if (!std.mem.eql(u8, word, "Tty") and word.len != line.len)
+ return tree.failText(req.tag, E.INVAL, std.fmt.bufPrint(&p.fs.ename, "wrong #args in control message \"{s}\"", .{line[0..@min(line.len, 64)]}) catch "wrong #args in control message");
const col = layout.columnBySerial(p, serial) orelse return Reply.fail(req.tag, E.NOENT);
p.exec_column = col;
defer p.exec_column = null;
@@ -272,7 +277,7 @@ test "a column's ctl and exec act on it as its tag would, and rmdir closes it on
try testing.expectEqual(@as(usize, 1), p.col_n[1]);
try testing.expectEqual(E.INVAL, th.wr(p, Node.ofCol(empty, .ctl), "Exit\n").errno());
var rec: [64]u8 = undefined;
- try testing.expect(th.logHas(p, try std.fmt.bufPrint(&rec, "err - col/{d}/ctl: a column's ctl takes", .{empty})));
+ try testing.expect(th.logHas(p, try std.fmt.bufPrint(&rec, "err - col/{d}/ctl: unknown control message \"Exit\"", .{empty})));
// rmdir refuses a column with a pane, and says why.
const full = th.call(p, .{ .tag = 1, .op = .release, .node = Node.ofCol(empty, .dir), .remove = true });
try testing.expectEqual(E.NOTEMPTY, full.errno());
@@ -356,3 +361,18 @@ test "a command run from a column's tag gets its pane in that column, not the la
try testing.expect(other != made);
try testing.expectEqual(@as(usize, 1), layout.findPane(p, other).?.col);
}
+
+test "a column's ctl refuses an unknown word and an argument as the other ctls do" {
+ const p = try th.withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const ctl_node = Node.ofCol(layout.columnSerial(p, 0), .ctl);
+ const unknown = th.wr(p, ctl_node, "Bogus\n");
+ try testing.expectEqual(E.INVAL, unknown.errno());
+ try testing.expect(std.mem.startsWith(u8, unknown.reply.ename, "unknown control message \"Bogus\""));
+ const extra = th.wr(p, ctl_node, "New extra\n");
+ try testing.expectEqual(E.INVAL, extra.errno());
+ try testing.expect(std.mem.startsWith(u8, extra.reply.ename, "wrong #args"));
+ try testing.expectEqual(@as(usize, 1), p.col_n[0]);
+ // A pane's bare answer and a winsize of no size say what they take.
+ try testing.expectEqualStrings("answer takes a choice or -", th.wr(p, Node.of(p.panes[0].?.serial, .ctl), "answer\n").reply.ename);
+}
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 1b1288a1..424245eb 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -660,6 +660,8 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply {
if (!held) return tree.failText(req.tag, E.INVAL, e_not_locked);
held = false;
if (apply) pane.fs.lock = null;
+ } else if (std.mem.eql(u8, line, "answer")) {
+ return tree.failText(req.tag, E.INVAL, "answer takes a choice or -");
} else if (std.mem.startsWith(u8, line, "answer ")) {
// The pane's question (its `ask` in the log), answered.
const choice = std.mem.trim(u8, line["answer ".len..], " ");