rustix/backend/libc/termios/
syscalls.rs1use crate::backend::c;
8#[cfg(not(target_os = "wasi"))]
9use crate::backend::conv::ret_pid_t;
10use crate::backend::conv::{borrowed_fd, ret};
11use crate::fd::BorrowedFd;
12#[cfg(all(feature = "alloc", feature = "procfs"))]
13#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
14use crate::ffi::CStr;
15#[cfg(any(
16 not(target_os = "espidf"),
17 all(
18 feature = "procfs",
19 not(any(target_os = "fuchsia", target_os = "wasi"))
20 )
21))]
22use core::mem::MaybeUninit;
23#[cfg(not(target_os = "wasi"))]
24use {crate::io, crate::pid::Pid};
25#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
26use {
27 crate::termios::{Action, OptionalActions, QueueSelector, Termios, Winsize},
28 crate::utils::as_mut_ptr,
29};
30
31#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
32pub(crate) fn tcgetattr(fd: BorrowedFd<'_>) -> io::Result<Termios> {
33 #[cfg(linux_kernel)]
35 {
36 use crate::termios::{ControlModes, InputModes, LocalModes, OutputModes, SpecialCodes};
37
38 let mut termios2 = MaybeUninit::<c::termios2>::uninit();
39 let ptr = termios2.as_mut_ptr();
40
41 let termios2 = unsafe {
44 match ret(c::ioctl(borrowed_fd(fd), c::TCGETS2 as _, ptr)) {
45 Ok(()) => {}
46
47 #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
51 Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
52 tcgetattr_fallback(fd, &mut termios2)?
53 }
54
55 Err(err) => return Err(err),
56 }
57
58 termios2.assume_init()
60 };
61
62 let mut result = Termios {
64 input_modes: InputModes::from_bits_retain(termios2.c_iflag),
65 output_modes: OutputModes::from_bits_retain(termios2.c_oflag),
66 control_modes: ControlModes::from_bits_retain(termios2.c_cflag),
67 local_modes: LocalModes::from_bits_retain(termios2.c_lflag),
68 line_discipline: termios2.c_line,
69 special_codes: SpecialCodes(Default::default()),
70
71 #[cfg(not(all(
74 target_env = "musl",
75 any(target_arch = "powerpc", target_arch = "powerpc64")
76 )))]
77 input_speed: termios2.c_ispeed,
78 #[cfg(not(all(
79 target_env = "musl",
80 any(target_arch = "powerpc", target_arch = "powerpc64")
81 )))]
82 output_speed: termios2.c_ospeed,
83 #[cfg(all(
84 target_env = "musl",
85 any(target_arch = "powerpc", target_arch = "powerpc64")
86 ))]
87 input_speed: termios2.__c_ispeed,
88 #[cfg(all(
89 target_env = "musl",
90 any(target_arch = "powerpc", target_arch = "powerpc64")
91 ))]
92 output_speed: termios2.__c_ospeed,
93 };
94
95 let nccs = termios2.c_cc.len();
98 result.special_codes.0[..nccs].copy_from_slice(&termios2.c_cc);
99
100 Ok(result)
101 }
102
103 #[cfg(not(linux_kernel))]
104 unsafe {
105 let mut result = MaybeUninit::<Termios>::uninit();
106
107 ret(c::tcgetattr(borrowed_fd(fd), result.as_mut_ptr().cast()))?;
110
111 Ok(result.assume_init())
112 }
113}
114
115#[cfg(all(
117 linux_kernel,
118 not(any(target_arch = "powerpc", target_arch = "powerpc64"))
119))]
120#[cold]
121fn tcgetattr_fallback(
122 fd: BorrowedFd<'_>,
123 termios: &mut MaybeUninit<c::termios2>,
124) -> io::Result<()> {
125 use crate::termios::speed;
126 use core::ptr::{addr_of, addr_of_mut};
127
128 unsafe {
133 let ptr = termios.as_mut_ptr();
134
135 ret(c::ioctl(borrowed_fd(fd), c::TCGETS as _, ptr))?;
138
139 let control_modes = addr_of!((*ptr).c_cflag).read();
142
143 let encoded_out = control_modes & c::CBAUD;
145 let output_speed = match speed::decode(encoded_out) {
146 Some(output_speed) => output_speed,
147 None => return Err(io::Errno::RANGE),
148 };
149 addr_of_mut!((*ptr).c_ospeed).write(output_speed);
150
151 let encoded_in = (control_modes & c::CIBAUD) >> c::IBSHIFT;
154 let input_speed = if encoded_in == c::B0 {
155 output_speed
156 } else {
157 match speed::decode(encoded_in) {
158 Some(input_speed) => input_speed,
159 None => return Err(io::Errno::RANGE),
160 }
161 };
162 addr_of_mut!((*ptr).c_ispeed).write(input_speed);
163 }
164
165 Ok(())
166}
167
168#[cfg(not(target_os = "wasi"))]
169pub(crate) fn tcgetpgrp(fd: BorrowedFd<'_>) -> io::Result<Pid> {
170 unsafe {
171 let pid = ret_pid_t(c::tcgetpgrp(borrowed_fd(fd)))?;
172
173 #[cfg(linux_kernel)]
177 if pid == 0 {
178 return Err(io::Errno::OPNOTSUPP);
179 }
180
181 Ok(Pid::from_raw_unchecked(pid))
182 }
183}
184
185#[cfg(not(target_os = "wasi"))]
186pub(crate) fn tcsetpgrp(fd: BorrowedFd<'_>, pid: Pid) -> io::Result<()> {
187 unsafe { ret(c::tcsetpgrp(borrowed_fd(fd), pid.as_raw_nonzero().get())) }
188}
189
190#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
191pub(crate) fn tcsetattr(
192 fd: BorrowedFd<'_>,
193 optional_actions: OptionalActions,
194 termios: &Termios,
195) -> io::Result<()> {
196 #[cfg(linux_kernel)]
198 {
199 use crate::termios::speed;
200
201 let output_speed = termios.output_speed();
202 let input_speed = termios.input_speed();
203
204 let mut termios2 = c::termios2 {
205 c_iflag: termios.input_modes.bits(),
206 c_oflag: termios.output_modes.bits(),
207 c_cflag: termios.control_modes.bits(),
208 c_lflag: termios.local_modes.bits(),
209 c_line: termios.line_discipline,
210 c_cc: Default::default(),
211
212 #[cfg(not(all(
215 target_env = "musl",
216 any(target_arch = "powerpc", target_arch = "powerpc64")
217 )))]
218 c_ispeed: input_speed,
219 #[cfg(not(all(
220 target_env = "musl",
221 any(target_arch = "powerpc", target_arch = "powerpc64")
222 )))]
223 c_ospeed: output_speed,
224 #[cfg(all(
225 target_env = "musl",
226 any(target_arch = "powerpc", target_arch = "powerpc64")
227 ))]
228 __c_ispeed: input_speed,
229 #[cfg(all(
230 target_env = "musl",
231 any(target_arch = "powerpc", target_arch = "powerpc64")
232 ))]
233 __c_ospeed: output_speed,
234 };
235
236 termios2.c_cflag &= !c::CBAUD;
239 termios2.c_cflag |= speed::encode(output_speed).unwrap_or(c::BOTHER);
240 termios2.c_cflag &= !c::CIBAUD;
241 termios2.c_cflag |= speed::encode(input_speed).unwrap_or(c::BOTHER) << c::IBSHIFT;
242
243 let nccs = termios2.c_cc.len();
246 termios2
247 .c_cc
248 .copy_from_slice(&termios.special_codes.0[..nccs]);
249
250 let request = c::TCSETS2 as c::c_ulong
253 + if cfg!(any(
254 target_arch = "mips",
255 target_arch = "mips32r6",
256 target_arch = "mips64",
257 target_arch = "mips64r6"
258 )) {
259 optional_actions as c::c_ulong - c::TCSETS as c::c_ulong
260 } else {
261 optional_actions as c::c_ulong
262 };
263
264 unsafe {
266 match ret(c::ioctl(borrowed_fd(fd), request as _, &termios2)) {
267 Ok(()) => Ok(()),
268
269 #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
273 Err(io::Errno::NOTTY) | Err(io::Errno::ACCESS) => {
274 tcsetattr_fallback(fd, optional_actions, &termios2)
275 }
276
277 Err(err) => Err(err),
278 }
279 }
280 }
281
282 #[cfg(not(linux_kernel))]
283 unsafe {
284 ret(c::tcsetattr(
285 borrowed_fd(fd),
286 optional_actions as _,
287 crate::utils::as_ptr(termios).cast(),
288 ))
289 }
290}
291
292#[cfg(all(
294 linux_kernel,
295 not(any(target_arch = "powerpc", target_arch = "powerpc64"))
296))]
297#[cold]
298fn tcsetattr_fallback(
299 fd: BorrowedFd<'_>,
300 optional_actions: OptionalActions,
301 termios2: &c::termios2,
302) -> io::Result<()> {
303 let encoded_out = termios2.c_cflag & c::CBAUD;
306 let encoded_in = (termios2.c_cflag & c::CIBAUD) >> c::IBSHIFT;
307 if encoded_out == c::BOTHER || encoded_in == c::BOTHER {
308 return Err(io::Errno::RANGE);
309 }
310
311 let request = if cfg!(any(
314 target_arch = "mips",
315 target_arch = "mips32r6",
316 target_arch = "mips64",
317 target_arch = "mips64r6"
318 )) {
319 optional_actions as c::c_ulong
320 } else {
321 optional_actions as c::c_ulong + c::TCSETS as c::c_ulong
322 };
323
324 unsafe { ret(c::ioctl(borrowed_fd(fd), request as _, termios2)) }
326}
327
328#[cfg(not(target_os = "wasi"))]
329pub(crate) fn tcsendbreak(fd: BorrowedFd<'_>) -> io::Result<()> {
330 unsafe { ret(c::tcsendbreak(borrowed_fd(fd), 0)) }
331}
332
333#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
334pub(crate) fn tcdrain(fd: BorrowedFd<'_>) -> io::Result<()> {
335 unsafe { ret(c::tcdrain(borrowed_fd(fd))) }
336}
337
338#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
339pub(crate) fn tcflush(fd: BorrowedFd<'_>, queue_selector: QueueSelector) -> io::Result<()> {
340 unsafe { ret(c::tcflush(borrowed_fd(fd), queue_selector as _)) }
341}
342
343#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
344pub(crate) fn tcflow(fd: BorrowedFd<'_>, action: Action) -> io::Result<()> {
345 unsafe { ret(c::tcflow(borrowed_fd(fd), action as _)) }
346}
347
348#[cfg(not(target_os = "wasi"))]
349pub(crate) fn tcgetsid(fd: BorrowedFd<'_>) -> io::Result<Pid> {
350 unsafe {
351 let pid = ret_pid_t(c::tcgetsid(borrowed_fd(fd)))?;
352 Ok(Pid::from_raw_unchecked(pid))
353 }
354}
355
356#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
357pub(crate) fn tcsetwinsize(fd: BorrowedFd<'_>, winsize: Winsize) -> io::Result<()> {
358 unsafe { ret(c::ioctl(borrowed_fd(fd), c::TIOCSWINSZ, &winsize)) }
359}
360
361#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
362pub(crate) fn tcgetwinsize(fd: BorrowedFd<'_>) -> io::Result<Winsize> {
363 unsafe {
364 let mut buf = MaybeUninit::<Winsize>::uninit();
365 ret(c::ioctl(
366 borrowed_fd(fd),
367 c::TIOCGWINSZ.into(),
368 buf.as_mut_ptr(),
369 ))?;
370 Ok(buf.assume_init())
371 }
372}
373
374#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))]
375#[inline]
376pub(crate) fn set_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
377 #[cfg(bsd)]
378 let encoded_speed = arbitrary_speed;
379
380 #[cfg(not(bsd))]
381 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
382 Some(encoded_speed) => encoded_speed,
383 #[cfg(linux_kernel)]
384 None => c::BOTHER,
385 #[cfg(not(linux_kernel))]
386 None => return Err(io::Errno::INVAL),
387 };
388
389 #[cfg(not(linux_kernel))]
390 unsafe {
391 ret(c::cfsetspeed(
392 as_mut_ptr(termios).cast(),
393 encoded_speed.into(),
394 ))
395 }
396
397 #[cfg(linux_kernel)]
400 {
401 use crate::termios::ControlModes;
402
403 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
404
405 termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD | c::CIBAUD);
406 termios.control_modes |=
407 ControlModes::from_bits_retain(encoded_speed | (encoded_speed << c::IBSHIFT));
408
409 termios.input_speed = arbitrary_speed;
410 termios.output_speed = arbitrary_speed;
411
412 Ok(())
413 }
414}
415
416#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
417#[inline]
418pub(crate) fn set_output_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
419 #[cfg(bsd)]
420 let encoded_speed = arbitrary_speed;
421
422 #[cfg(not(bsd))]
423 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
424 Some(encoded_speed) => encoded_speed,
425 #[cfg(linux_kernel)]
426 None => c::BOTHER,
427 #[cfg(not(linux_kernel))]
428 None => return Err(io::Errno::INVAL),
429 };
430
431 #[cfg(not(linux_kernel))]
432 unsafe {
433 ret(c::cfsetospeed(
434 as_mut_ptr(termios).cast(),
435 encoded_speed.into(),
436 ))
437 }
438
439 #[cfg(linux_kernel)]
442 {
443 use crate::termios::ControlModes;
444
445 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
446
447 termios.control_modes -= ControlModes::from_bits_retain(c::CBAUD);
448 termios.control_modes |= ControlModes::from_bits_retain(encoded_speed);
449
450 termios.output_speed = arbitrary_speed;
451
452 Ok(())
453 }
454}
455
456#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
457#[inline]
458pub(crate) fn set_input_speed(termios: &mut Termios, arbitrary_speed: u32) -> io::Result<()> {
459 #[cfg(bsd)]
460 let encoded_speed = arbitrary_speed;
461
462 #[cfg(not(bsd))]
463 let encoded_speed = match crate::termios::speed::encode(arbitrary_speed) {
464 Some(encoded_speed) => encoded_speed,
465 #[cfg(linux_kernel)]
466 None => c::BOTHER,
467 #[cfg(not(linux_kernel))]
468 None => return Err(io::Errno::INVAL),
469 };
470
471 #[cfg(not(linux_kernel))]
472 unsafe {
473 ret(c::cfsetispeed(
474 as_mut_ptr(termios).cast(),
475 encoded_speed.into(),
476 ))
477 }
478
479 #[cfg(linux_kernel)]
482 {
483 use crate::termios::ControlModes;
484
485 debug_assert_eq!(encoded_speed & !c::CBAUD, 0);
486
487 termios.control_modes -= ControlModes::from_bits_retain(c::CIBAUD);
488 termios.control_modes |= ControlModes::from_bits_retain(encoded_speed << c::IBSHIFT);
489
490 termios.input_speed = arbitrary_speed;
491
492 Ok(())
493 }
494}
495
496#[cfg(not(any(target_os = "espidf", target_os = "nto", target_os = "wasi")))]
497#[inline]
498pub(crate) fn cfmakeraw(termios: &mut Termios) {
499 unsafe { c::cfmakeraw(as_mut_ptr(termios).cast()) }
500}
501
502pub(crate) fn isatty(fd: BorrowedFd<'_>) -> bool {
503 unsafe { c::isatty(borrowed_fd(fd)) != 0 }
509}
510
511#[cfg(all(feature = "alloc", feature = "procfs"))]
512#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
513pub(crate) fn ttyname(dirfd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
514 unsafe {
515 match c::ttyname_r(borrowed_fd(dirfd), buf.as_mut_ptr().cast(), buf.len()) {
517 0 => Ok(CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len()),
518 err => Err(io::Errno::from_raw_os_error(err)),
519 }
520 }
521}