1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
| #![no_std]
#![no_main]
#[macro_use]
#[path = "ulib.rs"]
mod ulib;
extern crate alloc;
use alloc::format;
use ulib::*;
fn read_some(fd: i64, buf: &mut [u8]) -> usize {
let mut p = [PollFd { fd: fd as i32, events: POLLIN, revents: 0 }];
if poll(&mut p, 1000) <= 0 {
return 0;
}
read(fd, buf).max(0) as usize
}
fn main() -> i64 {
let mut c = Checks::new("ptytest");
let (master, n) = match openpty() {
Ok(x) => x,
Err(e) => {
println!("ptytest: openpty: {}", strerror(e));
return 1;
}
};
let path = format!("/dev/pts/{}", n);
let slave = open(&path, O_RDWR | O_NOCTTY);
c.check(slave >= 0, &format!("open {} (the slave side)", path));
let mut buf = [0u8; 64];
write(master, b"hel");
set_nonblocking(slave, true);
c.check(read(slave, &mut buf) == -EAGAIN, "canonical: half a line can't be read yet (EAGAIN)");
write(master, b"lo\x7f\x7fp me\r"); let got = read_some(slave, &mut buf);
c.check(&buf[..got] == b"help me\n", "canonical: backspace edits the line, Enter ('\\r') ends it as '\\n'");
let got = read_some(master, &mut buf);
c.check(got > 0 && buf[..got].ends_with(b"\r\n"), "echo: what's typed comes back to the master");
write(slave, b"out\n");
let got = read_some(master, &mut buf);
c.check(&buf[..got] == b"out\r\n", "output: '\\n' becomes \"\\r\\n\" (ONLCR)");
let saved = tcgetattr(slave).unwrap_or_default();
let mut raw = saved;
cfmakeraw(&mut raw);
c.check(tcsetattr(slave, &raw) == 0, "TCSETS: switch to raw mode");
write(master, b"x\r\x03");
let got = read_some(slave, &mut buf);
c.check(&buf[..got] == b"x\r\x03", "raw: bytes arrive at once, unchanged (even ^C)");
set_nonblocking(master, true);
c.check(read(master, &mut buf) == -EAGAIN, "raw: no echo");
set_nonblocking(master, false);
tcsetattr(slave, &saved);
c.check(tcgetattr(slave).map(|t| t.lflag & ICANON != 0).unwrap_or(false), "TCGETS: canonical mode back on");
let pid = fork();
if pid == 0 {
setsid();
let fd = open(&path, O_RDWR); if fd < 0 {
exit(3);
}
write(fd, b"ready\n");
loop {
pause();
}
}
let got = read_some(master, &mut buf); c.check(&buf[..got] == b"ready\r\n", "child opened the pty as its controlling terminal");
write(master, b"\x03");
let status = match wait4(pid, 0) {
Ok((_, s)) => s,
Err(_) => 0,
};
c.check(wifsignaled(status) && wtermsig(status) == SIGINT, "^C on the master: SIGINT for the foreground job");
let got = read_some(master, &mut buf);
c.check(got >= 2 && &buf[..2] == b"^C", "^C is echoed as \"^C\"");
close(master);
c.check(read(slave, &mut buf) == 0, "master closed: the slave reads end-of-file");
close(slave);
c.done()
}
|