1 //! libc syscalls supporting `rustix::termios`.
5 //! See the `rustix::backend::syscalls` module documentation for details.
8 use crate::backend
::conv
::{borrowed_fd, ret, ret_pid_t}
;
9 use crate::fd
::BorrowedFd
;
10 #[cfg(feature = "procfs")]
11 #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
13 use core
::mem
::MaybeUninit
;
14 #[cfg(not(target_os = "wasi"))]
18 crate::termios
::{Action, OptionalActions, QueueSelector, Termios, Winsize}
,
19 crate::utils
::as_mut_ptr
,
22 #[cfg(not(target_os = "wasi"))]
23 pub(crate) fn tcgetattr(fd
: BorrowedFd
<'_
>) -> io
::Result
<Termios
> {
24 // If we have `TCGETS2`, use it, so that we fill in the `c_ispeed` and
28 use crate::termios
::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes}
;
29 use core
::mem
::zeroed
;
31 let mut termios2
= MaybeUninit
::<c
::termios2
>::uninit();
36 termios2
.as_mut_ptr(),
39 let termios2
= termios2
.assume_init();
41 // Convert from the Linux `termios2` to our `Termios`.
42 let mut result
= Termios
{
43 input_modes
: InputModes
::from_bits_retain(termios2
.c_iflag
),
44 output_modes
: OutputModes
::from_bits_retain(termios2
.c_oflag
),
45 control_modes
: ControlModes
::from_bits_retain(termios2
.c_cflag
),
46 local_modes
: LocalModes
::from_bits_retain(termios2
.c_lflag
),
47 line_discipline
: termios2
.c_line
,
48 special_codes
: SpecialCodes(zeroed()),
49 input_speed
: termios2
.c_ispeed
,
50 output_speed
: termios2
.c_ospeed
,
52 result
.special_codes
.0[..termios2
.c_cc
.len()].copy_from_slice(&termios2
.c_cc
);
57 #[cfg(not(linux_kernel))]
59 let mut result
= MaybeUninit
::<Termios
>::uninit();
61 ret(c
::tcgetattr(borrowed_fd(fd
), result
.as_mut_ptr().cast()))?
;
63 Ok(result
.assume_init())
67 #[cfg(not(target_os = "wasi"))]
68 pub(crate) fn tcgetpgrp(fd
: BorrowedFd
<'_
>) -> io
::Result
<Pid
> {
70 let pid
= ret_pid_t(c
::tcgetpgrp(borrowed_fd(fd
)))?
;
71 Ok(Pid
::from_raw_unchecked(pid
))
75 #[cfg(not(target_os = "wasi"))]
76 pub(crate) fn tcsetpgrp(fd
: BorrowedFd
<'_
>, pid
: Pid
) -> io
::Result
<()> {
77 unsafe { ret(c::tcsetpgrp(borrowed_fd(fd), pid.as_raw_nonzero().get())) }
80 #[cfg(not(target_os = "wasi"))]
81 pub(crate) fn tcsetattr(
83 optional_actions
: OptionalActions
,
86 // If we have `TCSETS2`, use it, so that we use the `c_ispeed` and
90 use crate::termios
::speed
;
91 use core
::mem
::zeroed
;
92 use linux_raw_sys
::general
::{termios2, BOTHER, CBAUD, IBSHIFT}
;
94 #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
95 use linux_raw_sys
::ioctl
::{TCSETS, TCSETS2}
;
97 // linux-raw-sys' ioctl-generation script for sparc isn't working yet,
98 // so as a temporary workaround, declare these manually.
99 #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
100 const TCSETS
: u32 = 0x80245409;
101 #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
102 const TCSETS2
: u32 = 0x802c540d;
104 // Translate from `optional_actions` into an ioctl request code. On MIPS,
105 // `optional_actions` already has `TCGETS` added to it.
106 let request
= TCSETS2
107 + if cfg
!(any(target_arch
= "mips", target_arch
= "mips64")) {
108 optional_actions
as u32 - TCSETS
110 optional_actions
as u32
113 let input_speed
= termios
.input_speed();
114 let output_speed
= termios
.output_speed();
115 let mut termios2
= termios2
{
116 c_iflag
: termios
.input_modes
.bits(),
117 c_oflag
: termios
.output_modes
.bits(),
118 c_cflag
: termios
.control_modes
.bits(),
119 c_lflag
: termios
.local_modes
.bits(),
120 c_line
: termios
.line_discipline
,
122 c_ispeed
: input_speed
,
123 c_ospeed
: output_speed
,
125 // Ensure that our input and output speeds are set, as `libc`
126 // routines don't always support setting these separately.
127 termios2
.c_cflag
&= !CBAUD
;
128 termios2
.c_cflag
|= speed
::encode(output_speed
).unwrap_or(BOTHER
);
129 termios2
.c_cflag
&= !(CBAUD
<< IBSHIFT
);
130 termios2
.c_cflag
|= speed
::encode(input_speed
).unwrap_or(BOTHER
) << IBSHIFT
;
131 let nccs
= termios2
.c_cc
.len();
134 .copy_from_slice(&termios
.special_codes
.0[..nccs
]);
136 ret(c
::ioctl(borrowed_fd(fd
), request
as _
, &termios2
))
139 #[cfg(not(linux_kernel))]
143 optional_actions
as _
,
144 crate::utils
::as_ptr(termios
).cast(),
149 #[cfg(not(target_os = "wasi"))]
150 pub(crate) fn tcsendbreak(fd
: BorrowedFd
) -> io
::Result
<()> {
151 unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) }
154 #[cfg(not(target_os = "wasi"))]
155 pub(crate) fn tcdrain(fd
: BorrowedFd
) -> io
::Result
<()> {
156 unsafe { ret(c::tcdrain(borrowed_fd(fd))) }
159 #[cfg(not(target_os = "wasi"))]
160 pub(crate) fn tcflush(fd
: BorrowedFd
, queue_selector
: QueueSelector
) -> io
::Result
<()> {
161 unsafe { ret(c::tcflush(borrowed_fd(fd), queue_selector as _)) }
164 #[cfg(not(target_os = "wasi"))]
165 pub(crate) fn tcflow(fd
: BorrowedFd
, action
: Action
) -> io
::Result
<()> {
166 unsafe { ret(c::tcflow(borrowed_fd(fd), action as _)) }
169 #[cfg(not(target_os = "wasi"))]
170 pub(crate) fn tcgetsid(fd
: BorrowedFd
) -> io
::Result
<Pid
> {
172 let pid
= ret_pid_t(c
::tcgetsid(borrowed_fd(fd
)))?
;
173 Ok(Pid
::from_raw_unchecked(pid
))
177 #[cfg(not(target_os = "wasi"))]
178 pub(crate) fn tcsetwinsize(fd
: BorrowedFd
, winsize
: Winsize
) -> io
::Result
<()> {
179 unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSWINSZ, &winsize)) }
182 #[cfg(not(target_os = "wasi"))]
183 pub(crate) fn tcgetwinsize(fd
: BorrowedFd
) -> io
::Result
<Winsize
> {
185 let mut buf
= MaybeUninit
::<Winsize
>::uninit();
188 c
::TIOCGWINSZ
.into(),
191 Ok(buf
.assume_init())
195 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
196 pub(crate) fn ioctl_tiocexcl(fd
: BorrowedFd
) -> io
::Result
<()> {
197 unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCEXCL as _)) }
200 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
201 pub(crate) fn ioctl_tiocnxcl(fd
: BorrowedFd
) -> io
::Result
<()> {
202 unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCNXCL as _)) }
205 #[cfg(not(target_os = "wasi"))]
207 pub(crate) fn set_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
209 let encoded_speed
= arbitrary_speed
;
212 let encoded_speed
= match crate::termios
::speed
::encode(arbitrary_speed
) {
213 Some(encoded_speed
) => encoded_speed
,
216 #[cfg(not(linux_kernel))]
217 None
=> return Err(io
::Errno
::INVAL
),
220 #[cfg(not(linux_kernel))]
223 as_mut_ptr(termios
).cast(),
224 encoded_speed
.into(),
228 // Linux libc implementations don't support arbitrary speeds, so we encode
229 // the speed manually.
232 use crate::termios
::ControlModes
;
234 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
236 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CBAUD
| c
::CIBAUD
);
237 termios
.control_modes
|=
238 ControlModes
::from_bits_retain(encoded_speed
| (encoded_speed
<< c
::IBSHIFT
));
240 termios
.input_speed
= arbitrary_speed
;
241 termios
.output_speed
= arbitrary_speed
;
247 #[cfg(not(target_os = "wasi"))]
249 pub(crate) fn set_output_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
251 let encoded_speed
= arbitrary_speed
;
254 let encoded_speed
= match crate::termios
::speed
::encode(arbitrary_speed
) {
255 Some(encoded_speed
) => encoded_speed
,
258 #[cfg(not(linux_kernel))]
259 None
=> return Err(io
::Errno
::INVAL
),
262 #[cfg(not(linux_kernel))]
265 as_mut_ptr(termios
).cast(),
266 encoded_speed
.into(),
270 // Linux libc implementations don't support arbitrary speeds or setting the
271 // input and output speeds separately, so we encode the speed manually.
274 use crate::termios
::ControlModes
;
276 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
278 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CBAUD
);
279 termios
.control_modes
|= ControlModes
::from_bits_retain(encoded_speed
);
281 termios
.output_speed
= arbitrary_speed
;
287 #[cfg(not(target_os = "wasi"))]
289 pub(crate) fn set_input_speed(termios
: &mut Termios
, arbitrary_speed
: u32) -> io
::Result
<()> {
291 let encoded_speed
= arbitrary_speed
;
294 let encoded_speed
= match crate::termios
::speed
::encode(arbitrary_speed
) {
295 Some(encoded_speed
) => encoded_speed
,
298 #[cfg(not(linux_kernel))]
299 None
=> return Err(io
::Errno
::INVAL
),
302 #[cfg(not(linux_kernel))]
305 as_mut_ptr(termios
).cast(),
306 encoded_speed
.into(),
310 // Linux libc implementations don't support arbitrary speeds or setting the
311 // input and output speeds separately, so we encode the speed manually.
314 use crate::termios
::ControlModes
;
316 debug_assert_eq
!(encoded_speed
& !c
::CBAUD
, 0);
318 termios
.control_modes
-= ControlModes
::from_bits_retain(c
::CIBAUD
);
319 termios
.control_modes
|= ControlModes
::from_bits_retain(encoded_speed
<< c
::IBSHIFT
);
321 termios
.input_speed
= arbitrary_speed
;
327 #[cfg(not(target_os = "wasi"))]
329 pub(crate) fn cfmakeraw(termios
: &mut Termios
) {
330 unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) }
333 pub(crate) fn isatty(fd
: BorrowedFd
<'_
>) -> bool
{
334 // Use the return value of `isatty` alone. We don't check `errno` because
335 // we return `bool` rather than `io::Result<bool>`, because we assume
336 // `BorrrowedFd` protects us from `EBADF`, and any other reasonably
337 // anticipated `errno` value would end up interpreted as “assume it's not a
339 unsafe { c::isatty(borrowed_fd(fd)) != 0 }
342 #[cfg(feature = "procfs")]
343 #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
344 pub(crate) fn ttyname(dirfd
: BorrowedFd
<'_
>, buf
: &mut [MaybeUninit
<u8>]) -> io
::Result
<usize> {
346 // `ttyname_r` returns its error status rather than using `errno`.
347 match c
::ttyname_r(borrowed_fd(dirfd
), buf
.as_mut_ptr().cast(), buf
.len()) {
348 0 => Ok(CStr
::from_ptr(buf
.as_ptr().cast()).to_bytes().len()),
349 err
=> Err(io
::Errno
::from_raw_os_error(err
)),