diff --git a/CHANGELOG.md b/CHANGELOG.md index 346af45bdc..b79fbdb65c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). (#[1233](https://github.com/nix-rust/nix/pull/1233)) - Added `EventFilter` bitflags for `EV_DISPATCH` and `EV_RECEIPT` on OpenBSD. (#[1252](https://github.com/nix-rust/nix/pull/1252)) +- Added `unistd::ttyname` and `unistd::ttyname_r`. + (#[1259](https://github.com/nix-rust/nix/pull/1259)) ### Changed - Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201)) diff --git a/src/unistd.rs b/src/unistd.rs index 375ae82e3c..a115449557 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2778,3 +2778,42 @@ impl Group { }) } } + +/// Get the name of the terminal device that is open on file descriptor fd +/// (see [`ttyname(3)`](http://man7.org/linux/man-pages/man3/ttyname.3.html)). +/// +/// # Safety +/// +/// `ttyname()` mutates global variables and is *not* threadsafe. +/// Mutating global variables is always considered `unsafe` by Rust and this +/// function is marked as `unsafe` to reflect that. +/// +/// For a threadsafe and non-`unsafe` alternative, see `ttyname_r()`. +#[inline] +pub unsafe fn ttyname(fd: RawFd) -> Result { + let buf = libc::ttyname(fd); + if buf.is_null() { + return Err(Error::Sys(Errno::last())); + } + + Ok(CStr::from_ptr(buf).to_string_lossy().into_owned()) +} + +/// Get the name of the terminal device that is open on file descriptor fd +/// (see [`ttyname(3)`](http://man7.org/linux/man-pages/man3/ttyname.3.html)). +/// +/// This is the threadsafe version of `ttyname()`. +#[inline] +pub fn ttyname_r(fd: RawFd) -> Result { + const PATH_MAX: usize = libc::PATH_MAX as usize; + let mut buf = [0_u8; PATH_MAX]; + let c_buf = buf.as_mut_ptr() as *mut libc::c_char; + + let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) }; + if ret != 0 { + return Err(Error::Sys(Errno::last())); + } + + let nul = buf.iter().position(|c| *c == b'\0').unwrap(); + Ok(std::string::String::from_utf8_lossy(&buf[..nul]).to_string()) +} diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 24ed2e564e..c06ebe301e 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -7,6 +7,8 @@ use nix::unistd::ForkResult::*; use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction}; use nix::sys::wait::*; use nix::sys::stat::{self, Mode, SFlag}; +#[cfg(not(target_os = "redox"))] +use nix::pty::posix_openpt; use nix::errno::Errno; #[cfg(not(target_os = "redox"))] use nix::Error; @@ -964,3 +966,13 @@ fn test_setfsuid() { // open the temporary file with the current thread filesystem UID fs::File::open(temp_path_2).unwrap(); } + +#[test] +#[cfg(not(target_os = "redox"))] +fn test_ttyname() { + let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed"); + assert!(fd.as_raw_fd() > 0); + let name_r = ttyname_r(fd.as_raw_fd()).expect("ttyname_r failed"); + let name = unsafe { ttyname(fd.as_raw_fd()) }.expect("ttyname failed"); + assert_eq!(name, name_r); +}