From 38737af78d02ea081411cd8b79acf0c3f662dea4 Mon Sep 17 00:00:00 2001 From: Connor-GH Date: Wed, 3 Jun 2026 19:41:38 -0500 Subject: [PATCH 1/2] pty: update libc functions for ptyd changes Of note is that `unlockpt` is required, both by the standard, and our implementation. This has been taken care of inside of `openpty`, which is the only non-libc way we are acquiring ptys. Additionally, it has been modified such that it is POSIX compliant, so any outside future changes to ptyd will not affect it. This is part of my ptyd changes (#100). This can be safely merged once the changes from base/ and userutils/ are merged alongside it. --- src/header/limits/mod.rs | 1 + src/header/pty/redox.rs | 39 ++++++++++++++++-------- src/header/stdlib/mod.rs | 50 ++++++++++--------------------- src/header/sys_ioctl/constants.rs | 2 ++ src/header/sys_ioctl/linux.rs | 1 - src/header/sys_ioctl/redox/mod.rs | 10 +++++-- 6 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/header/limits/mod.rs b/src/header/limits/mod.rs index 192a82eafb..50054fd399 100644 --- a/src/header/limits/mod.rs +++ b/src/header/limits/mod.rs @@ -130,3 +130,4 @@ pub const PTHREAD_DESTRUCTOR_ITERATIONS: c_long = _POSIX_THREAD_DESTRUCTOR_ITERA pub const PTHREAD_KEYS_MAX: c_long = 4096 * 32; pub const PTHREAD_STACK_MIN: c_long = 65536; pub const SYMLOOP_MAX: c_long = 64; +pub const TTY_NAME_MAX: c_long = 32; // "/scheme/pty/".len() + size of usize::MAX as string + 1 diff --git a/src/header/pty/redox.rs b/src/header/pty/redox.rs index a4fad5c608..8ecfc0a2cd 100644 --- a/src/header/pty/redox.rs +++ b/src/header/pty/redox.rs @@ -1,29 +1,42 @@ use crate::{ - error::ResultExt, - header::{fcntl, unistd}, - platform::{ - Pal, Sys, - types::{c_char, c_int, ssize_t}, + header::{ + fcntl, + stdlib::{grantpt, posix_openpt, ptsname_r, unlockpt}, + unistd, }, + platform::types::{c_char, c_int}, }; pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> { - let master = unsafe { fcntl::open(c"/scheme/pty".as_ptr(), fcntl::O_RDWR, 0) }; + let master = unsafe { posix_openpt(fcntl::O_RDWR | fcntl::O_NOCTTY) }; if master < 0 { return Err(()); } - // TODO: better error handling - let count = Sys::fpath(master, name) - .map(|u| u as ssize_t) - .or_minus_one_errno(); - if count < 0 { + let ret = grantpt(master); + if ret == -1 { + unistd::close(master); + return Err(()); + } + let ret = unsafe { unlockpt(master) }; + if ret == -1 { unistd::close(master); return Err(()); } - name[count as usize] = 0; - let slave = unsafe { fcntl::open(name.as_ptr() as *const c_char, fcntl::O_RDWR, 0) }; + let ret = unsafe { ptsname_r(master, name.as_mut_ptr().cast(), name.len()) }; + if ret < 0 { + unistd::close(master); + return Err(()); + } + + let slave = unsafe { + fcntl::open( + name.as_ptr() as *const c_char, + fcntl::O_RDWR | fcntl::O_NOCTTY, + 0, + ) + }; if slave < 0 { unistd::close(master); return Err(()); diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 70662f4230..14b8f46f8f 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -924,8 +924,7 @@ pub unsafe extern "C" fn posix_memalign( #[unsafe(no_mangle)] pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { #[cfg(target_os = "redox")] - let r = unsafe { open(c"/scheme/pty".as_ptr(), flags) }; - + let r = unsafe { open(c"/scheme/pty/ptmx".as_ptr(), flags) }; #[cfg(target_os = "linux")] let r = unsafe { open(c"/dev/ptmx".as_ptr(), flags) }; @@ -939,9 +938,11 @@ pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn ptsname(fd: c_int) -> *mut c_char { - const PTS_BUFFER_LEN: usize = 9 + mem::size_of::() * 3 + 1; + const PTS_BUFFER_LEN: usize = limits::TTY_NAME_MAX as usize; static mut PTS_BUFFER: [c_char; PTS_BUFFER_LEN] = [0; PTS_BUFFER_LEN]; - if unsafe { ptsname_r(fd, (&raw mut PTS_BUFFER).cast(), PTS_BUFFER_LEN) } != 0 { + let ret = unsafe { ptsname_r(fd, (&raw mut PTS_BUFFER).cast(), PTS_BUFFER_LEN) }; + if ret != 0 { + platform::ERRNO.set(ret); ptr::null_mut() } else { (&raw mut PTS_BUFFER).cast() @@ -959,49 +960,28 @@ pub unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) } } -#[cfg(target_os = "redox")] +// ptsname_r is not allowed to set errno, but it has it as a return value. #[inline(always)] unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { - let tty_ptr = unsafe { unistd::ttyname(fd) }; - - if !tty_ptr.is_null() { - if let Ok(name) = unsafe { CStr::from_ptr(tty_ptr) }.to_str() { - let len = name.len(); - if len > buflen { - platform::ERRNO.set(ERANGE); - return ERANGE; - } else { - // we have checked the string will fit in the buffer - // so can use strcpy safely - let s = name.as_ptr().cast(); - unsafe { - ptr::copy_nonoverlapping(s, buf, len); - } - return 0; - } - } - } - platform::ERRNO.get() -} - -#[cfg(target_os = "linux")] -#[inline(always)] -unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { - let mut pty = 0; - let err = platform::ERRNO.get(); + let mut pty: c_int = 0; if unsafe { ioctl(fd, TIOCGPTN, ptr::from_mut(&mut pty).cast::()) } == 0 { + // Linux and Redox use different resource names for PTS's. + #[cfg(target_os = "linux")] let name = format!("/dev/pts/{}", pty); + #[cfg(target_os = "redox")] + let name = format!("/scheme/pty/{}", pty); let len = name.len(); - if len > buflen { - platform::ERRNO.set(ERANGE); + // We need + 1 to account for the NUL terminator. + if len + 1 > buflen { ERANGE } else { // we have checked the string will fit in the buffer // so can use strcpy safely let s = name.as_ptr().cast(); unsafe { ptr::copy_nonoverlapping(s, buf, len) }; - platform::ERRNO.set(err); + // NUL-terminate the result. + unsafe { *(buf.add(len + 1)) = 0 }; 0 } } else { diff --git a/src/header/sys_ioctl/constants.rs b/src/header/sys_ioctl/constants.rs index 86596b4c0b..8849a7ce0d 100644 --- a/src/header/sys_ioctl/constants.rs +++ b/src/header/sys_ioctl/constants.rs @@ -55,5 +55,7 @@ pub const TIOCSPTLCK: c_ulong = 0x4004_5431; /// location pointed to by `lock`. pub const TIOCGPTLCK: c_ulong = 0x8004_5439; +// Get the name of the PT as a c_int. +pub const TIOCGPTN: c_ulong = 0x8004_5430; /// POSIX `sockatmark()` from `sys/socket.h` is intended to replace this. pub const SIOCATMARK: c_ulong = 0x8905; diff --git a/src/header/sys_ioctl/linux.rs b/src/header/sys_ioctl/linux.rs index 2d55989a13..4484b78818 100644 --- a/src/header/sys_ioctl/linux.rs +++ b/src/header/sys_ioctl/linux.rs @@ -28,7 +28,6 @@ pub const TIOCSBRK: c_ulong = 0x5427; pub const TIOCCBRK: c_ulong = 0x5428; pub const TIOCGRS485: c_ulong = 0x542E; pub const TIOCSRS485: c_ulong = 0x542F; -pub const TIOCGPTN: c_ulong = 0x8004_5430; pub const TIOCGDEV: c_ulong = 0x8004_5432; pub const TCGETX: c_ulong = 0x5432; pub const TCSETX: c_ulong = 0x5433; diff --git a/src/header/sys_ioctl/redox/mod.rs b/src/header/sys_ioctl/redox/mod.rs index 4003041d53..04ed92dc7a 100644 --- a/src/header/sys_ioctl/redox/mod.rs +++ b/src/header/sys_ioctl/redox/mod.rs @@ -123,10 +123,16 @@ pub unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Resu dup_write(fd, "winsize", winsize)?; } TIOCGPTLCK => { - todo_skip!(0, "ioctl TIOCGPTLCK"); + let lock = unsafe { &mut *(out as *mut c_int) }; + dup_read(fd, "ptlock", lock)?; } TIOCSPTLCK => { - todo_skip!(0, "ioctl TIOCSPTLCK"); + let lock = unsafe { &*(out as *const c_int) }; + dup_write(fd, "ptlock", lock)?; + } + TIOCGPTN => { + let name = unsafe { &mut *(out as *mut c_int) }; + dup_read(fd, "ptsname", name)?; } TCSBRK => { todo_skip!(0, "ioctl TCSBRK"); From ee6b7137e7ba436200a50f0354a7ef86b103250f Mon Sep 17 00:00:00 2001 From: Connor-GH Date: Wed, 3 Jun 2026 19:59:53 -0500 Subject: [PATCH 2/2] pty: remove some duplicated code The Linux and Redox implementations can be combined to reduce code duplication. --- src/header/pty/linux.rs | 58 ----------------------------------------- src/header/pty/mod.rs | 49 ++++++++++++++++++++++++++++------ src/header/pty/redox.rs | 46 -------------------------------- 3 files changed, 41 insertions(+), 112 deletions(-) delete mode 100644 src/header/pty/linux.rs delete mode 100644 src/header/pty/redox.rs diff --git a/src/header/pty/linux.rs b/src/header/pty/linux.rs deleted file mode 100644 index 020e336915..0000000000 --- a/src/header/pty/linux.rs +++ /dev/null @@ -1,58 +0,0 @@ -use core::ptr; - -use crate::{ - header::{fcntl, sys_ioctl, unistd}, - io::{Cursor, Write}, - platform::types::{c_char, c_int, c_void}, -}; - -pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> { - //TODO: wrap in auto-close struct - let master = unsafe { fcntl::open(c"/dev/ptmx".as_ptr(), fcntl::O_RDWR | fcntl::O_NOCTTY, 0) }; - if master < 0 { - return Err(()); - } - - let mut lock: c_int = 0; - if unsafe { - sys_ioctl::ioctl( - master, - sys_ioctl::TIOCSPTLCK, - ptr::from_mut::(&mut lock).cast::(), - ) - } != 0 - { - unistd::close(master); - return Err(()); - } - - let mut ptn: c_int = 0; - if unsafe { - sys_ioctl::ioctl( - master, - sys_ioctl::TIOCGPTN, - ptr::from_mut::(&mut ptn).cast::(), - ) - } != 0 - { - unistd::close(master); - return Err(()); - } - - let mut cursor = Cursor::new(name); - if let Ok(()) = write!(cursor, "/dev/pts/{}\0", ptn) {}; // TODO handle error - - let slave = unsafe { - fcntl::open( - cursor.get_ref().as_ptr().cast::(), - fcntl::O_RDWR | fcntl::O_NOCTTY, - 0, - ) - }; - if slave < 0 { - unistd::close(master); - return Err(()); - } - - Ok((master, slave)) -} diff --git a/src/header/pty/mod.rs b/src/header/pty/mod.rs index 3620c45581..739d423dbe 100644 --- a/src/header/pty/mod.rs +++ b/src/header/pty/mod.rs @@ -6,7 +6,9 @@ use core::{mem, ptr, slice}; use crate::{ header::{ - bits_sigset_t, fcntl, limits, pthread, signal, sys_ioctl, sys_wait, termios, unistd, utmp, + bits_sigset_t, fcntl, limits, pthread, signal, + stdlib::{grantpt, posix_openpt, ptsname_r, unlockpt}, + sys_ioctl, sys_wait, termios, unistd, utmp, }, platform::{ self, @@ -14,13 +16,44 @@ use crate::{ }, }; -#[cfg(target_os = "linux")] -#[path = "linux.rs"] -mod imp; +unsafe fn openpty_inner(name: &mut [u8]) -> Result<(c_int, c_int), ()> { + // TODO: wrap in auto-close struct + let master = unsafe { posix_openpt(fcntl::O_RDWR | fcntl::O_NOCTTY) }; + if master < 0 { + return Err(()); + } -#[cfg(target_os = "redox")] -#[path = "redox.rs"] -mod imp; + let ret = grantpt(master); + if ret == -1 { + unistd::close(master); + return Err(()); + } + let ret = unsafe { unlockpt(master) }; + if ret == -1 { + unistd::close(master); + return Err(()); + } + + let ret = unsafe { ptsname_r(master, name.as_mut_ptr().cast(), name.len()) }; + if ret < 0 { + unistd::close(master); + return Err(()); + } + + let slave = unsafe { + fcntl::open( + name.as_ptr() as *const c_char, + fcntl::O_RDWR | fcntl::O_NOCTTY, + 0, + ) + }; + if slave < 0 { + unistd::close(master); + return Err(()); + } + + Ok((master, slave)) +} /// See . #[unsafe(no_mangle)] @@ -38,7 +71,7 @@ pub unsafe extern "C" fn openpty( &mut tmp_name }; - let (master, slave) = match unsafe { imp::openpty(name) } { + let (master, slave) = match unsafe { openpty_inner(name) } { Ok(ok) => ok, Err(()) => return -1, }; diff --git a/src/header/pty/redox.rs b/src/header/pty/redox.rs deleted file mode 100644 index 8ecfc0a2cd..0000000000 --- a/src/header/pty/redox.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{ - header::{ - fcntl, - stdlib::{grantpt, posix_openpt, ptsname_r, unlockpt}, - unistd, - }, - platform::types::{c_char, c_int}, -}; - -pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> { - let master = unsafe { posix_openpt(fcntl::O_RDWR | fcntl::O_NOCTTY) }; - if master < 0 { - return Err(()); - } - - let ret = grantpt(master); - if ret == -1 { - unistd::close(master); - return Err(()); - } - let ret = unsafe { unlockpt(master) }; - if ret == -1 { - unistd::close(master); - return Err(()); - } - - let ret = unsafe { ptsname_r(master, name.as_mut_ptr().cast(), name.len()) }; - if ret < 0 { - unistd::close(master); - return Err(()); - } - - let slave = unsafe { - fcntl::open( - name.as_ptr() as *const c_char, - fcntl::O_RDWR | fcntl::O_NOCTTY, - 0, - ) - }; - if slave < 0 { - unistd::close(master); - return Err(()); - } - - Ok((master, slave)) -}