rustix/backend/libc/pty/
syscalls.rs

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