]>
Commit | Line | Data |
---|---|---|
a69ef0b6 TL |
1 | //! Helper for creating a pseudo-terminal |
2 | //! | |
3 | //! see [PTY](struct.PTY.html) for an example on how to use it | |
4 | ||
5 | use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; | |
6 | ||
7 | use nix::fcntl::OFlag; | |
8 | use nix::pty::{grantpt, posix_openpt, ptsname_r, unlockpt, PtyMaster}; | |
9 | use nix::sys::stat::Mode; | |
10 | use nix::unistd::{dup2, setsid}; | |
11 | use nix::{ioctl_write_int_bad, ioctl_write_ptr_bad, Result}; | |
12 | ||
13 | ioctl_write_int_bad!(set_controlling_tty, libc::TIOCSCTTY); | |
14 | ioctl_write_ptr_bad!(set_size, libc::TIOCSWINSZ, nix::pty::Winsize); | |
15 | ||
16 | /// Represents a PTY | |
17 | /// | |
18 | /// Implements Read and Write (from std::io) so one can simply use it | |
19 | /// to read and write the terminal of a child process | |
20 | /// | |
21 | /// Example: | |
22 | /// ``` | |
23 | /// # use proxmox_sys::linux::pty::*; | |
24 | /// # use std::process::Command; | |
25 | /// # use nix::Result; | |
26 | /// fn fork() -> Result<u64> { | |
27 | /// // Code that forks and returs the pid/0 | |
28 | /// # Ok(1) | |
29 | /// } | |
30 | /// | |
31 | /// fn exec(cmd: &str) -> Result<()> { | |
32 | /// // Code that execs the cmd | |
33 | /// # Ok(()) | |
34 | /// } | |
35 | /// | |
36 | /// fn main() -> Result<()> { | |
37 | /// let (mut pty, secondary) = PTY::new()?; | |
38 | /// | |
39 | /// let child = fork()?; | |
40 | /// if child == 0 { | |
41 | /// make_controlling_terminal(&secondary)?; | |
42 | /// exec("/some/binary")?; | |
43 | /// } | |
44 | /// | |
45 | /// // read/write or set size of the terminal | |
46 | /// pty.set_size(80, 20); | |
47 | /// | |
48 | /// Ok(()) | |
49 | /// } | |
50 | /// ``` | |
51 | pub struct PTY { | |
52 | primary: PtyMaster, | |
53 | } | |
54 | ||
55 | /// Used to make a new process group of the current process, | |
56 | /// and make the given terminal its controlling terminal | |
57 | pub fn make_controlling_terminal(terminal: &str) -> Result<()> { | |
58 | setsid()?; // make new process group | |
59 | let mode = Mode::S_IRUSR | |
60 | | Mode::S_IWUSR | |
61 | | Mode::S_IRGRP | |
62 | | Mode::S_IWGRP | |
63 | | Mode::S_IROTH | |
64 | | Mode::S_IWOTH; // 0666 | |
65 | let secondary_fd = nix::fcntl::open(terminal, OFlag::O_RDWR | OFlag::O_NOCTTY, mode) | |
66 | .map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })?; | |
67 | let s_raw_fd = secondary_fd.as_raw_fd(); | |
68 | unsafe { set_controlling_tty(s_raw_fd, 0) }?; | |
69 | dup2(s_raw_fd, 0)?; | |
70 | dup2(s_raw_fd, 1)?; | |
71 | dup2(s_raw_fd, 2)?; | |
72 | ||
73 | if s_raw_fd <= 2 { | |
74 | std::mem::forget(secondary_fd); // don't call drop handler | |
75 | } | |
76 | ||
77 | Ok(()) | |
78 | } | |
79 | ||
80 | impl PTY { | |
81 | /// Creates a new PTY by opening /dev/ptmx and returns | |
82 | /// a new PTY and the path to the secondary terminal on success. | |
83 | pub fn new() -> Result<(Self, String)> { | |
84 | let primary = | |
85 | posix_openpt(OFlag::O_RDWR | OFlag::O_NOCTTY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC)?; | |
86 | grantpt(&primary)?; | |
87 | unlockpt(&primary)?; | |
88 | let secondary = ptsname_r(&primary)?; // linux specific | |
89 | Ok((Self { primary }, secondary)) | |
90 | } | |
91 | ||
92 | /// Uses the ioctl 'TIOCSWINSZ' on the terminal fd to set the terminals | |
93 | /// columns and rows | |
94 | pub fn set_size(&mut self, col: u16, row: u16) -> Result<()> { | |
95 | let size = nix::pty::Winsize { | |
96 | ws_row: row, | |
97 | ws_col: col, | |
98 | ws_xpixel: 0, | |
99 | ws_ypixel: 0, | |
100 | }; | |
101 | ||
102 | unsafe { set_size(self.primary.as_raw_fd(), &size) }?; | |
103 | ||
104 | Ok(()) | |
105 | } | |
106 | } | |
107 | ||
108 | impl std::io::Read for PTY { | |
109 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | |
110 | Ok(nix::unistd::read(self.primary.as_raw_fd(), buf)?) | |
111 | } | |
112 | } | |
113 | ||
114 | impl std::io::Write for PTY { | |
115 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { | |
116 | Ok(nix::unistd::write(self.primary.as_raw_fd(), buf)?) | |
117 | } | |
118 | ||
119 | fn flush(&mut self) -> std::io::Result<()> { | |
120 | Ok(()) | |
121 | } | |
122 | } | |
123 | ||
124 | impl AsRawFd for PTY { | |
125 | fn as_raw_fd(&self) -> RawFd { | |
126 | self.primary.as_raw_fd() | |
127 | } | |
128 | } | |
129 | ||
130 | impl AsFd for PTY { | |
131 | fn as_fd(&self) -> BorrowedFd<'_> { | |
132 | unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } | |
133 | } | |
134 | } |