diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-07 00:07:24 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-07 16:29:44 +0300 |
| commit | 3c2b46d24aa6c8011a1e198143752f2aa414d585 (patch) | |
| tree | a52280bccc824ca656f8bc986a756f30901974e4 /src/pty/backend.rs | |
| parent | 5d7e56737ef30d9de5802b0684a9428c40131dda (diff) | |
feat: add native windows beta support
Diffstat (limited to 'src/pty/backend.rs')
| -rw-r--r-- | src/pty/backend.rs | 74 |
1 files changed, 10 insertions, 64 deletions
diff --git a/src/pty/backend.rs b/src/pty/backend.rs index 5871004a..d09922b7 100644 --- a/src/pty/backend.rs +++ b/src/pty/backend.rs @@ -1,14 +1,19 @@ -use std::os::fd::{FromRawFd, OwnedFd}; +#[cfg(unix)] +mod unix; -use portable_pty::{native_pty_system, Child, CommandBuilder, PtySize}; +#[cfg(unix)] +pub(crate) use unix::*; -use crate::pty::fd; +#[cfg(windows)] +use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize}; +#[cfg(windows)] pub(crate) struct SpawnedPty { - pub master_fd: OwnedFd, + pub master: Box<dyn MasterPty + Send>, pub child: Box<dyn Child + Send + Sync>, } +#[cfg(windows)] pub(crate) fn spawn_with_portable_pty( rows: u16, cols: u16, @@ -23,72 +28,13 @@ pub(crate) fn spawn_with_portable_pty( pixel_height: 0, }) .map_err(|err| std::io::Error::other(err.to_string()))?; - let master_fd = pair - .master - .as_raw_fd() - .ok_or_else(|| std::io::Error::other("pty master fd is unavailable"))?; - let actor_fd = fd::duplicate_cloexec_fd(master_fd)?; - let actor_fd = unsafe { OwnedFd::from_raw_fd(actor_fd) }; let child = pair .slave .spawn_command(cmd) .map_err(|err| std::io::Error::other(err.to_string()))?; - drop(pair); Ok(SpawnedPty { - master_fd: actor_fd, + master: pair.master, child, }) } - -#[cfg(all(test, target_os = "linux"))] -mod tests { - use super::*; - use std::sync::{Mutex, OnceLock}; - - fn pty_fd_test_lock() -> &'static Mutex<()> { - static LOCK: OnceLock<Mutex<()>> = OnceLock::new(); - LOCK.get_or_init(|| Mutex::new(())) - } - - fn parent_pty_fd_targets() -> Vec<String> { - let Ok(entries) = std::fs::read_dir("/proc/self/fd") else { - return Vec::new(); - }; - let mut targets: Vec<String> = entries - .filter_map(Result::ok) - .filter_map(|entry| std::fs::read_link(entry.path()).ok()) - .map(|target| target.to_string_lossy().into_owned()) - .filter(|target| target.starts_with("/dev/pts/") || target == "/dev/ptmx") - .collect(); - targets.sort(); - targets - } - - fn parent_pty_fd_count() -> usize { - parent_pty_fd_targets().len() - } - - #[test] - fn portable_pty_setup_leaves_one_parent_pty_fd() { - let _guard = pty_fd_test_lock().lock().expect("pty fd test lock"); - let before = parent_pty_fd_count(); - let mut cmd = CommandBuilder::new("/bin/cat"); - cmd.env(crate::HERDR_ENV_VAR, crate::HERDR_ENV_VALUE); - - let mut spawned = - spawn_with_portable_pty(24, 80, cmd).expect("portable pty setup succeeds"); - let after_spawn = parent_pty_fd_count(); - - assert_eq!( - after_spawn, - before + 1, - "portable-pty setup should leave only the Herdr-owned master fd in the parent: {:?}", - parent_pty_fd_targets() - ); - - let _ = spawned.child.kill(); - let _ = spawned.child.wait(); - drop(spawned.master_fd); - } -} |
