]> git.proxmox.com Git - rustc.git/blob - vendor/rustix/src/backend/libc/termios/syscalls.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / vendor / rustix / src / backend / libc / termios / syscalls.rs
1 //! libc syscalls supporting `rustix::termios`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend::syscalls` module documentation for details.
6
7 use crate::backend::c;
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")))]
12 use crate::ffi::CStr;
13 use core::mem::MaybeUninit;
14 #[cfg(not(target_os = "wasi"))]
15 use {
16 crate::io,
17 crate::pid::Pid,
18 crate::termios::{Action, OptionalActions, QueueSelector, Termios, Winsize},
19 crate::utils::as_mut_ptr,
20 };
21
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
25 // `c_ospeed` fields.
26 #[cfg(linux_kernel)]
27 unsafe {
28 use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes};
29 use core::mem::zeroed;
30
31 let mut termios2 = MaybeUninit::<c::termios2>::uninit();
32
33 ret(c::ioctl(
34 borrowed_fd(fd),
35 c::TCGETS2.into(),
36 termios2.as_mut_ptr(),
37 ))?;
38
39 let termios2 = termios2.assume_init();
40
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,
51 };
52 result.special_codes.0[..termios2.c_cc.len()].copy_from_slice(&termios2.c_cc);
53
54 Ok(result)
55 }
56
57 #[cfg(not(linux_kernel))]
58 unsafe {
59 let mut result = MaybeUninit::<Termios>::uninit();
60
61 ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?;
62
63 Ok(result.assume_init())
64 }
65 }
66
67 #[cfg(not(target_os = "wasi"))]
68 pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> {
69 unsafe {
70 let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?;
71 Ok(Pid::from_raw_unchecked(pid))
72 }
73 }
74
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())) }
78 }
79
80 #[cfg(not(target_os = "wasi"))]
81 pub(crate) fn tcsetattr(
82 fd: BorrowedFd,
83 optional_actions: OptionalActions,
84 termios: &Termios,
85 ) -> io::Result<()> {
86 // If we have `TCSETS2`, use it, so that we use the `c_ispeed` and
87 // `c_ospeed` fields.
88 #[cfg(linux_kernel)]
89 unsafe {
90 use crate::termios::speed;
91 use core::mem::zeroed;
92 use linux_raw_sys::general::{termios2, BOTHER, CBAUD, IBSHIFT};
93
94 #[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
95 use linux_raw_sys::ioctl::{TCSETS, TCSETS2};
96
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;
103
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
109 } else {
110 optional_actions as u32
111 };
112
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,
121 c_cc: zeroed(),
122 c_ispeed: input_speed,
123 c_ospeed: output_speed,
124 };
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();
132 termios2
133 .c_cc
134 .copy_from_slice(&termios.special_codes.0[..nccs]);
135
136 ret(c::ioctl(borrowed_fd(fd), request as _, &termios2))
137 }
138
139 #[cfg(not(linux_kernel))]
140 unsafe {
141 ret(c::tcsetattr(
142 borrowed_fd(fd),
143 optional_actions as _,
144 crate::utils::as_ptr(termios).cast(),
145 ))
146 }
147 }
148
149 #[cfg(not(target_os = "wasi"))]
150 pub(crate) fn tcsendbreak(fd: BorrowedFd) -> io::Result<()> {
151 unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) }
152 }
153
154 #[cfg(not(target_os = "wasi"))]
155 pub(crate) fn tcdrain(fd: BorrowedFd) -> io::Result<()> {
156 unsafe { ret(c::tcdrain(borrowed_fd(fd))) }
157 }
158
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 _)) }
162 }
163
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 _)) }
167 }
168
169 #[cfg(not(target_os = "wasi"))]
170 pub(crate) fn tcgetsid(fd: BorrowedFd) -> io::Result<Pid> {
171 unsafe {
172 let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?;
173 Ok(Pid::from_raw_unchecked(pid))
174 }
175 }
176
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)) }
180 }
181
182 #[cfg(not(target_os = "wasi"))]
183 pub(crate) fn tcgetwinsize(fd: BorrowedFd) -> io::Result<Winsize> {
184 unsafe {
185 let mut buf = MaybeUninit::<Winsize>::uninit();
186 ret(c::ioctl(
187 borrowed_fd(fd),
188 c::TIOCGWINSZ.into(),
189 buf.as_mut_ptr(),
190 ))?;
191 Ok(buf.assume_init())
192 }
193 }
194
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 _)) }
198 }
199
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 _)) }
203 }
204
205 #[cfg(not(target_os = "wasi"))]
206 #[inline]
207 pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
208 #[cfg(bsd)]
209 let encoded_speed = arbitrary_speed;
210
211 #[cfg(not(bsd))]
212 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
213 Some(encoded_speed) => encoded_speed,
214 #[cfg(linux_kernel)]
215 None => c::BOTHER,
216 #[cfg(not(linux_kernel))]
217 None => return Err(io::Errno::INVAL),
218 };
219
220 #[cfg(not(linux_kernel))]
221 unsafe {
222 ret(c::cfsetspeed(
223 as_mut_ptr(termios).cast(),
224 encoded_speed.into(),
225 ))
226 }
227
228 // Linux libc implementations don't support arbitrary speeds, so we encode
229 // the speed manually.
230 #[cfg(linux_kernel)]
231 {
232 use crate::termios::ControlModes;
233
234 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
235
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));
239
240 termios.input_speed = arbitrary_speed;
241 termios.output_speed = arbitrary_speed;
242
243 Ok(())
244 }
245 }
246
247 #[cfg(not(target_os = "wasi"))]
248 #[inline]
249 pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
250 #[cfg(bsd)]
251 let encoded_speed = arbitrary_speed;
252
253 #[cfg(not(bsd))]
254 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
255 Some(encoded_speed) => encoded_speed,
256 #[cfg(linux_kernel)]
257 None => c::BOTHER,
258 #[cfg(not(linux_kernel))]
259 None => return Err(io::Errno::INVAL),
260 };
261
262 #[cfg(not(linux_kernel))]
263 unsafe {
264 ret(c::cfsetospeed(
265 as_mut_ptr(termios).cast(),
266 encoded_speed.into(),
267 ))
268 }
269
270 // Linux libc implementations don't support arbitrary speeds or setting the
271 // input and output speeds separately, so we encode the speed manually.
272 #[cfg(linux_kernel)]
273 {
274 use crate::termios::ControlModes;
275
276 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
277
278 termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD);
279 termios.control_modes |= ControlModes::from_bits_retain(encoded_speed);
280
281 termios.output_speed = arbitrary_speed;
282
283 Ok(())
284 }
285 }
286
287 #[cfg(not(target_os = "wasi"))]
288 #[inline]
289 pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
290 #[cfg(bsd)]
291 let encoded_speed = arbitrary_speed;
292
293 #[cfg(not(bsd))]
294 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
295 Some(encoded_speed) => encoded_speed,
296 #[cfg(linux_kernel)]
297 None => c::BOTHER,
298 #[cfg(not(linux_kernel))]
299 None => return Err(io::Errno::INVAL),
300 };
301
302 #[cfg(not(linux_kernel))]
303 unsafe {
304 ret(c::cfsetispeed(
305 as_mut_ptr(termios).cast(),
306 encoded_speed.into(),
307 ))
308 }
309
310 // Linux libc implementations don't support arbitrary speeds or setting the
311 // input and output speeds separately, so we encode the speed manually.
312 #[cfg(linux_kernel)]
313 {
314 use crate::termios::ControlModes;
315
316 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
317
318 termios.control_modes -= ControlModes::from_bits_retain(c::CIBAUD);
319 termios.control_modes |= ControlModes::from_bits_retain(encoded_speed << c::IBSHIFT);
320
321 termios.input_speed = arbitrary_speed;
322
323 Ok(())
324 }
325 }
326
327 #[cfg(not(target_os = "wasi"))]
328 #[inline]
329 pub(crate) fn cfmakeraw(termios: &mut Termios) {
330 unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) }
331 }
332
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
338 // terminal” anyway.
339 unsafe { c::isatty(borrowed_fd(fd)) != 0 }
340 }
341
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> {
345 unsafe {
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)),
350 }
351 }
352 }