Skip to content

Commit c2699e3

Browse files
authored
Add a rustix::pty module. (#673)
Add a `rustix::pty` module, providing functions that wrap `posix_openpt`, `grantpt`, `unlockpt`, `ptsname`, and so on.
1 parent 94edb27 commit c2699e3

File tree

17 files changed

+482
-5
lines changed

17 files changed

+482
-5
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ param = ["fs"]
166166
# Enable this to enable `rustix::io::proc_self_*` (on Linux) and `ttyname`.
167167
procfs = ["once_cell", "itoa", "fs"]
168168

169+
# Enable `rustix::pty::*`.
170+
pty = ["itoa", "fs"]
171+
169172
# Enable `rustix::termios::*`.
170173
termios = []
171174

@@ -188,6 +191,7 @@ all-apis = [
188191
"param",
189192
"process",
190193
"procfs",
194+
"pty",
191195
"rand",
192196
"runtime",
193197
"termios",

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ by default. The rest of the API is conditional with cargo feature flags:
6363
| `net` | [`rustix::net`] and [`rustix::path`]—Network-related operations.
6464
| `param` | [`rustix::param`]—Process parameters.
6565
| `process` | [`rustix::process`]—Process-associated operations.
66+
| `pty` | [`rustix::pty`]—Pseduoterminal operations.
6667
| `rand` | [`rustix::rand`]—Random-related operations.
6768
| `termios` | [`rustix::termios`]—Terminal I/O stream operations.
6869
| `thread` | [`rustix::thread`]—Thread-associated operations.
@@ -76,6 +77,7 @@ by default. The rest of the API is conditional with cargo feature flags:
7677
[`rustix::net`]: https://docs.rs/rustix/*/rustix/net/index.html
7778
[`rustix::param`]: https://docs.rs/rustix/*/rustix/param/index.html
7879
[`rustix::process`]: https://docs.rs/rustix/*/rustix/process/index.html
80+
[`rustix::pty`]: https://docs.rs/rustix/*/rustix/pty/index.html
7981
[`rustix::rand`]: https://docs.rs/rustix/*/rustix/rand/index.html
8082
[`rustix::termios`]: https://docs.rs/rustix/*/rustix/termios/index.html
8183
[`rustix::thread`]: https://docs.rs/rustix/*/rustix/thread/index.html

src/backend/libc/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ pub(crate) mod param;
7272
#[cfg(not(windows))]
7373
pub(crate) mod process;
7474
#[cfg(not(windows))]
75+
#[cfg(not(target_os = "wasi"))]
76+
#[cfg(feature = "pty")]
77+
pub(crate) mod pty;
78+
#[cfg(not(windows))]
7579
#[cfg(feature = "rand")]
7680
pub(crate) mod rand;
7781
#[cfg(not(windows))]

src/backend/libc/process/syscalls.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
//! libc syscalls supporting `rustix::process`.
22
33
use super::super::c;
4-
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
5-
use super::super::conv::borrowed_fd;
6-
use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr};
74
#[cfg(not(target_os = "wasi"))]
8-
use super::super::conv::{ret_infallible, ret_pid_t, ret_usize};
5+
use super::super::conv::{borrowed_fd, ret_infallible, ret_pid_t, ret_usize};
6+
use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr};
97
#[cfg(any(target_os = "android", target_os = "linux"))]
108
use super::super::conv::{syscall_ret, syscall_ret_u32};
119
#[cfg(any(
@@ -15,7 +13,7 @@ use super::super::conv::{syscall_ret, syscall_ret_u32};
1513
target_os = "linux",
1614
))]
1715
use super::types::RawCpuSet;
18-
#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
16+
#[cfg(not(target_os = "wasi"))]
1917
use crate::fd::BorrowedFd;
2018
#[cfg(target_os = "linux")]
2119
use crate::fd::{AsRawFd, OwnedFd};
@@ -648,3 +646,9 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
648646
))
649647
}
650648
}
649+
650+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
651+
#[inline]
652+
pub(crate) fn ioctl_tiocsctty(fd: BorrowedFd<'_>) -> io::Result<()> {
653+
unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSCTTY as _, &0_u32)) }
654+
}

src/backend/libc/pty/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod syscalls;

src/backend/libc/pty/syscalls.rs

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

src/backend/linux_raw/c.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ pub(crate) use linux_raw_sys::general::{
3232
SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD, SO_TYPE, TCP_NODELAY,
3333
};
3434
pub(crate) use linux_raw_sys::general::{NFS_SUPER_MAGIC, PROC_SUPER_MAGIC, UTIME_NOW, UTIME_OMIT};
35+
pub(crate) use linux_raw_sys::general::{O_NOCTTY, O_RDWR};
3536
pub(crate) use linux_raw_sys::general::{XATTR_CREATE, XATTR_REPLACE};

src/backend/linux_raw/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub(crate) mod net;
4141
))]
4242
pub(crate) mod param;
4343
pub(crate) mod process;
44+
#[cfg(feature = "pty")]
45+
pub(crate) mod pty;
4446
#[cfg(feature = "rand")]
4547
pub(crate) mod rand;
4648
#[cfg(feature = "runtime")]

src/backend/linux_raw/process/syscalls.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use linux_raw_sys::general::{
3131
__kernel_gid_t, __kernel_pid_t, __kernel_uid_t, membarrier_cmd, membarrier_cmd_flag, rlimit,
3232
rlimit64, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, RLIM64_INFINITY, RLIM_INFINITY,
3333
};
34+
use linux_raw_sys::ioctl::TIOCSCTTY;
3435
#[cfg(not(target_os = "wasi"))]
3536
#[cfg(feature = "fs")]
3637
use {super::super::conv::ret_c_uint_infallible, crate::fs::Mode};
@@ -745,3 +746,15 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
745746
let (ptr, len) = slice(name);
746747
unsafe { ret(syscall_readonly!(__NR_sethostname, ptr, len)) }
747748
}
749+
750+
#[inline]
751+
pub(crate) fn ioctl_tiocsctty(fd: BorrowedFd<'_>) -> io::Result<()> {
752+
unsafe {
753+
ret(syscall_readonly!(
754+
__NR_ioctl,
755+
fd,
756+
c_uint(TIOCSCTTY),
757+
by_ref(&0_u32)
758+
))
759+
}
760+
}

src/backend/linux_raw/pty/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod syscalls;

0 commit comments

Comments
 (0)