Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8ec3425

Browse files
committed
Auto merge of rust-lang#2349 - saethlin:isatty, r=RalfJung
Improve isatty support Per rust-lang/miri#2292 (comment), this is an attempt at > do something more clever with Miri's `isatty` shim Since Unix -> Unix is very simple, I'm starting with a patch that just does that. Happy to augment/rewrite this based on feedback. The linked file in libtest specifically only supports stdout. If we're doing this to support terminal applications, I think it would be strange to support one but not all 3 of the standard streams. The `atty` crate contains a bunch of extra logic that libtest does not contain, in order to support MSYS terminals: softprops/atty@db8d55f so I think if we're going to do Windows support, we should probably access all that logic somehow. I think it's pretty clear that the implementation is not going to change, so I think if we want to, pasting the contents of the `atty` crate into Miri is on the table, instead of taking a dependency.
2 parents 36a7a65 + eefdeac commit 8ec3425

File tree

3 files changed

+76
-6
lines changed

3 files changed

+76
-6
lines changed

src/shims/unix/foreign_items.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
440440
// Miscellaneous
441441
"isatty" => {
442442
let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
443-
this.read_scalar(fd)?.to_i32()?;
444-
// "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error"
445-
// FIXME: we just say nothing is a terminal.
446-
let enotty = this.eval_libc("ENOTTY")?;
447-
this.set_last_error(enotty)?;
448-
this.write_null(dest)?;
443+
let result = this.isatty(fd)?;
444+
this.write_scalar(Scalar::from_i32(result), dest)?;
449445
}
450446
"pthread_atfork" => {
451447
let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/shims/unix/fs.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ trait FileDescriptor: std::fmt::Debug {
5050
) -> InterpResult<'tcx, io::Result<i32>>;
5151

5252
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
53+
54+
#[cfg(unix)]
55+
fn as_unix_host_fd(&self) -> Option<i32>;
5356
}
5457

5558
impl FileDescriptor for FileHandle {
@@ -114,6 +117,12 @@ impl FileDescriptor for FileHandle {
114117
let duplicated = self.file.try_clone()?;
115118
Ok(Box::new(FileHandle { file: duplicated, writable: self.writable }))
116119
}
120+
121+
#[cfg(unix)]
122+
fn as_unix_host_fd(&self) -> Option<i32> {
123+
use std::os::unix::io::AsRawFd;
124+
Some(self.file.as_raw_fd())
125+
}
117126
}
118127

119128
impl FileDescriptor for io::Stdin {
@@ -159,6 +168,11 @@ impl FileDescriptor for io::Stdin {
159168
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
160169
Ok(Box::new(io::stdin()))
161170
}
171+
172+
#[cfg(unix)]
173+
fn as_unix_host_fd(&self) -> Option<i32> {
174+
Some(libc::STDIN_FILENO)
175+
}
162176
}
163177

164178
impl FileDescriptor for io::Stdout {
@@ -209,6 +223,11 @@ impl FileDescriptor for io::Stdout {
209223
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
210224
Ok(Box::new(io::stdout()))
211225
}
226+
227+
#[cfg(unix)]
228+
fn as_unix_host_fd(&self) -> Option<i32> {
229+
Some(libc::STDOUT_FILENO)
230+
}
212231
}
213232

214233
impl FileDescriptor for io::Stderr {
@@ -252,6 +271,11 @@ impl FileDescriptor for io::Stderr {
252271
fn dup(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
253272
Ok(Box::new(io::stderr()))
254273
}
274+
275+
#[cfg(unix)]
276+
fn as_unix_host_fd(&self) -> Option<i32> {
277+
Some(libc::STDERR_FILENO)
278+
}
255279
}
256280

257281
#[derive(Debug)]
@@ -297,6 +321,11 @@ impl FileDescriptor for DummyOutput {
297321
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>> {
298322
Ok(Box::new(DummyOutput))
299323
}
324+
325+
#[cfg(unix)]
326+
fn as_unix_host_fd(&self) -> Option<i32> {
327+
None
328+
}
300329
}
301330

302331
#[derive(Debug)]
@@ -1660,6 +1689,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
16601689
}
16611690
}
16621691
}
1692+
1693+
#[cfg_attr(not(unix), allow(unused))]
1694+
fn isatty(&mut self, miri_fd: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
1695+
let this = self.eval_context_mut();
1696+
#[cfg(unix)]
1697+
{
1698+
let miri_fd = this.read_scalar(miri_fd)?.to_i32()?;
1699+
if let Some(host_fd) =
1700+
this.machine.file_handler.handles.get(&miri_fd).and_then(|fd| fd.as_unix_host_fd())
1701+
{
1702+
// "returns 1 if fd is an open file descriptor referring to a terminal;
1703+
// otherwise 0 is returned, and errno is set to indicate the error"
1704+
// SAFETY: isatty has no preconditions
1705+
let is_tty = unsafe { libc::isatty(host_fd) };
1706+
if is_tty == 0 {
1707+
let errno = std::io::Error::last_os_error()
1708+
.raw_os_error()
1709+
.map(Scalar::from_i32)
1710+
.unwrap();
1711+
this.set_last_error(errno)?;
1712+
}
1713+
return Ok(is_tty);
1714+
}
1715+
}
1716+
// We are attemping to use a Unix interface on a non-Unix platform, or we are on a Unix
1717+
// platform and the passed file descriptor is not open.
1718+
// FIXME: It should be possible to emulate this at least on Windows by using
1719+
// GetConsoleMode.
1720+
let enotty = this.eval_libc("ENOTTY")?;
1721+
this.set_last_error(enotty)?;
1722+
Ok(0)
1723+
}
16631724
}
16641725

16651726
/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when

tests/pass/libc.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,17 @@ fn test_posix_gettimeofday() {
311311
assert_eq!(is_error, -1);
312312
}
313313

314+
fn test_isatty() {
315+
// Testing whether our isatty shim returns the right value would require controlling whether
316+
// these streams are actually TTYs, which is hard.
317+
// For now, we just check that these calls are supported at all.
318+
unsafe {
319+
libc::isatty(libc::STDIN_FILENO);
320+
libc::isatty(libc::STDOUT_FILENO);
321+
libc::isatty(libc::STDERR_FILENO);
322+
}
323+
}
324+
314325
fn main() {
315326
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
316327
test_posix_fadvise();
@@ -335,4 +346,6 @@ fn main() {
335346

336347
#[cfg(any(target_os = "linux"))]
337348
test_clocks();
349+
350+
test_isatty();
338351
}

0 commit comments

Comments
 (0)