pub use libc::pid_t as SessionId;
pub use libc::winsize as Winsize;
use std::ffi::CStr;
use std::io;
use std::mem;
use std::os::unix::prelude::*;
use crate::sys::termios::Termios;
#[cfg(feature = "process")]
use crate::unistd::{ForkResult, Pid};
use crate::{Result, fcntl, unistd};
use crate::errno::Errno;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct OpenptyResult {
pub master: RawFd,
pub slave: RawFd,
}
feature! {
#![feature = "process"]
#[derive(Clone, Copy, Debug)]
pub struct ForkptyResult {
pub master: RawFd,
pub fork_result: ForkResult,
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
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) {
let e = unistd::close(self.0);
if e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
impl io::Read for PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Read for &PtyMaster {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unistd::read(self.0, buf).map_err(io::Error::from)
}
}
impl io::Write for &PtyMaster {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unistd::write(self.0, buf).map_err(io::Error::from)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[inline]
pub fn grantpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
#[inline]
pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
let fd = unsafe {
libc::posix_openpt(flags.bits())
};
if fd < 0 {
return Err(Errno::last());
}
Ok(PtyMaster(fd))
}
#[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(Errno::last());
}
let name = CStr::from_ptr(name_ptr);
Ok(name.to_string_lossy().into_owned())
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
let name_buf_ptr = name_buf.as_mut_ptr();
let cname = unsafe {
let cap = name_buf.capacity();
if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
return Err(crate::Error::last());
}
CStr::from_ptr(name_buf.as_ptr())
};
let name = cname.to_string_lossy().into_owned();
Ok(name)
}
#[inline]
pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
return Err(Errno::last());
}
Ok(())
}
#[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 = mem::MaybeUninit::<libc::c_int>::uninit();
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let ret = {
match (termios.into(), winsize.into()) {
(Some(termios), Some(winsize)) => {
let inner_termios = termios.get_libc_termios();
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
winsize as *const Winsize as *mut _,
)
}
}
(None, Some(winsize)) => {
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
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(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
&*inner_termios as *const libc::termios as *mut _,
ptr::null_mut(),
)
}
}
(None, None) => {
unsafe {
libc::openpty(
master.as_mut_ptr(),
slave.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
)
}
}
}
};
Errno::result(ret)?;
unsafe {
Ok(OpenptyResult {
master: master.assume_init(),
slave: slave.assume_init(),
})
}
}
feature! {
#![feature = "process"]
pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
winsize: T,
termios: U,
) -> Result<ForkptyResult> {
use std::ptr;
let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
let term = match termios.into() {
Some(termios) => {
let inner_termios = termios.get_libc_termios();
&*inner_termios as *const libc::termios as *mut _
},
None => ptr::null_mut(),
};
let win = winsize
.into()
.map(|ws| ws as *const Winsize as *mut _)
.unwrap_or(ptr::null_mut());
let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
let fork_result = Errno::result(res).map(|res| match res {
0 => ForkResult::Child,
res => ForkResult::Parent { child: Pid::from_raw(res) },
})?;
Ok(ForkptyResult {
master: master.assume_init(),
fork_result,
})
}
}