#![no_std]
extern crate alloc;
#[cfg(any(target_os = "android", target_os = "linux"))]
use alloc::vec::Vec;
use rustix::fd::{AsFd, OwnedFd};
#[cfg(any(target_os = "android", target_os = "linux"))] use rustix::fd::{AsRawFd, RawFd};
use rustix::io;
use rustix::termios::{Termios, Winsize};
pub use rustix;
pub struct Pty {
pub controller: OwnedFd,
pub user: OwnedFd,
}
pub fn openpty(termios: Option<&Termios>, winsize: Option<&Winsize>) -> io::Result<Pty> {
#[cfg(not(any(target_os = "android", target_os = "linux")))]
{
use core::mem::{align_of, size_of, MaybeUninit};
use core::ptr::{null, null_mut};
use rustix::fd::FromRawFd;
assert_eq!(size_of::<Termios>(), size_of::<libc::termios>());
assert_eq!(align_of::<Termios>(), align_of::<libc::termios>());
let termios: *const libc::termios = match termios {
Some(termios) => {
let termios: *const Termios = termios;
termios.cast()
}
None => null(),
};
let mut libc_winsize: libc::winsize;
let winsize: *mut libc::winsize = match winsize {
Some(winsize) => {
libc_winsize = libc::winsize {
ws_row: winsize.ws_row,
ws_col: winsize.ws_col,
ws_xpixel: winsize.ws_xpixel,
ws_ypixel: winsize.ws_ypixel,
};
&mut libc_winsize
}
None => null_mut(),
};
let mut controller = MaybeUninit::<libc::c_int>::uninit();
let mut user = MaybeUninit::<libc::c_int>::uninit();
unsafe {
if libc::openpty(
controller.as_mut_ptr(),
user.as_mut_ptr(),
null_mut(),
termios as _,
winsize,
) == 0
{
let controller = OwnedFd::from_raw_fd(controller.assume_init());
let user = OwnedFd::from_raw_fd(user.assume_init());
set_cloexec(&controller)?;
set_cloexec(&user)?;
Ok(Pty { controller, user })
} else {
Err(io::Errno::from_raw_os_error(errno::errno().0))
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
{
use rustix::pty::{grantpt, openpt, unlockpt, OpenptFlags};
use rustix::termios::{tcsetattr, tcsetwinsize, OptionalActions};
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC;
let controller = openpt(flags)?;
grantpt(&controller)?;
unlockpt(&controller)?;
let user = open_user(&controller, flags | OpenptFlags::CLOEXEC)?;
if let Some(termios) = termios {
tcsetattr(&user, OptionalActions::Now, termios)?;
}
if let Some(winsize) = winsize {
tcsetwinsize(&user, *winsize)?;
}
Ok(Pty { controller, user })
}
}
pub fn openpty_nocloexec(termios: Option<&Termios>, winsize: Option<&Winsize>) -> io::Result<Pty> {
#[cfg(not(any(target_os = "android", target_os = "linux")))]
{
use core::mem::{align_of, size_of, MaybeUninit};
use core::ptr::{null, null_mut};
use rustix::fd::FromRawFd;
assert_eq!(size_of::<Termios>(), size_of::<libc::termios>());
assert_eq!(align_of::<Termios>(), align_of::<libc::termios>());
let termios: *const libc::termios = match termios {
Some(termios) => {
let termios: *const Termios = termios;
termios.cast()
}
None => null(),
};
let mut libc_winsize: libc::winsize;
let winsize: *mut libc::winsize = match winsize {
Some(winsize) => {
libc_winsize = libc::winsize {
ws_row: winsize.ws_row,
ws_col: winsize.ws_col,
ws_xpixel: winsize.ws_xpixel,
ws_ypixel: winsize.ws_ypixel,
};
&mut libc_winsize
}
None => null_mut(),
};
let mut controller = MaybeUninit::<libc::c_int>::uninit();
let mut user = MaybeUninit::<libc::c_int>::uninit();
unsafe {
if libc::openpty(
controller.as_mut_ptr(),
user.as_mut_ptr(),
null_mut(),
termios as _,
winsize,
) == 0
{
let controller = OwnedFd::from_raw_fd(controller.assume_init());
let user = OwnedFd::from_raw_fd(user.assume_init());
Ok(Pty { controller, user })
} else {
Err(io::Errno::from_raw_os_error(errno::errno().0))
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
{
use rustix::pty::{grantpt, openpt, unlockpt, OpenptFlags};
use rustix::termios::{tcsetattr, tcsetwinsize, OptionalActions};
let flags = OpenptFlags::RDWR | OpenptFlags::NOCTTY;
let controller = openpt(flags)?;
grantpt(&controller)?;
unlockpt(&controller)?;
let user = open_user(&controller, flags)?;
if let Some(termios) = termios {
tcsetattr(&user, OptionalActions::Now, termios)?;
}
if let Some(winsize) = winsize {
tcsetwinsize(&user, *winsize)?;
}
Ok(Pty { controller, user })
}
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
fn set_cloexec<Fd: AsFd>(fd: Fd) -> io::Result<()> {
use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags};
let fd = fd.as_fd();
fcntl_setfd(fd, fcntl_getfd(fd)? | FdFlags::CLOEXEC)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn open_user(controller: &OwnedFd, flags: rustix::pty::OpenptFlags) -> io::Result<OwnedFd> {
use rustix::fs::{openat, Mode, CWD};
#[cfg(not(target_os = "android"))]
{
match rustix::pty::ioctl_tiocgptpeer(controller, flags) {
Ok(fd) => return Ok(fd),
Err(io::Errno::NOSYS) | Err(io::Errno::PERM) => {}
Err(e) => return Err(e),
}
}
let name = rustix::pty::ptsname(controller, Vec::new())?;
openat(CWD, name, flags.into(), Mode::empty())
}
#[cfg(not(any(target_os = "fuchsia", target_os = "illumos", target_os = "solaris")))]
pub fn login_tty<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<()> {
_login_tty(fd.into())
}
#[cfg(not(any(target_os = "fuchsia", target_os = "illumos", target_os = "solaris")))]
fn _login_tty(fd: OwnedFd) -> io::Result<()> {
#[cfg(not(any(target_os = "android", target_os = "linux")))]
unsafe {
if libc::login_tty(rustix::fd::IntoRawFd::into_raw_fd(fd)) != 0 {
return Err(io::Errno::from_raw_os_error(errno::errno().0));
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
{
rustix::process::setsid().ok();
rustix::process::ioctl_tiocsctty(&fd)?;
rustix::stdio::dup2_stdin(&fd).ok();
rustix::stdio::dup2_stdout(&fd).ok();
rustix::stdio::dup2_stderr(&fd).ok();
if rustix::fd::AsRawFd::as_raw_fd(&fd) <= 2 {
core::mem::forget(fd);
}
}
Ok(())
}
#[cfg(any(target_os = "android", target_os = "linux"))] pub unsafe fn closefrom(from: RawFd) {
use core::mem::MaybeUninit;
use core::str;
use rustix::fs::{openat, Mode, OFlags, RawDir, CWD};
let dir = openat(
CWD,
rustix::cstr!("/dev/fd"),
OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
Mode::empty(),
)
.unwrap();
let dir_raw_fd = dir.as_fd().as_raw_fd();
let mut buf = [MaybeUninit::uninit(); 1024];
let mut iter = RawDir::new(dir, &mut buf);
while let Some(entry) = iter.next() {
let entry = entry.unwrap();
let name_bytes = entry.file_name().to_bytes();
if name_bytes == b"." || name_bytes == b".." {
continue;
}
let name = str::from_utf8(name_bytes).unwrap();
let num = name.parse::<RawFd>().unwrap();
if num >= from && num != dir_raw_fd {
io::close(num);
}
}
}