1 //! libc syscalls supporting `rustix::pty`.
2
3 use crate::backend::c;
4 use crate::backend::conv::{borrowed_fd, ret};
5 use crate::fd::BorrowedFd;
6 use crate::io;
7 #[cfg(all(
8 feature = "alloc",
9 any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia")
10 ))]
11 use {
12 crate::ffi::{CStr, CString},
13 crate::path::SMALL_PATH_BUFFER_SIZE,
14 alloc::borrow::ToOwned,
15 alloc::vec::Vec,
16 };
17
18 #[cfg(not(linux_kernel))]
19 use crate::{backend::conv::ret_owned_fd, fd::OwnedFd, pty::OpenptFlags};
20
21 #[cfg(not(linux_kernel))]
22 #[inline]
openpt(flags: OpenptFlags) -> io::Result<OwnedFd>23 pub(crate) fn openpt(flags: OpenptFlags) -> io::Result<OwnedFd> {
24 unsafe { ret_owned_fd(c::posix_openpt(flags.bits() as _)) }
25 }
26
27 #[cfg(all(
28 feature = "alloc",
29 any(apple, linux_like, target_os = "freebsd", target_os = "fuchsia")
30 ))]
31 #[inline]
ptsname(fd: BorrowedFd<'_>, mut buffer: Vec<u8>) -> io::Result<CString>32 pub(crate) fn ptsname(fd: BorrowedFd<'_>, mut buffer: Vec<u8>) -> io::Result<CString> {
33 // This code would benefit from having a better way to read into
34 // uninitialized memory, but that requires `unsafe`.
35 buffer.clear();
36 buffer.reserve(SMALL_PATH_BUFFER_SIZE);
37 buffer.resize(buffer.capacity(), 0_u8);
38
39 loop {
40 // On platforms with `ptsname_r`, use it.
41 #[cfg(any(linux_like, target_os = "fuchsia"))]
42 let r = unsafe { c::ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len()) };
43
44 // FreeBSD 12 doesn't have `ptsname_r`.
45 #[cfg(target_os = "freebsd")]
46 let r = unsafe {
47 weak! {
48 fn ptsname_r(
49 c::c_int,
50 *mut c::c_char,
51 c::size_t
52 ) -> c::c_int
53 }
54 if let Some(func) = ptsname_r.get() {
55 func(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len())
56 } else {
57 libc::ENOSYS
58 }
59 };
60
61 // macOS 10.13.4 has `ptsname_r`; use it if we have it, otherwise fall
62 // back to calling the underlying ioctl directly.
63 #[cfg(apple)]
64 let r = unsafe {
65 weak! { fn ptsname_r(c::c_int, *mut c::c_char, c::size_t) -> c::c_int }
66
67 if let Some(libc_ptsname_r) = ptsname_r.get() {
68 libc_ptsname_r(borrowed_fd(fd), buffer.as_mut_ptr().cast(), buffer.len())
69 } else {
70 // The size declared in the `TIOCPTYGNAME` macro in
71 // sys/ttycom.h is 128.
72 let mut name: [u8; 128] = [0_u8; 128];
73 match c::ioctl(borrowed_fd(fd), c::TIOCPTYGNAME as _, &mut name) {
74 0 => {
75 let len = CStr::from_ptr(name.as_ptr().cast()).to_bytes().len();
76 std::ptr::copy_nonoverlapping(name.as_ptr(), buffer.as_mut_ptr(), len + 1);
77 0
78 }
79 _ => libc_errno::errno().0,
80 }
81 }
82 };
83
84 if r == 0 {
85 return Ok(unsafe { CStr::from_ptr(buffer.as_ptr().cast()).to_owned() });
86 }
87 if r != c::ERANGE {
88 return Err(io::Errno::from_raw_os_error(r));
89 }
90
91 // Use `Vec` reallocation strategy to grow capacity exponentially.
92 buffer.reserve(1);
93 buffer.resize(buffer.capacity(), 0_u8);
94 }
95 }
96
97 #[inline]
unlockpt(fd: BorrowedFd<'_>) -> io::Result<()>98 pub(crate) fn unlockpt(fd: BorrowedFd<'_>) -> io::Result<()> {
99 unsafe { ret(c::unlockpt(borrowed_fd(fd))) }
100 }
101
102 #[cfg(not(linux_kernel))]
103 #[inline]
grantpt(fd: BorrowedFd<'_>) -> io::Result<()>104 pub(crate) fn grantpt(fd: BorrowedFd<'_>) -> io::Result<()> {
105 unsafe { ret(c::grantpt(borrowed_fd(fd))) }
106 }
107