summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-06-16 00:44:59 -0400
committerJesse Luehrs <doy@tozt.net>2020-07-03 16:47:13 -0400
commitb5eef58e549c34f543316d3c1fd032f8475d42df (patch)
treea9cbca8fc3bda1da1c8b47469de37a28b4e3a63a
parent6f0ca030a30fd813ad74f7dc4d63d3d5538090b0 (diff)
downloadnix-b5eef58e549c34f543316d3c1fd032f8475d42df.zip
implement ttyname
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/unistd.rs17
-rw-r--r--test/test_unistd.rs47
3 files changed, 66 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0efdfc5d..15c7b859 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
(#[1222](https://github.com/nix-rust/nix/pull/1222))
- `CpuSet` and `UnixCredentials` now implement `Default`.
(#[1244](https://github.com/nix-rust/nix/pull/1244))
+- 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))
diff --git a/src/unistd.rs b/src/unistd.rs
index f2902bb4..d53b438c 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -2774,3 +2774,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())
+}
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index 24ed2e56..da677287 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, grantpt, unlockpt, ptsname};
use nix::errno::Errno;
#[cfg(not(target_os = "redox"))]
use nix::Error;
@@ -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};
@@ -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)));
+}