Skip to content

Provide and emulate ptsname_r on macOS and iOS by using the TIOCPTYGNAME syscall. #742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#739](https://github.com/nix-rust/nix/pull/739))
- Expose `signalfd` module on Android as well.
([#739](https://github.com/nix-rust/nix/pull/739))
- Added the `ptsname_r` function for macOS and iOS that emulates Linux's `ptsname_r` behavior.
([#742](https://github.com/nix-rust/nix/pull/742))

### Changed
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
Expand Down
37 changes: 36 additions & 1 deletion src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,41 @@ pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
Ok(name)
}

/// (emulated on macOS) Get the name of the slave pseudoterminal (see
/// [ptsname(3)](http://man7.org/linux/man-pages/man3/ptsname.3.html))
///
/// returns the name of the slave pseudoterminal device corresponding to the master
/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
/// POSIX standard and is instead a Linux-specific extension.
///
/// This value is useful for opening the slave ptty once the master has already been opened with
/// `posix_openpt()`.
///
/// As `ptsname_r()` is Linux-specific, this implementation emulates `ptsname_r()` through
/// the `TIOCPTYGNAME` syscall on macOS.
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[inline]
pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
// This is based on
// https://blog.tarq.io/ptsname-on-osx-with-rust/
// and its derivative
// https://github.com/philippkeller/rexpect/blob/a71dd02/src/process.rs#L67
use libc::{ioctl, TIOCPTYGNAME, c_ulong};

// the buffer size on OSX is 128, defined by sys/ttycom.h
let buf: [i8; 128] = [0; 128];

unsafe {
match ioctl(fd.as_raw_fd(), TIOCPTYGNAME as c_ulong, &buf) {
0 => {
let res = CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned();
Ok(res)
}
_ => Err(Error::last()),
}
}
}

/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
/// [unlockpt(3)](http://man7.org/linux/man-pages/man3/unlockpt.3.html))
///
Expand All @@ -187,7 +222,7 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {

/// Create a new pseudoterminal, returning the slave and master file descriptors
/// in `OpenptyResult`
/// (see [openpty](http://man7.org/linux/man-pages/man3/openpty.3.html)).
/// (see [openpty](http://man7.org/linux/man-pages/man3/openpty.3.html)).
///
/// If `winsize` is not `None`, the window size of the slave will be set to
/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
Expand Down
8 changes: 4 additions & 4 deletions test/test_pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn test_explicit_close() {

/// Test equivalence of `ptsname` and `ptsname_r`
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos", target_os = "ios"))]
fn test_ptsname_equivalence() {
#[allow(unused_variables)]
let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
Expand All @@ -43,7 +43,7 @@ fn test_ptsname_equivalence() {
/// Test data copying of `ptsname`
// TODO need to run in a subprocess, since ptsname is non-reentrant
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos", target_os = "ios"))]
fn test_ptsname_copy() {
#[allow(unused_variables)]
let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
Expand All @@ -63,7 +63,7 @@ fn test_ptsname_copy() {

/// Test data copying of `ptsname_r`
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos", target_os = "ios"))]
fn test_ptsname_r_copy() {
// Open a new PTTY master
let master_fd = posix_openpt(O_RDWR).unwrap();
Expand All @@ -78,7 +78,7 @@ fn test_ptsname_r_copy() {

/// Test that `ptsname` returns different names for different devices
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos", target_os = "ios"))]
fn test_ptsname_unique() {
#[allow(unused_variables)]
let m = ::PTSNAME_MTX.lock().expect("Mutex got poisoned by another test");
Expand Down