Skip to content

Commit e0dab6a

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 e0dab6a

File tree

3 files changed

+63
-69
lines changed

3 files changed

+63
-69
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

+57-66
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,20 @@ 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`, their types, and even their interpretations
172+
// vary from one platform to another. We want to expose a portable subset as public fields of
173+
// `Termios`, using stable types, and allow access to the rest via accessors, if at all.
174+
//
175+
// So we carry around this private `libc::termios`, initialize our public fields from it
176+
// whenever it's changed (see `set_libc_termios`), and propagate our public fields' values back
177+
// into it whenever we need a real `libc::termios` to pass to `tcsetattr`, `cfmakeraw`, or the
178+
// like (see `get_libc_termios`).
179+
//
180+
// Any fields unknown to us get reasonable values from the initial `tcgetattr` used to create
181+
// this `Termios`, and are thus passed through to subsequent `tcsetattr` calls unchanged.
182+
inner: libc::termios,
173183
/// Input mode flags (see `termios.c_iflag` documentation)
174184
pub input_flags: InputFlags,
175185
/// Output mode flags (see `termios.c_oflag` documentation)
@@ -183,43 +193,24 @@ pub struct Termios {
183193
}
184194

185195
impl Termios {
186-
/// Exposes an immutable reference to the underlying `libc::termios` data structure.
196+
/// Return a copy of the internal `libc::termios` structure.
187197
///
188198
/// This is not part of `nix`'s public API because it requires additional work to maintain type
189199
/// 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()
200+
pub(crate) fn get_libc_termios(&self) -> libc::termios {
201+
let mut termios = self.inner;
202+
termios.c_iflag = self.input_flags.bits();
203+
termios.c_oflag = self.output_flags.bits();
204+
termios.c_cflag = self.control_flags.bits();
205+
termios.c_lflag = self.local_flags.bits();
206+
termios.c_cc = self.control_chars;
207+
termios
218208
}
219209

220-
/// 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();
210+
/// Set the internal `libc::termios` structure to `termios`,
211+
/// and update the public fields.
212+
pub(crate) fn set_libc_termios(&mut self, termios: &libc::termios) {
213+
self.inner = *termios;
223214
self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
224215
self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
225216
self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
@@ -231,7 +222,7 @@ impl Termios {
231222
impl From<libc::termios> for Termios {
232223
fn from(termios: libc::termios) -> Self {
233224
Termios {
234-
inner: RefCell::new(termios),
225+
inner: termios,
235226
input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
236227
output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
237228
control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
@@ -243,7 +234,7 @@ impl From<libc::termios> for Termios {
243234

244235
impl From<Termios> for libc::termios {
245236
fn from(termios: Termios) -> Self {
246-
termios.inner.into_inner()
237+
termios.inner
247238
}
248239
}
249240

@@ -881,7 +872,7 @@ cfg_if!{
881872
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
882873
pub fn cfgetispeed(termios: &Termios) -> u32 {
883874
let inner_termios = termios.get_libc_termios();
884-
unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
875+
unsafe { libc::cfgetispeed(&inner_termios) as u32 }
885876
}
886877

887878
/// Get output baud rate (see
@@ -890,17 +881,17 @@ cfg_if!{
890881
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
891882
pub fn cfgetospeed(termios: &Termios) -> u32 {
892883
let inner_termios = termios.get_libc_termios();
893-
unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
884+
unsafe { libc::cfgetospeed(&inner_termios) as u32 }
894885
}
895886

896887
/// Set input baud rate (see
897888
/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
898889
///
899890
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
900891
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();
892+
let mut inner_termios = termios.get_libc_termios();
893+
let res = unsafe { libc::cfsetispeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
894+
termios.set_libc_termios(&inner_termios);
904895
Errno::result(res).map(drop)
905896
}
906897

@@ -909,9 +900,9 @@ cfg_if!{
909900
///
910901
/// `cfsetospeed()` sets the output baud rate in the given termios structure.
911902
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();
903+
let mut inner_termios = termios.get_libc_termios();
904+
let res = unsafe { libc::cfsetospeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
905+
termios.set_libc_termios(&inner_termios);
915906
Errno::result(res).map(drop)
916907
}
917908

@@ -921,9 +912,9 @@ cfg_if!{
921912
/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
922913
/// this is part of the 4.4BSD standard and not part of POSIX.
923914
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();
915+
let mut inner_termios = termios.get_libc_termios();
916+
let res = unsafe { libc::cfsetspeed(&mut inner_termios as *mut _, baud.into() as libc::speed_t) };
917+
termios.set_libc_termios(&inner_termios);
927918
Errno::result(res).map(drop)
928919
}
929920
} else {
@@ -935,7 +926,7 @@ cfg_if!{
935926
/// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
936927
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
937928
let inner_termios = termios.get_libc_termios();
938-
unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
929+
unsafe { libc::cfgetispeed(&inner_termios) }.try_into().unwrap()
939930
}
940931

941932
/// Get output baud rate (see
@@ -944,17 +935,17 @@ cfg_if!{
944935
/// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
945936
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
946937
let inner_termios = termios.get_libc_termios();
947-
unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
938+
unsafe { libc::cfgetospeed(&inner_termios) }.try_into().unwrap()
948939
}
949940

950941
/// Set input baud rate (see
951942
/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
952943
///
953944
/// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
954945
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();
946+
let mut inner_termios = termios.get_libc_termios();
947+
let res = unsafe { libc::cfsetispeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
948+
termios.set_libc_termios(&inner_termios);
958949
Errno::result(res).map(drop)
959950
}
960951

@@ -963,9 +954,9 @@ cfg_if!{
963954
///
964955
/// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
965956
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();
957+
let mut inner_termios = termios.get_libc_termios();
958+
let res = unsafe { libc::cfsetospeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
959+
termios.set_libc_termios(&inner_termios);
969960
Errno::result(res).map(drop)
970961
}
971962

@@ -975,9 +966,9 @@ cfg_if!{
975966
/// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
976967
/// this is part of the 4.4BSD standard and not part of POSIX.
977968
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();
969+
let mut inner_termios = termios.get_libc_termios();
970+
let res = unsafe { libc::cfsetspeed(&mut inner_termios as *mut _, baud as libc::speed_t) };
971+
termios.set_libc_termios(&inner_termios);
981972
Errno::result(res).map(drop)
982973
}
983974
}
@@ -990,11 +981,11 @@ cfg_if!{
990981
/// character, echoing is disabled, and all special input and output processing is disabled. Note
991982
/// that this is a non-standard function, but is available on Linux and BSDs.
992983
pub fn cfmakeraw(termios: &mut Termios) {
993-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
984+
let mut inner_termios = termios.get_libc_termios();
994985
unsafe {
995-
libc::cfmakeraw(inner_termios);
986+
libc::cfmakeraw(&mut inner_termios as *mut _);
996987
}
997-
termios.update_wrapper();
988+
termios.set_libc_termios(&inner_termios);
998989
}
999990

1000991
/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
@@ -1003,11 +994,11 @@ pub fn cfmakeraw(termios: &mut Termios) {
1003994
/// Note that this is a non-standard function, available on FreeBSD.
1004995
#[cfg(target_os = "freebsd")]
1005996
pub fn cfmakesane(termios: &mut Termios) {
1006-
let inner_termios = unsafe { termios.get_libc_termios_mut() };
997+
let mut inner_termios = termios.get_libc_termios();
1007998
unsafe {
1008-
libc::cfmakesane(inner_termios);
999+
libc::cfmakesane(&mut inner_termios as *mut _);
10091000
}
1010-
termios.update_wrapper();
1001+
termios.set_libc_termios(&inner_termios);
10111002
}
10121003

10131004
/// Return the configuration of a port
@@ -1034,7 +1025,7 @@ pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
10341025
/// *any* of the parameters were successfully set, not only if all were set successfully.
10351026
pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
10361027
let inner_termios = termios.get_libc_termios();
1037-
Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
1028+
Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &inner_termios) }).map(drop)
10381029
}
10391030

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

0 commit comments

Comments
 (0)