Skip to content

Commit

Permalink
implement ttyname
Browse files Browse the repository at this point in the history
  • Loading branch information
doy committed Jul 2, 2020
1 parent f6cac16 commit 5a2107c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -35,6 +35,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
(#[1252](https://github.com/nix-rust/nix/pull/1252))
- Added support for `Ipv4PacketInfo` and `Ipv6PacketInfo` to `ControlMessage`.
(#[1222](https://github.com/nix-rust/nix/pull/1222))
- Added `unistd::ttyname`
(#[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))
Expand Down
17 changes: 17 additions & 0 deletions src/unistd.rs
Expand Up @@ -2778,3 +2778,20 @@ 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)).
pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
const PATH_MAX: usize = libc::PATH_MAX as usize;
let mut buf = vec![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::from_i32(ret)));
}

let nul = buf.iter().position(|c| *c == b'\0').unwrap();
buf.truncate(nul);
Ok(OsString::from_vec(buf).into())
}
47 changes: 47 additions & 0 deletions test/test_unistd.rs
Expand Up @@ -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, grantpt, unlockpt, ptsname};
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::Error;
Expand All @@ -19,6 +21,8 @@ use std::fs::{self, File};
use std::io::Write;
use std::mem;
use std::os::unix::prelude::*;
#[cfg(not(target_os = "redox"))]
use std::path::Path;
use tempfile::{tempdir, tempfile};
use libc::{_exit, off_t};

Expand Down Expand Up @@ -964,3 +968,46 @@ 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);

// on linux, we can just call ttyname on the pty master directly, but
// apparently osx requires that ttyname is called on a slave pty (can't
// find this documented anywhere, but it seems to empirically be the case)
grantpt(&fd).expect("grantpt failed");
unlockpt(&fd).expect("unlockpt failed");
let sname = unsafe { ptsname(&fd) }.expect("ptsname failed");
let fds = open(
Path::new(&sname),
OFlag::O_RDWR,
stat::Mode::empty(),
).expect("open failed");
assert!(fds > 0);

let name = ttyname(fds).expect("ttyname failed");
assert!(name.starts_with("/dev"));
}

#[test]
#[cfg(not(target_os = "redox"))]
fn test_ttyname_not_pty() {
let fd = File::open("/dev/zero").unwrap();
assert!(fd.as_raw_fd() > 0);
assert_eq!(ttyname(fd.as_raw_fd()), Err(Error::Sys(Errno::ENOTTY)));
}

#[test]
#[cfg(all(not(target_os = "redox"), not(target_env = "musl")))]
fn test_ttyname_invalid_fd() {
assert_eq!(ttyname(-1), Err(Error::Sys(Errno::EBADF)));
}

#[test]
#[cfg(all(not(target_os = "redox"), target_env = "musl"))]
fn test_ttyname_invalid_fd() {
assert_eq!(ttyname(-1), Err(Error::Sys(Errno::ENOTTY)));
}

0 comments on commit 5a2107c

Please sign in to comment.