use crate::run_with_preexec;
use nix::fcntl::{OFlag, open};
use nix::libc;
use nix::pty::{PtyMaster, grantpt, posix_openpt, ptsname, unlockpt};
use nix::sys::stat::Mode;
use nix::sys::termios::tcgetsid;
use nix::unistd::{close, getpid, setsid};
use std::ffi::c_int;
use std::os::fd::{AsRawFd as _, OwnedFd};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
pub fn run_with_pty(name: &str) {
let master = prepare_pty_master();
let slave_path = pty_slave_path(&master);
let slave = open_pty_slave(&slave_path);
let raw_master = master.as_raw_fd();
let raw_slave = slave.as_raw_fd();
unsafe {
run_with_preexec(name, move || {
close(raw_master)?;
prepare_as_slave(&slave_path)?;
close(raw_slave)?;
Ok(())
});
}
}
fn prepare_pty_master() -> PtyMaster {
let master = posix_openpt(OFlag::O_RDWR | OFlag::O_NOCTTY).expect("posix_openpt failed");
grantpt(&master).expect("grantpt failed");
unlockpt(&master).expect("unlockpt failed");
master
}
static PTSNAME_MUTEX: Mutex<()> = Mutex::new(());
fn pty_slave_path(master: &PtyMaster) -> PathBuf {
let _lock = PTSNAME_MUTEX.lock().expect("PTSNAME_MUTEX poisoned");
unsafe { ptsname(master) }.expect("ptsname failed").into()
}
fn open_pty_slave(path: &Path) -> OwnedFd {
open(path, OFlag::O_RDWR | OFlag::O_NOCTTY, Mode::empty()).expect("open failed")
}
fn prepare_as_slave(slave_path: &Path) -> nix::Result<()> {
setsid()?;
let fd = open(slave_path, OFlag::O_RDWR, Mode::empty())?;
unsafe { libc::ioctl(fd.as_raw_fd(), libc::TIOCSCTTY as _, 0 as c_int) };
if tcgetsid(fd) == Ok(getpid()) {
Ok(())
} else {
Err(nix::Error::ENOTSUP)
}
}