Skip to content

Commit 8b097a5

Browse files
committed
Make nix::sys::termios::Termios implement Sync and Copy.
It's kind of tough that `Termios` doesn't implement `Sync`, because then you can't use them with the `signal_hook` crate's `register` function, or any other signal-handling wrapper that tries to be safe. This approach probably entails a bit more copying bits around, but I'd argue the convenience of `Sync` and `Copy` implementations is worth it.
1 parent 5d2a8c2 commit 8b097a5

File tree

3 files changed

+54
-67
lines changed

3 files changed

+54
-67
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66
## [Unreleased] - ReleaseDate
77
### Added
88
- Added `mremap` (#[1306](https://github.com/nix-rust/nix/pull/1306))
9+
- `nix::sys::termios::Termios` now implements `Sync` and `Copy`.
10+
- There are now `From` implementations for converting both ways between
11+
`libc::termios` and `nix::sys::termios::Termios`.
912
### Changed
1013
### Fixed
1114
### Removed

src/pty.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
243243
master.as_mut_ptr(),
244244
slave.as_mut_ptr(),
245245
ptr::null_mut(),
246-
&*inner_termios as *const libc::termios as *mut _,
246+
&inner_termios as *const libc::termios as *mut _,
247247
winsize as *const Winsize as *mut _,
248248
)
249249
}
@@ -266,7 +266,7 @@ pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
266266
master.as_mut_ptr(),
267267
slave.as_mut_ptr(),
268268
ptr::null_mut(),
269-
&*inner_termios as *const libc::termios as *mut _,
269+
&inner_termios as *const libc::termios as *mut _,
270270
ptr::null_mut(),
271271
)
272272
}
@@ -313,7 +313,7 @@ pub fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>
313313
let term = match termios.into() {
314314
Some(termios) => {
315315
let inner_termios = termios.get_libc_termios();
316-
&*inner_termios as *const libc::termios as *mut _
316+
&inner_termios as *const libc::termios as *mut _
317317
},
318318
None => ptr::null_mut(),
319319
};

src/sys/termios.rs

+48-64
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ use cfg_if::cfg_if;
155155
use crate::{Error, Result};
156156
use crate::errno::Errno;
157157
use libc::{self, c_int, tcflag_t};
158-
use std::cell::{Ref, RefCell};
159158
use std::convert::{From, TryFrom};
160159
use std::mem;
161160
use std::os::unix::io::RawFd;
@@ -167,9 +166,14 @@ use crate::unistd::Pid;
167166
/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
168167
/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
169168
/// an open port using `tcgetattr()`.
170-
#[derive(Clone, Debug, Eq, PartialEq)]
169+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171170
pub struct Termios {
172-
inner: RefCell<libc::termios>,
171+
// The actual fields present in `libc::termios` vary from one platform to
172+
// another. By only constructing `Termios` values from actual tcgetattr
173+
// calls, we ensure that unknown fields have reasonable values. We just need
174+
// to remember to update `inner` before it's read, and update the public
175+
// fields after `inner` has been modified.
176+
inner: libc::termios,
173177
/// Input mode flags (see `termios.c_iflag` documentation)
174178
pub input_flags: InputFlags,
175179
/// Output mode flags (see `termios.c_oflag` documentation)
@@ -187,39 +191,19 @@ impl Termios {
187191
///
188192
/// This is not part of `nix`'s public API because it requires additional work to maintain type
189193
/// safety.
190-
pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
191-
{
192-
let mut termios = self.inner.borrow_mut();
193-
termios.c_iflag = self.input_flags.bits();
194-
termios.c_oflag = self.output_flags.bits();
195-
termios.c_cflag = self.control_flags.bits();
196-
termios.c_lflag = self.local_flags.bits();
197-
termios.c_cc = self.control_chars;
198-
}
199-
self.inner.borrow()
200-
}
201-
202-
/// Exposes the inner `libc::termios` datastore within `Termios`.
203-
///
204-
/// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
205-
/// not automatically update the safe wrapper type around it. In this case it should also be
206-
/// paired with a call to `update_wrapper()` so that the wrapper-type and internal
207-
/// representation stay consistent.
208-
pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
209-
{
210-
let mut termios = self.inner.borrow_mut();
211-
termios.c_iflag = self.input_flags.bits();
212-
termios.c_oflag = self.output_flags.bits();
213-
termios.c_cflag = self.control_flags.bits();
214-
termios.c_lflag = self.local_flags.bits();
215-
termios.c_cc = self.control_chars;
216-
}
217-
self.inner.as_ptr()
194+
pub(crate) fn get_libc_termios(&self) -> libc::termios {
195+
let mut termios = self.inner.clone();
196+
termios.c_iflag = self.input_flags.bits();
197+
termios.c_oflag = self.output_flags.bits();
198+
termios.c_cflag = self.control_flags.bits();
199+
termios.c_lflag = self.local_flags.bits();
200+
termios.c_cc = self.control_chars;
201+
termios
218202
}
219203

220204
/// Updates the wrapper values from the internal `libc::termios` data structure.
221-
pub(crate) fn update_wrapper(&mut self) {
222-
let termios = *self.inner.borrow_mut();
205+
pub(crate) fn set_libc_termios(&mut self, termios: &libc::termios) {
206+
self.inner = *termios;
223207
self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
224208
self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
225209
self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
@@ -231,7 +215,7 @@ impl Termios {
231215
impl From<libc::termios> for Termios {
232216
fn from(termios: libc::termios) -> Self {
233217
Termios {
234-
inner: RefCell::new(termios),
218+
inner: termios,
235219
input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
236220
output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
237221
control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
@@ -243,7 +227,7 @@ impl From<libc::termios> for Termios {
243227

244228
impl From<Termios> for libc::termios {
245229
fn from(termios: Termios) -> Self {
246-
termios.inner.into_inner()
230+
termios.inner
247231
}
248232
}
249233

@@ -881,7 +865,7 @@ cfg_if!{
881865
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
882866
pub fn cfgetispeed(termios: &Termios) -> u32 {
883867
let inner_termios = termios.get_libc_termios();
884-
unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
868+
unsafe { libc::cfgetispeed(&inner_termios) as u32 }
885869
}
886870

887871
/// Get output baud rate (see
@@ -890,17 +874,17 @@ cfg_if!{
890874
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
891875
pub fn cfgetospeed(termios: &Termios) -> u32 {
892876
let inner_termios = termios.get_libc_termios();
893-
unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
877+
unsafe { libc::cfgetospeed(&inner_termios) as u32 }
894878
}
895879

896880
/// Set input baud rate (see
897881
/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
898882
///
899883
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
900884
pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
901-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
902-
let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
903-
termios.update_wrapper();
885+
let mut inner_termios = termios.get_libc_termios();
886+
let res = unsafe { libc::cfsetispeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
887+
termios.set_libc_termios(&inner_termios);
904888
Errno::result(res).map(drop)
905889
}
906890

@@ -909,9 +893,9 @@ cfg_if!{
909893
///
910894
/// `cfsetospeed()` sets the output baud rate in the given termios structure.
911895
pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
912-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
913-
let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
914-
termios.update_wrapper();
896+
let mut inner_termios = termios.get_libc_termios();
897+
let res = unsafe { libc::cfsetospeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
898+
termios.set_libc_termios(&inner_termios);
915899
Errno::result(res).map(drop)
916900
}
917901

@@ -921,9 +905,9 @@ cfg_if!{
921905
/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
922906
/// this is part of the 4.4BSD standard and not part of POSIX.
923907
pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
924-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
925-
let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
926-
termios.update_wrapper();
908+
let mut inner_termios = termios.get_libc_termios();
909+
let res = unsafe { libc::cfsetspeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
910+
termios.set_libc_termios(&inner_termios);
927911
Errno::result(res).map(drop)
928912
}
929913
} else {
@@ -935,7 +919,7 @@ cfg_if!{
935919
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
936920
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
937921
let inner_termios = termios.get_libc_termios();
938-
unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
922+
unsafe { libc::cfgetispeed(&inner_termios) }.try_into().unwrap()
939923
}
940924

941925
/// Get output baud rate (see
@@ -944,17 +928,17 @@ cfg_if!{
944928
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
945929
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
946930
let inner_termios = termios.get_libc_termios();
947-
unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
931+
unsafe { libc::cfgetospeed(&inner_termios) }.try_into().unwrap()
948932
}
949933

950934
/// Set input baud rate (see
951935
/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
952936
///
953937
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
954938
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
955-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
956-
let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
957-
termios.update_wrapper();
939+
let mut inner_termios = termios.get_libc_termios();
940+
let res = unsafe { libc::cfsetispeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
941+
termios.set_libc_termios(&inner_termios);
958942
Errno::result(res).map(drop)
959943
}
960944

@@ -963,9 +947,9 @@ cfg_if!{
963947
///
964948
/// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
965949
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
966-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
967-
let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
968-
termios.update_wrapper();
950+
let mut inner_termios = termios.get_libc_termios();
951+
let res = unsafe { libc::cfsetospeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
952+
termios.set_libc_termios(&inner_termios);
969953
Errno::result(res).map(drop)
970954
}
971955

@@ -975,9 +959,9 @@ cfg_if!{
975959
/// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
976960
/// this is part of the 4.4BSD standard and not part of POSIX.
977961
pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
978-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
979-
let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
980-
termios.update_wrapper();
962+
let mut inner_termios = termios.get_libc_termios();
963+
let res = unsafe { libc::cfsetspeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
964+
termios.set_libc_termios(&inner_termios);
981965
Errno::result(res).map(drop)
982966
}
983967
}
@@ -990,11 +974,11 @@ cfg_if!{
990974
/// character, echoing is disabled, and all special input and output processing is disabled. Note
991975
/// that this is a non-standard function, but is available on Linux and BSDs.
992976
pub fn cfmakeraw(termios: &mut Termios) {
993-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
977+
let mut inner_termios = termios.get_libc_termios();
994978
unsafe {
995-
libc::cfmakeraw(inner_termios);
979+
libc::cfmakeraw(&mut inner_termios as *mut _);
996980
}
997-
termios.update_wrapper();
981+
termios.set_libc_termios(&inner_termios);
998982
}
999983

1000984
/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
@@ -1003,11 +987,11 @@ pub fn cfmakeraw(termios: &mut Termios) {
1003987
/// Note that this is a non-standard function, available on FreeBSD.
1004988
#[cfg(target_os = "freebsd")]
1005989
pub fn cfmakesane(termios: &mut Termios) {
1006-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
990+
let mut inner_termios = termios.get_libc_termios();
1007991
unsafe {
1008-
libc::cfmakesane(inner_termios);
992+
libc::cfmakesane(&mut inner_termios as *mut _);
1009993
}
1010-
termios.update_wrapper();
994+
termios.set_libc_termios(&inner_termios);
1011995
}
1012996

1013997
/// Return the configuration of a port
@@ -1034,7 +1018,7 @@ pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
10341018
/// *any* of the parameters were successfully set, not only if all were set successfully.
10351019
pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
10361020
let inner_termios = termios.get_libc_termios();
1037-
Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
1021+
Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &inner_termios) }).map(drop)
10381022
}
10391023

10401024
/// Block until all output data is written (see

0 commit comments

Comments
 (0)