-
Notifications
You must be signed in to change notification settings - Fork 3
add ptsname_r shim for macos #4
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
Changes from all commits
9b13853
88b85ef
72e4d52
66a840d
e08ee1b
befd3af
3bca717
559aa75
a5c5e93
961828d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| //! macOS implementation of ptsname_r | ||
| //! | ||
| //! 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(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( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a "Saftey" comment that explains the formal precondtions that any call site must meet in order to use this function correctly (should be pretty easy, just stuff like "buf needs to point to allocated memory" and "buflen can't be longer than the allocation" and "fd must be an open file descriptor").
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added; 559aa75 |
||
| 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 libc::c_ulong, &mut ioctl_buf) != 0 { | ||
| return *libc::__error(); | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| std::ptr::copy(ioctl_buf.as_ptr(), buf, len); | ||
|
|
||
| 0 | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use std::ffi::CStr; | ||
|
|
||
| #[cfg(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) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a Saftey comment. I know it's just a test, but having them on every block makes auditing easier.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| assert!(master_fd >= 0, "Failed to open master PTY"); | ||
|
|
||
| // Safety: master_fd is a valid open file descriptor. | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a Saftey comment. I know it's just a test, but having them on every block makes auditing easier.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated each unsafe section in the tests, not sure about the wording though. How all these sounds like? 559aa75 |
||
| 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()) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey comment.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // Safety: closing the fd opened above. | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey comment.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey comment.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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(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) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| if master_fd >= 0 { | ||
| // Safety: master_fd is a valid open file descriptor. | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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()) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // Safety: closing the fd opened above. | ||
| unsafe { | ||
| libc::close(master_fd); | ||
| } | ||
|
|
||
| assert_eq!(result, libc::ERANGE); | ||
| } | ||
| } | ||
|
|
||
| #[cfg(target_os = "macos")] | ||
| #[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()) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| assert_ne!(result, 0, "Expected non-zero error code for invalid fd"); | ||
| } | ||
|
|
||
| #[cfg(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) }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| if master_fd >= 0 { | ||
| // Safety: master_fd is a valid open file descriptor. | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Saftey
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| assert_eq!(result, libc::EINVAL); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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("bash").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"); | ||
| } | ||
|
Comment on lines
+31
to
+39
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that seems fine to me.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cheers |
||
|
|
||
| let _ = cmd.status(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to maintain the style that every unsafe block has a saftey comment above it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mistakenly moved this one, updated; 559aa75