1 //! linux_raw syscalls supporting `rustix::termios`.
5 //! See the `rustix::backend` module documentation for details.
7 #![allow(clippy::undocumented_unsafe_blocks)]
10 use crate::backend
::conv
::{by_ref, c_uint, ret}
;
11 use crate::fd
::BorrowedFd
;
14 #[cfg(feature = "procfs")]
17 Action
, ControlModes
, InputModes
, LocalModes
, OptionalActions
, OutputModes
, QueueSelector
,
18 SpecialCodeIndex
, Termios
, Winsize
,
20 #[cfg(feature = "procfs")]
21 use crate::{ffi::CStr, fs::FileType, path::DecInt}
;
22 use core
::mem
::MaybeUninit
;
23 use linux_raw_sys
::general
::IBSHIFT
;
24 use linux_raw_sys
::ioctl
::{
25 TCFLSH
, TCSBRK
, TCXONC
, TIOCEXCL
, TIOCGPGRP
, TIOCGSID
, TIOCGWINSZ
, TIOCNXCL
, TIOCSPGRP
,
30 pub(crate) fn tcgetwinsize(fd
: BorrowedFd
<'_
>) -> io
::Result
<Winsize
> {
32 let mut result
= MaybeUninit
::<Winsize
>::uninit();
33 ret(syscall
!(__NR_ioctl
, fd
, c_uint(TIOCGWINSZ
), &mut result
))?
;
34 Ok(result
.assume_init())
39 pub(crate) fn tcgetattr(fd
: BorrowedFd
<'_
>) -> io
::Result
<Termios
> {
41 let mut result
= MaybeUninit
::<Termios
>::uninit();
42 ret(syscall
!(__NR_ioctl
, fd
, c_uint(c
::TCGETS2
), &mut result
))?
;
43 Ok(result
.assume_init())
48 pub(crate) fn tcgetpgrp(fd
: BorrowedFd
<'_
>) -> io
::Result
<Pid
> {
50 let mut result
= MaybeUninit
::<c
::pid_t
>::uninit();
51 ret(syscall
!(__NR_ioctl
, fd
, c_uint(TIOCGPGRP
), &mut result
))?
;
52 let pid
= result
.assume_init();
53 Ok(Pid
::from_raw_unchecked(pid
))
58 pub(crate) fn tcsetattr(
60 optional_actions
: OptionalActions
,
63 // Translate from `optional_actions` into an ioctl request code. On MIPS,
64 // `optional_actions` already has `TCGETS` added to it.
65 let request
= linux_raw_sys
::ioctl
::TCSETS2
66 + if cfg
!(any(target_arch
= "mips", target_arch
= "mips64")) {
67 optional_actions
as u32 - linux_raw_sys
::ioctl
::TCSETS
69 optional_actions
as u32
72 ret(syscall_readonly
!(
82 pub(crate) fn tcsendbreak(fd
: BorrowedFd
) -> io
::Result
<()> {
83 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TCSBRK), c_uint(0))) }
87 pub(crate) fn tcdrain(fd
: BorrowedFd
) -> io
::Result
<()> {
88 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TCSBRK), c_uint(1))) }
92 pub(crate) fn tcflush(fd
: BorrowedFd
, queue_selector
: QueueSelector
) -> io
::Result
<()> {
94 ret(syscall_readonly
!(
98 c_uint(queue_selector
as u32)
104 pub(crate) fn tcflow(fd
: BorrowedFd
, action
: Action
) -> io
::Result
<()> {
106 ret(syscall_readonly
!(
110 c_uint(action
as u32)
116 pub(crate) fn tcgetsid(fd
: BorrowedFd
) -> io
::Result
<Pid
> {
118 let mut result
= MaybeUninit
::<c
::pid_t
>::uninit();
119 ret(syscall
!(__NR_ioctl
, fd
, c_uint(TIOCGSID
), &mut result
))?
;
120 let pid
= result
.assume_init();
121 Ok(Pid
::from_raw_unchecked(pid
))
126 pub(crate) fn tcsetwinsize(fd
: BorrowedFd
, winsize
: Winsize
) -> io
::Result
<()> {
138 pub(crate) fn tcsetpgrp(fd
: BorrowedFd
<'_
>, pid
: Pid
) -> io
::Result
<()> {
139 unsafe { ret(syscall!(__NR_ioctl, fd, c_uint(TIOCSPGRP), pid)) }
143 pub(crate) fn ioctl_tiocexcl(fd
: BorrowedFd
<'_
>) -> io
::Result
<()> {
144 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCEXCL))) }
148 pub(crate) fn ioctl_tiocnxcl(fd
: BorrowedFd
<'_
>) -> io
::Result
<()> {
149 unsafe { ret(syscall_readonly!(__NR_ioctl, fd, c_uint(TIOCNXCL))) }
152 /// A wrapper around a conceptual `cfsetspeed` which handles an arbitrary
153 /// integer speed value.
155 pub(crate) fn set_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
156 let encoded_speed
= crate::termios
::speed
::encode(arbitrary_speed
).unwrap_or(c
::BOTHER
);
158 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
160 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CBAUD
| c
::CIBAUD
);
161 termios
.control_modes
|=
162 ControlModes
::from_bits_retain(encoded_speed
| (encoded_speed
<< IBSHIFT
));
164 termios
.input_speed
= arbitrary_speed
;
165 termios
.output_speed
= arbitrary_speed
;
170 /// A wrapper around a conceptual `cfsetospeed` which handles an arbitrary
171 /// integer speed value.
173 pub(crate) fn set_output_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
174 let encoded_speed
= crate::termios
::speed
::encode(arbitrary_speed
).unwrap_or(c
::BOTHER
);
176 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
178 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CBAUD
);
179 termios
.control_modes
|= ControlModes
::from_bits_retain(encoded_speed
);
181 termios
.output_speed
= arbitrary_speed
;
186 /// A wrapper around a conceptual `cfsetispeed` which handles an arbitrary
187 /// integer speed value.
189 pub(crate) fn set_input_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
190 let encoded_speed
= crate::termios
::speed
::encode(arbitrary_speed
).unwrap_or(c
::BOTHER
);
192 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
194 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CIBAUD
);
195 termios
.control_modes
|= ControlModes
::from_bits_retain(encoded_speed
<< IBSHIFT
);
197 termios
.input_speed
= arbitrary_speed
;
203 pub(crate) fn cfmakeraw(termios
: &mut Termios
) {
204 // From the Linux [`cfmakeraw` manual page]:
206 // [`cfmakeraw` manual page]: https://man7.org/linux/man-pages/man3/cfmakeraw.3.html
207 termios
.input_modes
-= InputModes
::IGNBRK
215 termios
.output_modes
-= OutputModes
::OPOST
;
216 termios
.local_modes
-= LocalModes
::ECHO
220 | LocalModes
::IEXTEN
;
221 termios
.control_modes
-= ControlModes
::CSIZE
| ControlModes
::PARENB
;
222 termios
.control_modes
|= ControlModes
::CS8
;
224 // Musl and glibc also do these:
225 termios
.special_codes
[SpecialCodeIndex
::VMIN
] = 1;
226 termios
.special_codes
[SpecialCodeIndex
::VTIME
] = 0;
230 pub(crate) fn isatty(fd
: BorrowedFd
<'_
>) -> bool
{
231 // On error, Linux will return either `EINVAL` (2.6.32) or `ENOTTY`
232 // (otherwise), because we assume we're never passing an invalid
233 // file descriptor (which would get `EBADF`). Either way, an error
234 // means we don't have a tty.
235 tcgetwinsize(fd
).is_ok()
238 #[cfg(feature = "procfs")]
239 #[allow(unsafe_code)]
240 pub(crate) fn ttyname(fd
: BorrowedFd
<'_
>, buf
: &mut [MaybeUninit
<u8>]) -> io
::Result
<usize> {
241 let fd_stat
= crate::backend
::fs
::syscalls
::fstat(fd
)?
;
243 // Quick check: if `fd` isn't a character device, it's not a tty.
244 if FileType
::from_raw_mode(fd_stat
.st_mode
) != FileType
::CharacterDevice
{
245 return Err(io
::Errno
::NOTTY
);
248 // Check that `fd` is really a tty.
251 // Get a fd to '/proc/self/fd'.
252 let proc_self_fd
= procfs
::proc_self_fd()?
;
254 // Gather the ttyname by reading the 'fd' file inside 'proc_self_fd'.
255 let r
= crate::backend
::fs
::syscalls
::readlinkat(
257 DecInt
::from_fd(fd
).as_c_str(),
261 // If the number of bytes is equal to the buffer length, truncation may
262 // have occurred. This check also ensures that we have enough space for
263 // adding a NUL terminator.
265 return Err(io
::Errno
::RANGE
);
268 // `readlinkat` returns the number of bytes placed in the buffer.
269 // NUL-terminate the string at that offset.
272 // Check that the path we read refers to the same file as `fd`.
274 // SAFETY: We just wrote the NUL byte above
275 let path
= unsafe { CStr::from_ptr(buf.as_ptr().cast()) }
;
277 let path_stat
= crate::backend
::fs
::syscalls
::stat(path
)?
;
278 if path_stat
.st_dev
!= fd_stat
.st_dev
|| path_stat
.st_ino
!= fd_stat
.st_ino
{
279 return Err(io
::Errno
::NODEV
);