From 9b13853f4c3209f3d523dc59de7c17a955393173 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Sun, 12 Oct 2025 00:25:15 +0300 Subject: [PATCH 01/10] add ioctl based ptsname_r implementation for macos --- src/fork/pty/master/mod.rs | 16 ++- src/fork/pty/master/ptsname_r_macos.rs | 134 +++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 src/fork/pty/master/ptsname_r_macos.rs diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index da26983..c1ad537 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -1,5 +1,8 @@ mod err; +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod ptsname_r_macos; + use descriptor::Descriptor; pub use self::err::{MasterError, Result}; @@ -66,13 +69,18 @@ impl Master { /// subsequent calls. pub fn ptsname_r(&self, buf: &mut [u8]) -> Result<()> { if let Some(fd) = self.pty { - // Safety: the vector's memory is valid for the duration - // of the call unsafe { let data: *mut u8 = &mut buf[0]; - match libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()) { + + #[cfg(any(target_os = "linux", target_os = "android"))] + let result = libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + let result = ptsname_r_macos::ptsname_r(fd, data as *mut libc::c_char, buf.len()); + + match result { 0 => Ok(()), - _ => Err(MasterError::PtsnameError), // should probably capture errno + _ => Err(MasterError::PtsnameError), } } } else { diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs new file mode 100644 index 0000000..8b989cf --- /dev/null +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -0,0 +1,134 @@ +//! macOS/iOS implementation of ptsname_r +//! +//! As `ptsname_r()` is not available on macOS/iOS, this provides a compatible +//! implementation using the `TIOCPTYGNAME` ioctl syscall. +//! +//! Based on: https://tarq.net/posts/ptsname-on-osx-with-rust/ + +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[inline] +pub unsafe fn ptsname_r( + fd: libc::c_int, + buf: *mut libc::c_char, + buflen: libc::size_t, +) -> libc::c_int { + const IOCTL_BUF_SIZE: usize = 128; + + if buf.is_null() || buflen == 0 { + return libc::EINVAL; + } + + let mut ioctl_buf: [libc::c_char; IOCTL_BUF_SIZE] = [0; IOCTL_BUF_SIZE]; + + if libc::ioctl(fd, libc::TIOCPTYGNAME as u64, &mut ioctl_buf) != 0 { + return *libc::__error(); + } + + let mut len = 0; + while len < IOCTL_BUF_SIZE && ioctl_buf[len] != 0 { + len += 1; + } + len += 1; + + if len > buflen { + return libc::ERANGE; + } + + libc::memcpy( + buf as *mut libc::c_void, + ioctl_buf.as_ptr() as *const libc::c_void, + len, + ); + + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::CStr; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + fn test_ptsname_r_retrieves_valid_name() { + let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; + assert!(master_fd >= 0, "Failed to open master PTY"); + + unsafe { + assert_eq!(libc::grantpt(master_fd), 0, "grantpt failed"); + assert_eq!(libc::unlockpt(master_fd), 0, "unlockpt failed"); + } + + let mut buf = vec![0u8; 1024]; + let result = + unsafe { ptsname_r(master_fd, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; + + unsafe { + libc::close(master_fd); + } + + assert_eq!(result, 0, "ptsname_r failed with error code: {}", result); + + let name = unsafe { CStr::from_ptr(buf.as_ptr() as *const libc::c_char) }; + let name_str = name.to_str().expect("Invalid UTF-8 in PTY name"); + + assert!( + name_str.starts_with("/dev/ttys") || name_str.starts_with("/dev/pty"), + "Unexpected PTY name format: {}", + name_str + ); + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + fn test_ptsname_r_buffer_too_small() { + let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; + + if master_fd >= 0 { + unsafe { + libc::grantpt(master_fd); + libc::unlockpt(master_fd); + } + + let mut buf = [0u8; 2]; + let result = + unsafe { ptsname_r(master_fd, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; + + unsafe { + libc::close(master_fd); + } + + assert_eq!(result, libc::ERANGE); + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + fn test_ptsname_r_invalid_fd() { + let mut buf = vec![0u8; 1024]; + let result = unsafe { ptsname_r(-1, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; + + assert_ne!(result, 0, "Expected non-zero error code for invalid fd"); + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + #[test] + fn test_ptsname_r_null_buffer() { + let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; + + if master_fd >= 0 { + unsafe { + libc::grantpt(master_fd); + libc::unlockpt(master_fd); + } + + let result = unsafe { ptsname_r(master_fd, std::ptr::null_mut(), 1024) }; + + unsafe { + libc::close(master_fd); + } + + assert_eq!(result, libc::EINVAL); + } + } +} From 88b85efc431472de589632e037eaf3bf59f40dd5 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Sun, 12 Oct 2025 00:28:43 +0300 Subject: [PATCH 02/10] use sh instead of bash in read/write test on macos provided bash, by default it outputs; The default interactive shell is now zsh. To update your account to use zsh, please run `chsh -s /bin/zsh`. --- tests/it_can_read_write.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/it_can_read_write.rs b/tests/it_can_read_write.rs index d32b6df..faf8327 100644 --- a/tests/it_can_read_write.rs +++ b/tests/it_can_read_write.rs @@ -28,6 +28,6 @@ fn it_can_read_write() { assert_eq!(read_line(&mut master).trim(), "readme!"); let _ = master.write("exit\n".to_string().as_bytes()); } else { - let _ = Command::new("bash").env_clear().status(); + let _ = Command::new("sh").env_clear().status(); } } From 72e4d5240c2785026e894cca3c7d20b6c3ef9788 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Mon, 3 Nov 2025 22:26:37 +0300 Subject: [PATCH 03/10] add linux ptsname_r safety comment back --- src/fork/pty/master/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index c1ad537..b4b90d3 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -73,6 +73,8 @@ impl Master { let data: *mut u8 = &mut buf[0]; #[cfg(any(target_os = "linux", target_os = "android"))] + // Safety: the vector's memory is valid for the duration + // of the call let result = libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()); #[cfg(any(target_os = "macos", target_os = "ios"))] From 66a840d4eb2695cc14927ffc4213228f3731be7e Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Mon, 3 Nov 2025 22:33:32 +0300 Subject: [PATCH 04/10] cast TIOCPTYGNAME to libc::c_ulong --- src/fork/pty/master/ptsname_r_macos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs index 8b989cf..5e7dfde 100644 --- a/src/fork/pty/master/ptsname_r_macos.rs +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -20,7 +20,7 @@ pub unsafe fn ptsname_r( let mut ioctl_buf: [libc::c_char; IOCTL_BUF_SIZE] = [0; IOCTL_BUF_SIZE]; - if libc::ioctl(fd, libc::TIOCPTYGNAME as u64, &mut ioctl_buf) != 0 { + if libc::ioctl(fd, libc::TIOCPTYGNAME as libc::c_ulong, &mut ioctl_buf) != 0 { return *libc::__error(); } From e08ee1bb313cb2d793d6e93b6ff8d8ad60c15f42 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Mon, 3 Nov 2025 22:38:15 +0300 Subject: [PATCH 05/10] remove ios references --- src/fork/pty/master/mod.rs | 4 ++-- src/fork/pty/master/ptsname_r_macos.rs | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index b4b90d3..16a27e1 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -1,6 +1,6 @@ mod err; -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos"))] mod ptsname_r_macos; use descriptor::Descriptor; @@ -77,7 +77,7 @@ impl Master { // of the call let result = libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()); - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos"))] let result = ptsname_r_macos::ptsname_r(fd, data as *mut libc::c_char, buf.len()); match result { diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs index 5e7dfde..2ba3dfe 100644 --- a/src/fork/pty/master/ptsname_r_macos.rs +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -1,12 +1,11 @@ -//! macOS/iOS implementation of ptsname_r +//! macOS implementation of ptsname_r //! -//! As `ptsname_r()` is not available on macOS/iOS, this provides a compatible +//! As `ptsname_r()` is not available on macOS, this provides a compatible //! implementation using the `TIOCPTYGNAME` ioctl syscall. //! //! Based on: https://tarq.net/posts/ptsname-on-osx-with-rust/ -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[inline] +#[cfg(any(target_os = "macos"))] pub unsafe fn ptsname_r( fd: libc::c_int, buf: *mut libc::c_char, @@ -48,7 +47,7 @@ mod tests { use super::*; use std::ffi::CStr; - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_retrieves_valid_name() { let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; @@ -79,7 +78,7 @@ mod tests { ); } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_buffer_too_small() { let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; @@ -102,7 +101,7 @@ mod tests { } } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_invalid_fd() { let mut buf = vec![0u8; 1024]; @@ -111,7 +110,7 @@ mod tests { assert_ne!(result, 0, "Expected non-zero error code for invalid fd"); } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_null_buffer() { let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; From befd3af79a17b744391757959a1f199c79c95b04 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Mon, 3 Nov 2025 22:52:39 +0300 Subject: [PATCH 06/10] add comments back --- src/fork/pty/master/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index 16a27e1..bd93925 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -82,7 +82,7 @@ impl Master { match result { 0 => Ok(()), - _ => Err(MasterError::PtsnameError), + _ => Err(MasterError::PtsnameError), // should probably capture errno } } } else { From 3bca7173b1c3d93a5abf7d916ac5448c0d63eb78 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Fri, 7 Nov 2025 18:48:13 +0300 Subject: [PATCH 07/10] ptsname_r_macos: use libc::memcpy to count up to null terminator --- src/fork/pty/master/ptsname_r_macos.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs index 2ba3dfe..f727a01 100644 --- a/src/fork/pty/master/ptsname_r_macos.rs +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -23,21 +23,20 @@ pub unsafe fn ptsname_r( return *libc::__error(); } - let mut len = 0; - while len < IOCTL_BUF_SIZE && ioctl_buf[len] != 0 { - len += 1; - } - len += 1; + let null_ptr = libc::memchr(ioctl_buf.as_ptr() as *const libc::c_void, 0, IOCTL_BUF_SIZE); + + let len = if null_ptr.is_null() { + IOCTL_BUF_SIZE + } else { + let offset = (null_ptr as usize) - (ioctl_buf.as_ptr() as usize); + offset + 1 // include null terminator. + }; if len > buflen { return libc::ERANGE; } - libc::memcpy( - buf as *mut libc::c_void, - ioctl_buf.as_ptr() as *const libc::c_void, - len, - ); + std::ptr::copy(ioctl_buf.as_ptr(), buf, len); 0 } From 559aa75a7b1347c612aee43aa0174a89d30ba53f Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Fri, 7 Nov 2025 18:48:58 +0300 Subject: [PATCH 08/10] update safety comments --- src/fork/pty/master/mod.rs | 4 ++-- src/fork/pty/master/ptsname_r_macos.rs | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index bd93925..08c1c24 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -69,12 +69,12 @@ impl Master { /// subsequent calls. pub fn ptsname_r(&self, buf: &mut [u8]) -> Result<()> { if let Some(fd) = self.pty { + // Safety: the vector's memory is valid for the duration + // of the call unsafe { let data: *mut u8 = &mut buf[0]; #[cfg(any(target_os = "linux", target_os = "android"))] - // Safety: the vector's memory is valid for the duration - // of the call let result = libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()); #[cfg(any(target_os = "macos"))] diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs index f727a01..b6fb76e 100644 --- a/src/fork/pty/master/ptsname_r_macos.rs +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -6,6 +6,12 @@ //! Based on: https://tarq.net/posts/ptsname-on-osx-with-rust/ #[cfg(any(target_os = "macos"))] +/// # Safety +/// +/// Callers must uphold the following invariants: +/// - `fd` must refer to an open master PTY file descriptor. +/// - `buf` must point to a valid allocation with capacity of at least `buflen` bytes and the +/// allocation must remain valid throughout the function call. pub unsafe fn ptsname_r( fd: libc::c_int, buf: *mut libc::c_char, @@ -49,24 +55,29 @@ mod tests { #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_retrieves_valid_name() { + // Safety: calling libc function with valid flags. let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; assert!(master_fd >= 0, "Failed to open master PTY"); + // Safety: master_fd is a valid open file descriptor. unsafe { assert_eq!(libc::grantpt(master_fd), 0, "grantpt failed"); assert_eq!(libc::unlockpt(master_fd), 0, "unlockpt failed"); } let mut buf = vec![0u8; 1024]; + // Safety: master_fd is valid, buf is a properly sized allocation. let result = unsafe { ptsname_r(master_fd, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; + // Safety: closing the fd opened above. unsafe { libc::close(master_fd); } assert_eq!(result, 0, "ptsname_r failed with error code: {}", result); + // Safety: buf contains a null-terminated string from ptsname_r. let name = unsafe { CStr::from_ptr(buf.as_ptr() as *const libc::c_char) }; let name_str = name.to_str().expect("Invalid UTF-8 in PTY name"); @@ -80,18 +91,23 @@ mod tests { #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_buffer_too_small() { + // Safety: calling libc function with valid flags. let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; if master_fd >= 0 { + // Safety: master_fd is a valid open file descriptor. unsafe { libc::grantpt(master_fd); libc::unlockpt(master_fd); } let mut buf = [0u8; 2]; + // Safety: master_fd is valid, buf is a properly sized allocation though intentionally + // too small. let result = unsafe { ptsname_r(master_fd, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; + // Safety: closing the fd opened above. unsafe { libc::close(master_fd); } @@ -104,6 +120,7 @@ mod tests { #[test] fn test_ptsname_r_invalid_fd() { let mut buf = vec![0u8; 1024]; + // Safety: buf is a properly sized allocation. let result = unsafe { ptsname_r(-1, buf.as_mut_ptr() as *mut libc::c_char, buf.len()) }; assert_ne!(result, 0, "Expected non-zero error code for invalid fd"); @@ -112,16 +129,20 @@ mod tests { #[cfg(any(target_os = "macos"))] #[test] fn test_ptsname_r_null_buffer() { + // Safety: calling libc function with valid flags. let master_fd = unsafe { libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY) }; if master_fd >= 0 { + // Safety: master_fd is a valid open file descriptor. unsafe { libc::grantpt(master_fd); libc::unlockpt(master_fd); } + // Safety: function handles null buffer safely. let result = unsafe { ptsname_r(master_fd, std::ptr::null_mut(), 1024) }; + // Safety: closing the fd opened above. unsafe { libc::close(master_fd); } From a5c5e9395820bb17f62cd27a83ebef03a3bb0471 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Fri, 7 Nov 2025 21:15:04 +0300 Subject: [PATCH 09/10] make cranky happy --- src/descriptor/err.rs | 1 - src/fork/pty/master/err.rs | 3 --- src/fork/pty/master/mod.rs | 8 ++++---- src/fork/pty/master/ptsname_r_macos.rs | 12 ++++++------ src/fork/pty/slave/err.rs | 1 - 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/descriptor/err.rs b/src/descriptor/err.rs index a896dd4..39a1298 100644 --- a/src/descriptor/err.rs +++ b/src/descriptor/err.rs @@ -28,7 +28,6 @@ impl Error for DescriptorError { } /// The function `cause` returns the lower-level cause of this error, if any. - fn cause(&self) -> Option<&dyn Error> { None } diff --git a/src/fork/pty/master/err.rs b/src/fork/pty/master/err.rs index 0f63fd6..e71fd8f 100644 --- a/src/fork/pty/master/err.rs +++ b/src/fork/pty/master/err.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::fmt; /// The alias `Result` learns `MasterError` possibility. - pub type Result = ::std::result::Result; /// The enum `MasterError` defines the possible errors from constructor Master. @@ -18,7 +17,6 @@ pub enum MasterError { impl fmt::Display for MasterError { /// The function `fmt` formats the value using the given formatter. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", ::errno::errno()) } @@ -26,7 +24,6 @@ impl fmt::Display for MasterError { impl Error for MasterError { /// The function `description` returns a short description of the error. - fn description(&self) -> &str { match *self { MasterError::BadDescriptor(_) => "the descriptor as occured an error", diff --git a/src/fork/pty/master/mod.rs b/src/fork/pty/master/mod.rs index 08c1c24..a4170f1 100644 --- a/src/fork/pty/master/mod.rs +++ b/src/fork/pty/master/mod.rs @@ -1,6 +1,6 @@ mod err; -#[cfg(any(target_os = "macos"))] +#[cfg(target_os = "macos")] mod ptsname_r_macos; use descriptor::Descriptor; @@ -77,7 +77,7 @@ impl Master { #[cfg(any(target_os = "linux", target_os = "android"))] let result = libc::ptsname_r(fd, data as *mut libc::c_char, buf.len()); - #[cfg(any(target_os = "macos"))] + #[cfg(target_os = "macos")] let result = ptsname_r_macos::ptsname_r(fd, data as *mut libc::c_char, buf.len()); match result { @@ -107,7 +107,7 @@ impl io::Read for Master { } } } else { - Err(io::Error::new(io::ErrorKind::Other, "already closed")) + Err(io::Error::other("already closed")) } } } @@ -122,7 +122,7 @@ impl io::Write for Master { } } } else { - Err(io::Error::new(io::ErrorKind::Other, "already closed")) + Err(io::Error::other("already closed")) } } diff --git a/src/fork/pty/master/ptsname_r_macos.rs b/src/fork/pty/master/ptsname_r_macos.rs index b6fb76e..5df3072 100644 --- a/src/fork/pty/master/ptsname_r_macos.rs +++ b/src/fork/pty/master/ptsname_r_macos.rs @@ -5,13 +5,13 @@ //! //! Based on: https://tarq.net/posts/ptsname-on-osx-with-rust/ -#[cfg(any(target_os = "macos"))] +#[cfg(target_os = "macos")] /// # Safety /// /// Callers must uphold the following invariants: /// - `fd` must refer to an open master PTY file descriptor. /// - `buf` must point to a valid allocation with capacity of at least `buflen` bytes and the -/// allocation must remain valid throughout the function call. +/// allocation must remain valid throughout the function call. pub unsafe fn ptsname_r( fd: libc::c_int, buf: *mut libc::c_char, @@ -52,7 +52,7 @@ mod tests { use super::*; use std::ffi::CStr; - #[cfg(any(target_os = "macos"))] + #[cfg(target_os = "macos")] #[test] fn test_ptsname_r_retrieves_valid_name() { // Safety: calling libc function with valid flags. @@ -88,7 +88,7 @@ mod tests { ); } - #[cfg(any(target_os = "macos"))] + #[cfg(target_os = "macos")] #[test] fn test_ptsname_r_buffer_too_small() { // Safety: calling libc function with valid flags. @@ -116,7 +116,7 @@ mod tests { } } - #[cfg(any(target_os = "macos"))] + #[cfg(target_os = "macos")] #[test] fn test_ptsname_r_invalid_fd() { let mut buf = vec![0u8; 1024]; @@ -126,7 +126,7 @@ mod tests { assert_ne!(result, 0, "Expected non-zero error code for invalid fd"); } - #[cfg(any(target_os = "macos"))] + #[cfg(target_os = "macos")] #[test] fn test_ptsname_r_null_buffer() { // Safety: calling libc function with valid flags. diff --git a/src/fork/pty/slave/err.rs b/src/fork/pty/slave/err.rs index 88cc272..6ddfe95 100644 --- a/src/fork/pty/slave/err.rs +++ b/src/fork/pty/slave/err.rs @@ -3,7 +3,6 @@ use std::error::Error; use std::fmt; /// The alias `Result` learns `SlaveError` possibility. - pub type Result = ::std::result::Result; /// The enum `SlaveError` defines the possible errors from constructor Slave. From 961828deab65fdad7cdd38774b4e85a6229eb833 Mon Sep 17 00:00:00 2001 From: Selman Kayrancioglu Date: Fri, 7 Nov 2025 23:36:50 +0300 Subject: [PATCH 10/10] use bash i read/write tests, silence deprecation warning on macos --- tests/it_can_read_write.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/it_can_read_write.rs b/tests/it_can_read_write.rs index faf8327..89bb259 100644 --- a/tests/it_can_read_write.rs +++ b/tests/it_can_read_write.rs @@ -28,6 +28,16 @@ fn it_can_read_write() { assert_eq!(read_line(&mut master).trim(), "readme!"); let _ = master.write("exit\n".to_string().as_bytes()); } else { - let _ = Command::new("sh").env_clear().status(); + let mut cmd = Command::new("bash"); + cmd.env_clear(); + + // On macOS, silence the deprecation warning; + // https://github.com/apple-oss-distributions/bash/blob/e86b2aa8e37a31f8fce56366d1abaf08a3fac7d2/bash-3.2/shell.c#L760-L765 + #[cfg(target_os = "macos")] + { + cmd.env("BASH_SILENCE_DEPRECATION_WARNING", "1"); + } + + let _ = cmd.status(); } }