Skip to main content

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//! ```no_run
20//! use rust_pty::unix::UnixPtySystem;
21//! use rust_pty::{PtySystem, PtyConfig};
22//!
23//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
24//! let config = PtyConfig::default();
25//! let args: [&str; 0] = [];
26//! let (master, child) = UnixPtySystem::spawn("/bin/bash", args, &config).await?;
27//! # let _ = (master, child);
28//! # Ok(()) }
29//! ```
30
31mod buffer;
32mod child;
33mod pty;
34mod signals;
35
36use std::ffi::OsStr;
37
38pub use buffer::PtyBuffer;
39pub use child::{UnixPtyChild, spawn_child};
40pub use pty::{UnixPtyMaster, open_slave};
41pub use signals::{
42    PtySignalEvent, SignalHandle, is_sigchld, is_sigwinch, on_window_change, sigchld, sigwinch,
43    start_signal_handler,
44};
45
46use crate::config::PtyConfig;
47use crate::error::Result;
48use crate::traits::PtySystem;
49
50/// Unix PTY system implementation.
51///
52/// This struct provides the factory methods for creating PTY sessions on Unix.
53#[derive(Debug, Clone, Copy, Default)]
54pub struct UnixPtySystem;
55
56impl PtySystem for UnixPtySystem {
57    type Master = UnixPtyMaster;
58    type Child = UnixPtyChild;
59
60    async fn spawn<S, I>(
61        program: S,
62        args: I,
63        config: &PtyConfig,
64    ) -> Result<(Self::Master, Self::Child)>
65    where
66        S: AsRef<OsStr> + Send,
67        I: IntoIterator + Send,
68        I::Item: AsRef<OsStr>,
69    {
70        // Open master PTY
71        let (master, slave_path) = UnixPtyMaster::open()?;
72
73        // Set initial window size
74        let window_size = config.window_size.into();
75        master.set_window_size(window_size)?;
76
77        // Open slave for child
78        let slave_fd = open_slave(&slave_path)?;
79
80        // Spawn child process
81        let child = spawn_child(slave_fd, program, args, config).await?;
82
83        Ok((master, child))
84    }
85}
86
87/// Convenience type alias for the default PTY system on Unix.
88pub type NativePtySystem = UnixPtySystem;
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[tokio::test]
95    async fn spawn_shell() {
96        let config = PtyConfig::default();
97        let result = UnixPtySystem::spawn_shell(&config).await;
98
99        // This may fail in some test environments, but the logic should be correct
100        if let Ok((mut master, mut child)) = result {
101            assert!(master.is_open());
102            assert!(child.is_running());
103
104            // Clean up
105            child.kill().ok();
106            master.close().ok();
107        }
108    }
109
110    #[tokio::test]
111    async fn spawn_echo() {
112        let config = PtyConfig::default();
113        let result = UnixPtySystem::spawn("echo", ["hello"], &config).await;
114
115        if let Ok((mut master, mut child)) = result {
116            // Wait for child to exit
117            let status = child.wait().await;
118            assert!(status.is_ok());
119
120            master.close().ok();
121        }
122    }
123}