summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/pty.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/nix/src/pty.rs
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/src/pty.rs')
-rw-r--r--third_party/rust/nix/src/pty.rs268
1 files changed, 268 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/pty.rs b/third_party/rust/nix/src/pty.rs
new file mode 100644
index 0000000000..b71718850e
--- /dev/null
+++ b/third_party/rust/nix/src/pty.rs
@@ -0,0 +1,268 @@
+//! Create master and slave virtual pseudo-terminals (PTYs)
+
+use libc;
+
+pub use libc::pid_t as SessionId;
+pub use libc::winsize as Winsize;
+
+use std::ffi::CStr;
+use std::mem;
+use std::os::unix::prelude::*;
+
+use sys::termios::Termios;
+use {Result, Error, fcntl};
+use errno::Errno;
+
+/// Representation of a master/slave pty pair
+///
+/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
+/// must manually close the file descriptors.
+#[derive(Clone, Copy)]
+#[allow(missing_debug_implementations)]
+pub struct OpenptyResult {
+ /// The master port in a virtual pty pair
+ pub master: RawFd,
+ /// The slave port in a virtual pty pair
+ pub slave: RawFd,
+}
+
+
+/// Representation of the Master device in a master/slave pty pair
+///
+/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY
+/// functions are given the correct file descriptor. Additionally this type implements `Drop`,
+/// so that when it's consumed or goes out of scope, it's automatically cleaned-up.
+#[derive(Debug)]
+pub struct PtyMaster(RawFd);
+
+impl AsRawFd for PtyMaster {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
+
+impl IntoRawFd for PtyMaster {
+ fn into_raw_fd(self) -> RawFd {
+ let fd = self.0;
+ mem::forget(self);
+ fd
+ }
+}
+
+impl Drop for PtyMaster {
+ fn drop(&mut self) {
+ // On drop, we ignore errors like EINTR and EIO because there's no clear
+ // way to handle them, we can't return anything, and (on FreeBSD at
+ // least) the file descriptor is deallocated in these cases. However,
+ // we must panic on EBADF, because it is always an error to close an
+ // invalid file descriptor. That frequently indicates a double-close
+ // condition, which can cause confusing errors for future I/O
+ // operations.
+ let e = ::unistd::close(self.0);
+ if e == Err(Error::Sys(Errno::EBADF)) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+/// Grant access to a slave pseudoterminal (see
+/// [`grantpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
+///
+/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
+/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
+#[inline]
+pub fn grantpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
+ return Err(Error::last());
+ }
+
+ Ok(())
+}
+
+/// Open a pseudoterminal device (see
+/// [`posix_openpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
+///
+/// `posix_openpt()` returns a file descriptor to an existing unused pseuterminal master device.
+///
+/// # Examples
+///
+/// A common use case with this function is to open both a master and slave PTY pair. This can be
+/// done as follows:
+///
+/// ```
+/// use std::path::Path;
+/// use nix::fcntl::{OFlag, open};
+/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+/// use nix::sys::stat::Mode;
+///
+/// # #[allow(dead_code)]
+/// # fn run() -> nix::Result<()> {
+/// // Open a new PTY master
+/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
+///
+/// // Allow a slave to be generated for it
+/// grantpt(&master_fd)?;
+/// unlockpt(&master_fd)?;
+///
+/// // Get the name of the slave
+/// let slave_name = unsafe { ptsname(&master_fd) }?;
+///
+/// // Try to open the slave
+/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
+/// # Ok(())
+/// # }
+/// ```
+#[inline]
+pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
+ let fd = unsafe {
+ libc::posix_openpt(flags.bits())
+ };
+
+ if fd < 0 {
+ return Err(Error::last());
+ }
+
+ Ok(PtyMaster(fd))
+}
+
+/// Get the name of the slave pseudoterminal (see
+/// [`ptsname(3)`](http://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`.
+///
+/// This value is useful for opening the slave pty once the master has already been opened with
+/// `posix_openpt()`.
+///
+/// # Safety
+///
+/// `ptsname()` mutates global variables and is *not* threadsafe.
+/// Mutating global variables is always considered `unsafe` by Rust and this
+/// function is marked as `unsafe` to reflect that.
+///
+/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
+#[inline]
+pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
+ let name_ptr = libc::ptsname(fd.as_raw_fd());
+ if name_ptr.is_null() {
+ return Err(Error::last());
+ }
+
+ let name = CStr::from_ptr(name_ptr);
+ Ok(name.to_string_lossy().into_owned())
+}
+
+/// Get the name of the slave pseudoterminal (see
+/// [`ptsname(3)`](http://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
+/// POSIX standard and is instead a Linux-specific extension.
+///
+/// This value is useful for opening the slave ptty once the master has already been opened with
+/// `posix_openpt()`.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[inline]
+pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
+ let mut name_buf = vec![0u8; 64];
+ let name_buf_ptr = name_buf.as_mut_ptr() as *mut libc::c_char;
+ if unsafe { libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, name_buf.capacity()) } != 0 {
+ return Err(Error::last());
+ }
+
+ // Find the first null-character terminating this string. This is guaranteed to succeed if the
+ // return value of `libc::ptsname_r` is 0.
+ let null_index = name_buf.iter().position(|c| *c == b'\0').unwrap();
+ name_buf.truncate(null_index);
+
+ let name = String::from_utf8(name_buf)?;
+ Ok(name)
+}
+
+/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
+/// [`unlockpt(3)`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
+///
+/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
+/// referred to by `fd`. This must be called before trying to open the slave side of a
+/// pseuoterminal.
+#[inline]
+pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
+ return Err(Error::last());
+ }
+
+ Ok(())
+}
+
+
+/// Create a new pseudoterminal, returning the slave and master file descriptors
+/// in `OpenptyResult`
+/// (see [`openpty`](http://man7.org/linux/man-pages/man3/openpty.3.html)).
+///
+/// If `winsize` is not `None`, the window size of the slave will be set to
+/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
+/// terminal settings of the slave will be set to the values in `termios`.
+#[inline]
+pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
+ use std::ptr;
+
+ let mut slave: libc::c_int = unsafe { mem::uninitialized() };
+ let mut master: libc::c_int = unsafe { mem::uninitialized() };
+ let ret = {
+ match (termios.into(), winsize.into()) {
+ (Some(termios), Some(winsize)) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (None, Some(winsize)) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (Some(termios), None) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ ptr::null_mut(),
+ )
+ }
+ }
+ (None, None) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ }
+ }
+ }
+ };
+
+ Errno::result(ret)?;
+
+ Ok(OpenptyResult {
+ master: master,
+ slave: slave,
+ })
+}