diff options
Diffstat (limited to 'vendor/rustix/src/backend/libc/termios/syscalls.rs')
-rw-r--r-- | vendor/rustix/src/backend/libc/termios/syscalls.rs | 297 |
1 files changed, 219 insertions, 78 deletions
diff --git a/vendor/rustix/src/backend/libc/termios/syscalls.rs b/vendor/rustix/src/backend/libc/termios/syscalls.rs index dba73c960..d4182f4fe 100644 --- a/vendor/rustix/src/backend/libc/termios/syscalls.rs +++ b/vendor/rustix/src/backend/libc/termios/syscalls.rs @@ -4,46 +4,62 @@ //! //! See the `rustix::backend::syscalls` module documentation for details. -use super::super::c; -use super::super::conv::{borrowed_fd, ret, ret_pid_t}; +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret, ret_pid_t}; use crate::fd::BorrowedFd; #[cfg(feature = "procfs")] #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] use crate::ffi::CStr; -#[cfg(not(target_os = "wasi"))] -use crate::io; -#[cfg(not(target_os = "wasi"))] -use crate::process::{Pid, RawNonZeroPid}; -#[cfg(not(target_os = "wasi"))] -use crate::termios::{Action, OptionalActions, QueueSelector, Speed, Termios, Winsize}; use core::mem::MaybeUninit; +#[cfg(not(target_os = "wasi"))] +use { + crate::io, + crate::pid::Pid, + crate::termios::{Action, OptionalActions, QueueSelector, Termios, Winsize}, + crate::utils::as_mut_ptr, +}; #[cfg(not(target_os = "wasi"))] pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result<Termios> { - let mut result = MaybeUninit::<Termios>::uninit(); + // If we have `TCGETS2`, use it, so that we fill in the `c_ispeed` and + // `c_ospeed` fields. + #[cfg(linux_kernel)] unsafe { - ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr()))?; - Ok(result.assume_init()) + use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes}; + use core::mem::zeroed; + + let mut termios2 = MaybeUninit::<c::termios2>::uninit(); + + ret(c::ioctl( + borrowed_fd(fd), + c::TCGETS2.into(), + termios2.as_mut_ptr(), + ))?; + + let termios2 = termios2.assume_init(); + + // Convert from the Linux `termios2` to our `Termios`. + let mut result = Termios { + input_modes: InputModes::from_bits_retain(termios2.c_iflag), + output_modes: OutputModes::from_bits_retain(termios2.c_oflag), + control_modes: ControlModes::from_bits_retain(termios2.c_cflag), + local_modes: LocalModes::from_bits_retain(termios2.c_lflag), + line_discipline: termios2.c_line, + special_codes: SpecialCodes(zeroed()), + input_speed: termios2.c_ispeed, + output_speed: termios2.c_ospeed, + }; + result.special_codes.0[..termios2.c_cc.len()].copy_from_slice(&termios2.c_cc); + + Ok(result) } -} -#[cfg(all( - any(target_os = "android", target_os = "linux"), - any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "x32", - target_arch = "riscv64", - target_arch = "aarch64", - target_arch = "arm", - target_arch = "mips", - target_arch = "mips64", - ) -))] -pub(crate) fn tcgetattr2(fd: BorrowedFd<'_>) -> io::Result<crate::termios::Termios2> { - let mut result = MaybeUninit::<crate::termios::Termios2>::uninit(); + #[cfg(not(linux_kernel))] unsafe { - ret(c::ioctl(borrowed_fd(fd), c::TCGETS2, result.as_mut_ptr()))?; + let mut result = MaybeUninit::<Termios>::uninit(); + + ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?; + Ok(result.assume_init()) } } @@ -52,8 +68,7 @@ pub(crate) fn tcgetattr2(fd: BorrowedFd<'_>) -> io::Result<crate::termios::Termi pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> { unsafe { let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?; - debug_assert_ne!(pid, 0); - Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid))) + Ok(Pid::from_raw_unchecked(pid)) } } @@ -68,38 +83,65 @@ pub(crate) fn tcsetattr( optional_actions: OptionalActions, termios: &Termios, ) -> io::Result<()> { + // If we have `TCSETS2`, use it, so that we use the `c_ispeed` and + // `c_ospeed` fields. + #[cfg(linux_kernel)] unsafe { - ret(c::tcsetattr( - borrowed_fd(fd), - optional_actions as _, - termios, - )) + use crate::termios::speed; + use core::mem::zeroed; + use linux_raw_sys::general::{termios2, BOTHER, CBAUD, IBSHIFT}; + + #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))] + use linux_raw_sys::ioctl::{TCSETS, TCSETS2}; + + // linux-raw-sys' ioctl-generation script for sparc isn't working yet, + // so as a temporary workaround, declare these manually. + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + const TCSETS: u32 = 0x80245409; + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + const TCSETS2: u32 = 0x802c540d; + + // Translate from `optional_actions` into an ioctl request code. On MIPS, + // `optional_actions` already has `TCGETS` added to it. + let request = TCSETS2 + + if cfg!(any(target_arch = "mips", target_arch = "mips64")) { + optional_actions as u32 - TCSETS + } else { + optional_actions as u32 + }; + + let input_speed = termios.input_speed(); + let output_speed = termios.output_speed(); + let mut termios2 = termios2 { + c_iflag: termios.input_modes.bits(), + c_oflag: termios.output_modes.bits(), + c_cflag: termios.control_modes.bits(), + c_lflag: termios.local_modes.bits(), + c_line: termios.line_discipline, + c_cc: zeroed(), + c_ispeed: input_speed, + c_ospeed: output_speed, + }; + // Ensure that our input and output speeds are set, as `libc` + // routines don't always support setting these separately. + termios2.c_cflag &= !CBAUD; + termios2.c_cflag |= speed::encode(output_speed).unwrap_or(BOTHER); + termios2.c_cflag &= !(CBAUD << IBSHIFT); + termios2.c_cflag |= speed::encode(input_speed).unwrap_or(BOTHER) << IBSHIFT; + let nccs = termios2.c_cc.len(); + termios2 + .c_cc + .copy_from_slice(&termios.special_codes.0[..nccs]); + + ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) } -} -#[cfg(all( - any(target_os = "android", target_os = "linux"), - any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "x32", - target_arch = "riscv64", - target_arch = "aarch64", - target_arch = "arm", - target_arch = "mips", - target_arch = "mips64", - ) -))] -pub(crate) fn tcsetattr2( - fd: BorrowedFd, - optional_actions: OptionalActions, - termios: &crate::termios::Termios2, -) -> io::Result<()> { + #[cfg(not(linux_kernel))] unsafe { - ret(c::ioctl( + ret(c::tcsetattr( borrowed_fd(fd), - (c::TCSETS2 as u32 + optional_actions as u32) as _, - termios, + optional_actions as _, + crate::utils::as_ptr(termios).cast(), )) } } @@ -128,8 +170,7 @@ pub(crate) fn tcflow(fd: BorrowedFd, action: Action) -> io::Result<()> { pub(crate) fn tcgetsid(fd: BorrowedFd) -> io::Result<Pid> { unsafe { let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?; - debug_assert_ne!(pid, 0); - Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid))) + Ok(Pid::from_raw_unchecked(pid)) } } @@ -151,42 +192,142 @@ pub(crate) fn tcgetwinsize(fd: BorrowedFd) -> io::Result<Winsize> { } } -#[cfg(not(target_os = "wasi"))] -#[inline] -#[must_use] -pub(crate) fn cfgetospeed(termios: &Termios) -> Speed { - unsafe { c::cfgetospeed(termios) } +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) fn ioctl_tiocexcl(fd: BorrowedFd) -> io::Result<()> { + unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCEXCL as _)) } } -#[cfg(not(target_os = "wasi"))] -#[inline] -#[must_use] -pub(crate) fn cfgetispeed(termios: &Termios) -> Speed { - unsafe { c::cfgetispeed(termios) } +#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))] +pub(crate) fn ioctl_tiocnxcl(fd: BorrowedFd) -> io::Result<()> { + unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCNXCL as _)) } } #[cfg(not(target_os = "wasi"))] #[inline] -pub(crate) fn cfmakeraw(termios: &mut Termios) { - unsafe { c::cfmakeraw(termios) } +pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetspeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds, so we encode + // the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD | c::CIBAUD); + termios.control_modes |= + ControlModes::from_bits_retain(encoded_speed | (encoded_speed << c::IBSHIFT)); + + termios.input_speed = arbitrary_speed; + termios.output_speed = arbitrary_speed; + + Ok(()) + } } #[cfg(not(target_os = "wasi"))] #[inline] -pub(crate) fn cfsetospeed(termios: &mut Termios, speed: Speed) -> io::Result<()> { - unsafe { ret(c::cfsetospeed(termios, speed)) } +pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetospeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds or setting the + // input and output speeds separately, so we encode the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD); + termios.control_modes |= ControlModes::from_bits_retain(encoded_speed); + + termios.output_speed = arbitrary_speed; + + Ok(()) + } } #[cfg(not(target_os = "wasi"))] #[inline] -pub(crate) fn cfsetispeed(termios: &mut Termios, speed: Speed) -> io::Result<()> { - unsafe { ret(c::cfsetispeed(termios, speed)) } +pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> { + #[cfg(bsd)] + let encoded_speed = arbitrary_speed; + + #[cfg(not(bsd))] + let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) { + Some(encoded_speed) => encoded_speed, + #[cfg(linux_kernel)] + None => c::BOTHER, + #[cfg(not(linux_kernel))] + None => return Err(io::Errno::INVAL), + }; + + #[cfg(not(linux_kernel))] + unsafe { + ret(c::cfsetispeed( + as_mut_ptr(termios).cast(), + encoded_speed.into(), + )) + } + + // Linux libc implementations don't support arbitrary speeds or setting the + // input and output speeds separately, so we encode the speed manually. + #[cfg(linux_kernel)] + { + use crate::termios::ControlModes; + + debug_assert_eq!(encoded_speed & !c::CBAUD, 0); + + termios.control_modes -= ControlModes::from_bits_retain(c::CIBAUD); + termios.control_modes |= ControlModes::from_bits_retain(encoded_speed << c::IBSHIFT); + + termios.input_speed = arbitrary_speed; + + Ok(()) + } } #[cfg(not(target_os = "wasi"))] #[inline] -pub(crate) fn cfsetspeed(termios: &mut Termios, speed: Speed) -> io::Result<()> { - unsafe { ret(c::cfsetspeed(termios, speed)) } +pub(crate) fn cfmakeraw(termios: &mut Termios) { + unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) } } pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool { @@ -200,7 +341,7 @@ pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool { #[cfg(feature = "procfs")] #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] -pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> { +pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> { unsafe { // `ttyname_r` returns its error status rather than using `errno`. match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) { |