blob: 07e0961259bdde2cc91342467f9c4fac359f7b66 [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001//! Create master and slave virtual pseudo-terminals (PTYs)
2
3pub use libc::pid_t as SessionId;
4pub use libc::winsize as Winsize;
5
6use std::ffi::CStr;
7use std::io;
8use std::mem;
9use std::os::unix::prelude::*;
10
11use crate::sys::termios::Termios;
12use crate::unistd::{self, ForkResult, Pid};
13use crate::{Result, Error, fcntl};
14use crate::errno::Errno;
15
16/// Representation of a master/slave pty pair
17///
18/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
19/// must manually close the file descriptors.
20#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
21pub struct OpenptyResult {
22 /// The master port in a virtual pty pair
23 pub master: RawFd,
24 /// The slave port in a virtual pty pair
25 pub slave: RawFd,
26}
27
28/// Representation of a master with a forked pty
29///
30/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user
31/// must manually close the file descriptors.
32#[derive(Clone, Copy, Debug)]
33pub struct ForkptyResult {
34 /// The master port in a virtual pty pair
35 pub master: RawFd,
36 /// Metadata about forked process
37 pub fork_result: ForkResult,
38}
39
40
41/// Representation of the Master device in a master/slave pty pair
42///
43/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY
44/// functions are given the correct file descriptor. Additionally this type implements `Drop`,
45/// so that when it's consumed or goes out of scope, it's automatically cleaned-up.
Joel Galenson4727c112021-04-02 12:22:24 -070046#[derive(Debug, Eq, Hash, PartialEq)]
Andrew Walbran12f61402020-10-14 11:10:53 +010047pub struct PtyMaster(RawFd);
48
49impl AsRawFd for PtyMaster {
50 fn as_raw_fd(&self) -> RawFd {
51 self.0
52 }
53}
54
55impl IntoRawFd for PtyMaster {
56 fn into_raw_fd(self) -> RawFd {
57 let fd = self.0;
58 mem::forget(self);
59 fd
60 }
61}
62
63impl Drop for PtyMaster {
64 fn drop(&mut self) {
65 // On drop, we ignore errors like EINTR and EIO because there's no clear
66 // way to handle them, we can't return anything, and (on FreeBSD at
67 // least) the file descriptor is deallocated in these cases. However,
68 // we must panic on EBADF, because it is always an error to close an
69 // invalid file descriptor. That frequently indicates a double-close
70 // condition, which can cause confusing errors for future I/O
71 // operations.
72 let e = unistd::close(self.0);
73 if e == Err(Error::Sys(Errno::EBADF)) {
74 panic!("Closing an invalid file descriptor!");
75 };
76 }
77}
78
79impl io::Read for PtyMaster {
80 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
81 unistd::read(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
82 }
83}
84
85impl io::Write for PtyMaster {
86 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
87 unistd::write(self.0, buf).map_err(|e| e.as_errno().unwrap().into())
88 }
89 fn flush(&mut self) -> io::Result<()> {
90 Ok(())
91 }
92}
93
94/// Grant access to a slave pseudoterminal (see
Joel Galensone7950d92021-06-21 14:41:02 -070095/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
Andrew Walbran12f61402020-10-14 11:10:53 +010096///
97/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
98/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
99#[inline]
100pub fn grantpt(fd: &PtyMaster) -> Result<()> {
101 if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
102 return Err(Error::last());
103 }
104
105 Ok(())
106}
107
108/// Open a pseudoterminal device (see
Joel Galensone7950d92021-06-21 14:41:02 -0700109/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
Andrew Walbran12f61402020-10-14 11:10:53 +0100110///
111/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal master device.
112///
113/// # Examples
114///
115/// A common use case with this function is to open both a master and slave PTY pair. This can be
116/// done as follows:
117///
118/// ```
119/// use std::path::Path;
120/// use nix::fcntl::{OFlag, open};
121/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
122/// use nix::sys::stat::Mode;
123///
124/// # #[allow(dead_code)]
125/// # fn run() -> nix::Result<()> {
126/// // Open a new PTY master
127/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
128///
129/// // Allow a slave to be generated for it
130/// grantpt(&master_fd)?;
131/// unlockpt(&master_fd)?;
132///
133/// // Get the name of the slave
134/// let slave_name = unsafe { ptsname(&master_fd) }?;
135///
136/// // Try to open the slave
137/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
138/// # Ok(())
139/// # }
140/// ```
141#[inline]
142pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
143 let fd = unsafe {
144 libc::posix_openpt(flags.bits())
145 };
146
147 if fd < 0 {
148 return Err(Error::last());
149 }
150
151 Ok(PtyMaster(fd))
152}
153
154/// Get the name of the slave pseudoterminal (see
Joel Galensone7950d92021-06-21 14:41:02 -0700155/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
Andrew Walbran12f61402020-10-14 11:10:53 +0100156///
157/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
158/// referred to by `fd`.
159///
160/// This value is useful for opening the slave pty once the master has already been opened with
161/// `posix_openpt()`.
162///
163/// # Safety
164///
165/// `ptsname()` mutates global variables and is *not* threadsafe.
166/// Mutating global variables is always considered `unsafe` by Rust and this
167/// function is marked as `unsafe` to reflect that.
168///
169/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
170#[inline]
171pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
172 let name_ptr = libc::ptsname(fd.as_raw_fd());
173 if name_ptr.is_null() {
174 return Err(Error::last());
175 }
176
177 let name = CStr::from_ptr(name_ptr);
178 Ok(name.to_string_lossy().into_owned())
179}
180
181/// Get the name of the slave pseudoterminal (see
Joel Galensone7950d92021-06-21 14:41:02 -0700182/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
Andrew Walbran12f61402020-10-14 11:10:53 +0100183///
184/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
185/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
186/// POSIX standard and is instead a Linux-specific extension.
187///
188/// This value is useful for opening the slave ptty once the master has already been opened with
189/// `posix_openpt()`.
190#[cfg(any(target_os = "android", target_os = "linux"))]
191#[inline]
192pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
193 let mut name_buf = vec![0u8; 64];
194 let name_buf_ptr = name_buf.as_mut_ptr() as *mut libc::c_char;
195 if unsafe { libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, name_buf.capacity()) } != 0 {
196 return Err(Error::last());
197 }
198
199 // Find the first null-character terminating this string. This is guaranteed to succeed if the
200 // return value of `libc::ptsname_r` is 0.
201 let null_index = name_buf.iter().position(|c| *c == b'\0').unwrap();
202 name_buf.truncate(null_index);
203
204 let name = String::from_utf8(name_buf)?;
205 Ok(name)
206}
207
208/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
Joel Galensone7950d92021-06-21 14:41:02 -0700209/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
Andrew Walbran12f61402020-10-14 11:10:53 +0100210///
211/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
212/// referred to by `fd`. This must be called before trying to open the slave side of a
213/// pseuoterminal.
214#[inline]
215pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
216 if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
217 return Err(Error::last());
218 }
219
220 Ok(())
221}
222
223
224/// Create a new pseudoterminal, returning the slave and master file descriptors
225/// in `OpenptyResult`
Joel Galensone7950d92021-06-21 14:41:02 -0700226/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
Andrew Walbran12f61402020-10-14 11:10:53 +0100227///
228/// If `winsize` is not `None`, the window size of the slave will be set to
229/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
230/// terminal settings of the slave will be set to the values in `termios`.
231#[inline]
232pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
233 use std::ptr;
234
235 let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
236 let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
237 let ret = {
238 match (termios.into(), winsize.into()) {
239 (Some(termios), Some(winsize)) => {
240 let inner_termios = termios.get_libc_termios();
241 unsafe {
242 libc::openpty(
243 master.as_mut_ptr(),
244 slave.as_mut_ptr(),
245 ptr::null_mut(),
246 &*inner_termios as *const libc::termios as *mut _,
247 winsize as *const Winsize as *mut _,
248 )
249 }
250 }
251 (None, Some(winsize)) => {
252 unsafe {
253 libc::openpty(
254 master.as_mut_ptr(),
255 slave.as_mut_ptr(),
256 ptr::null_mut(),
257 ptr::null_mut(),
258 winsize as *const Winsize as *mut _,
259 )
260 }
261 }
262 (Some(termios), None) => {
263 let inner_termios = termios.get_libc_termios();
264 unsafe {
265 libc::openpty(
266 master.as_mut_ptr(),
267 slave.as_mut_ptr(),
268 ptr::null_mut(),
269 &*inner_termios as *const libc::termios as *mut _,
270 ptr::null_mut(),
271 )
272 }
273 }
274 (None, None) => {
275 unsafe {
276 libc::openpty(
277 master.as_mut_ptr(),
278 slave.as_mut_ptr(),
279 ptr::null_mut(),
280 ptr::null_mut(),
281 ptr::null_mut(),
282 )
283 }
284 }
285 }
286 };
287
288 Errno::result(ret)?;
289
290 unsafe {
291 Ok(OpenptyResult {
292 master: master.assume_init(),
293 slave: slave.assume_init(),
294 })
295 }
296}
297
298/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
299/// in `ForkptyResult`
Joel Galensone7950d92021-06-21 14:41:02 -0700300/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)).
Andrew Walbran12f61402020-10-14 11:10:53 +0100301///
302/// If `winsize` is not `None`, the window size of the slave will be set to
303/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
304/// terminal settings of the slave will be set to the values in `termios`.
Joel Galensone7950d92021-06-21 14:41:02 -0700305///
306/// # Safety
307///
308/// In a multithreaded program, only [async-signal-safe] functions like `pause`
309/// and `_exit` may be called by the child (the parent isn't restricted). Note
310/// that memory allocation may **not** be async-signal-safe and thus must be
311/// prevented.
312///
313/// Those functions are only a small subset of your operating system's API, so
314/// special care must be taken to only invoke code you can control and audit.
315///
316/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
317pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
Andrew Walbran12f61402020-10-14 11:10:53 +0100318 winsize: T,
319 termios: U,
320) -> Result<ForkptyResult> {
321 use std::ptr;
322
323 let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
324
325 let term = match termios.into() {
326 Some(termios) => {
327 let inner_termios = termios.get_libc_termios();
328 &*inner_termios as *const libc::termios as *mut _
329 },
330 None => ptr::null_mut(),
331 };
332
333 let win = winsize
334 .into()
335 .map(|ws| ws as *const Winsize as *mut _)
336 .unwrap_or(ptr::null_mut());
337
Joel Galensone7950d92021-06-21 14:41:02 -0700338 let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
Andrew Walbran12f61402020-10-14 11:10:53 +0100339
340 let fork_result = Errno::result(res).map(|res| match res {
341 0 => ForkResult::Child,
342 res => ForkResult::Parent { child: Pid::from_raw(res) },
343 })?;
344
Joel Galensone7950d92021-06-21 14:41:02 -0700345 Ok(ForkptyResult {
346 master: master.assume_init(),
347 fork_result,
348 })
Andrew Walbran12f61402020-10-14 11:10:53 +0100349}