Skip to content

Commit a2f40c2

Browse files
bors[bot]doy
andauthored
Merge #1259
1259: implement ttyname and ttyname_r r=asomers a=doy this fixes #1204. i know this at least works on linux, but i'm not super sure about other platforms - happy to add conditionals if it makes sense. Co-authored-by: Jesse Luehrs <doy@tozt.net>
2 parents 6f0ca03 + b5eef58 commit a2f40c2

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3737
(#[1222](https://github.com/nix-rust/nix/pull/1222))
3838
- `CpuSet` and `UnixCredentials` now implement `Default`.
3939
(#[1244](https://github.com/nix-rust/nix/pull/1244))
40+
- Added `unistd::ttyname`
41+
(#[1259](https://github.com/nix-rust/nix/pull/1259))
4042

4143
### Changed
4244
- Changed `fallocate` return type from `c_int` to `()` (#[1201](https://github.com/nix-rust/nix/pull/1201))

src/unistd.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,3 +2774,20 @@ impl Group {
27742774
})
27752775
}
27762776
}
2777+
2778+
/// Get the name of the terminal device that is open on file descriptor fd
2779+
/// (see [`ttyname(3)`](http://man7.org/linux/man-pages/man3/ttyname.3.html)).
2780+
pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
2781+
const PATH_MAX: usize = libc::PATH_MAX as usize;
2782+
let mut buf = vec![0_u8; PATH_MAX];
2783+
let c_buf = buf.as_mut_ptr() as *mut libc::c_char;
2784+
2785+
let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
2786+
if ret != 0 {
2787+
return Err(Error::Sys(Errno::from_i32(ret)));
2788+
}
2789+
2790+
let nul = buf.iter().position(|c| *c == b'\0').unwrap();
2791+
buf.truncate(nul);
2792+
Ok(OsString::from_vec(buf).into())
2793+
}

test/test_unistd.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use nix::unistd::ForkResult::*;
77
use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
88
use nix::sys::wait::*;
99
use nix::sys::stat::{self, Mode, SFlag};
10+
#[cfg(not(target_os = "redox"))]
11+
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
1012
use nix::errno::Errno;
1113
#[cfg(not(target_os = "redox"))]
1214
use nix::Error;
@@ -19,6 +21,8 @@ use std::fs::{self, File};
1921
use std::io::Write;
2022
use std::mem;
2123
use std::os::unix::prelude::*;
24+
#[cfg(not(target_os = "redox"))]
25+
use std::path::Path;
2226
use tempfile::{tempdir, tempfile};
2327
use libc::{_exit, off_t};
2428

@@ -964,3 +968,46 @@ fn test_setfsuid() {
964968
// open the temporary file with the current thread filesystem UID
965969
fs::File::open(temp_path_2).unwrap();
966970
}
971+
972+
#[test]
973+
#[cfg(not(target_os = "redox"))]
974+
fn test_ttyname() {
975+
let fd = posix_openpt(OFlag::O_RDWR).expect("posix_openpt failed");
976+
assert!(fd.as_raw_fd() > 0);
977+
978+
// on linux, we can just call ttyname on the pty master directly, but
979+
// apparently osx requires that ttyname is called on a slave pty (can't
980+
// find this documented anywhere, but it seems to empirically be the case)
981+
grantpt(&fd).expect("grantpt failed");
982+
unlockpt(&fd).expect("unlockpt failed");
983+
let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
984+
let fds = open(
985+
Path::new(&sname),
986+
OFlag::O_RDWR,
987+
stat::Mode::empty(),
988+
).expect("open failed");
989+
assert!(fds > 0);
990+
991+
let name = ttyname(fds).expect("ttyname failed");
992+
assert!(name.starts_with("/dev"));
993+
}
994+
995+
#[test]
996+
#[cfg(not(target_os = "redox"))]
997+
fn test_ttyname_not_pty() {
998+
let fd = File::open("/dev/zero").unwrap();
999+
assert!(fd.as_raw_fd() > 0);
1000+
assert_eq!(ttyname(fd.as_raw_fd()), Err(Error::Sys(Errno::ENOTTY)));
1001+
}
1002+
1003+
#[test]
1004+
#[cfg(all(not(target_os = "redox"), not(target_env = "musl")))]
1005+
fn test_ttyname_invalid_fd() {
1006+
assert_eq!(ttyname(-1), Err(Error::Sys(Errno::EBADF)));
1007+
}
1008+
1009+
#[test]
1010+
#[cfg(all(not(target_os = "redox"), target_env = "musl"))]
1011+
fn test_ttyname_invalid_fd() {
1012+
assert_eq!(ttyname(-1), Err(Error::Sys(Errno::ENOTTY)));
1013+
}

0 commit comments

Comments
 (0)