rust_pty/
unix.rs

1//! Unix platform implementation for PTY operations.
2//!
3//! This module provides the Unix-specific PTY implementation, including:
4//!
5//! - PTY master/slave pair allocation via openpt/grantpt/unlockpt
6//! - Async I/O through tokio's `AsyncFd`
7//! - Child process management with proper session/controlling terminal setup
8//! - Signal handling for SIGWINCH and SIGCHLD
9//!
10//! # Platform Support
11//!
12//! This implementation works on:
13//! - Linux (using /dev/ptmx)
14//! - macOS (using /dev/ptmx)
15//! - FreeBSD and other Unix-like systems
16//!
17//! # Example
18//!
19//! ```ignore
20//! use rust_pty::unix::UnixPtySystem;
21//! use rust_pty::{PtySystem, PtyConfig};
22//!
23//! let config = PtyConfig::default();
24//! let (master, child) = UnixPtySystem::spawn("/bin/bash", &[], &config).await?;
25//! ```
26
27mod buffer;
28mod child;
29mod pty;
30mod signals;
31
32use std::ffi::OsStr;
33
34pub use buffer::PtyBuffer;
35pub use child::{UnixPtyChild, spawn_child};
36pub use pty::{UnixPtyMaster, open_slave};
37pub use signals::{
38    PtySignalEvent, SignalHandle, is_sigchld, is_sigwinch, on_window_change, sigchld, sigwinch,
39    start_signal_handler,
40};
41
42use crate::config::PtyConfig;
43use crate::error::Result;
44use crate::traits::PtySystem;
45
46/// Unix PTY system implementation.
47///
48/// This struct provides the factory methods for creating PTY sessions on Unix.
49#[derive(Debug, Clone, Copy, Default)]
50pub struct UnixPtySystem;
51
52impl PtySystem for UnixPtySystem {
53    type Master = UnixPtyMaster;
54    type Child = UnixPtyChild;
55
56    async fn spawn<S, I>(
57        program: S,
58        args: I,
59        config: &PtyConfig,
60    ) -> Result<(Self::Master, Self::Child)>
61    where
62        S: AsRef<OsStr> + Send,
63        I: IntoIterator + Send,
64        I::Item: AsRef<OsStr>,
65    {
66        // Open master PTY
67        let (master, slave_path) = UnixPtyMaster::open()?;
68
69        // Set initial window size
70        let window_size = config.window_size.into();
71        master.set_window_size(window_size)?;
72
73        // Open slave for child
74        let slave_fd = open_slave(&slave_path)?;
75
76        // Spawn child process
77        let child = spawn_child(slave_fd, program, args, config).await?;
78
79        Ok((master, child))
80    }
81}
82
83/// Convenience type alias for the default PTY system on Unix.
84pub type NativePtySystem = UnixPtySystem;
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[tokio::test]
91    async fn spawn_shell() {
92        let config = PtyConfig::default();
93        let result = UnixPtySystem::spawn_shell(&config).await;
94
95        // This may fail in some test environments, but the logic should be correct
96        if let Ok((mut master, mut child)) = result {
97            assert!(master.is_open());
98            assert!(child.is_running());
99
100            // Clean up
101            child.kill().ok();
102            master.close().ok();
103        }
104    }
105
106    #[tokio::test]
107    async fn spawn_echo() {
108        let config = PtyConfig::default();
109        let result = UnixPtySystem::spawn("echo", ["hello"], &config).await;
110
111        if let Ok((mut master, mut child)) = result {
112            // Wait for child to exit
113            let status = child.wait().await;
114            assert!(status.is_ok());
115
116            master.close().ok();
117        }
118    }
119}