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