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