14

I searched the linux repo and couldn't find a definition for it, so I guess it's something that comes with C or something like that? I'm trying to understand how isatty is able to tell whether a file descriptor is a terminal or not, and what it even means to "be a terminal," in technical language.

I'm not really sure where to find its implementation, and if it's in assembly I won't really be able to follow along with it very easily.

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
  • 1
    I’m guessing the “is this a tty” is simply a flag in the file descriptor struct that was created when the file was opened. Read further at https://unix.stackexchange.com/questions/354373/using-a-file-descriptor-in-a-system-call – racraman Jun 02 '19 at 22:28
  • 2
    On my system, according to `strace`, `isatty` attempts to performs a command that only works on terminals (`ioctl(fd, TCGETS, ...)`) – ikegami Jun 02 '19 at 22:31
  • 3
    "*In unix terminology, a tty is a particular kind of device file which implements a number of additional commands (ioctls) beyond read and write.*" More on this at [What is the exact difference between a 'terminal', a 'shell', a 'tty' and a 'console'?](https://unix.stackexchange.com/q/4126/92782) – ikegami Jun 02 '19 at 22:31
  • 1
    [The TTY demystified](http://www.linusakesson.net/programming/tty/) – ikegami Jun 02 '19 at 22:32

2 Answers2

8

The general strategy for implementing isatty is to attempt a tty-specific ioctl operation on the file descriptor, and check for ENOTTY error result. Traditionally, TCGETS, which is the backend for the tcgetattr function, is used, but this is slightly dangerous since the ioctl number for it on Linux clashes with legacy OSS sound devices, and if the file descriptor actually refers to a certain type of MIDI device, it will make changes to the device. In musl libc, we use TIOCGWINSZ, the "get window size" operation, whose number was not inadvertently reused for any other types of devices and which reliably fails with ENOTTY for non-tty devices.

In theory, you could do it with fstat and check the st_rdev field for the device major number, but this would require a hard-coded list of all device majors which are ttys, and would have broken when new types were added (e.g. USB-serial/ACM devices, uartlite devices, etc.).

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • I think that you're mistaken -- it's TC**S**ETS (tcsetattr) which conflicts with an alsa or oss ioctl, not TC**G**ETS (tcgetattr). If that's not the case, please mention explicitly which ioctl TCGETS conflicts with. –  Jun 03 '19 at 01:21
  • @mosvy: `strace` will happily tell you. – R.. GitHub STOP HELPING ICE Jun 03 '19 at 03:21
  • strace tells me that it's only TC**S**ET[SA][WF]? (A or S depending on 32/64 bit). It doesn't say anything about TC**G**ETS. –  Jun 03 '19 at 03:24
  • @mosvy: Perhaps it was actually a bug in an old version of `strace` - `SNDCTL_TMR_TIMEBASE` seems to have a size parameter that prevents it from clashing with `TCGETS`, but historically `strace` reported that it could be either. I wonder if there were old kernels where `SNDCTL_TMR_TIMEBASE` didn't have the size field. – R.. GitHub STOP HELPING ICE Jun 03 '19 at 03:30
  • TCGETS and SNDCTL_TMR_TIMEBASE don't seem to have changed since at least linux-1.3.0. I had once (wrongly) reached the same conclusion as you -- but it turned out that I was blocking while trying to open a sound device via `/proc/PID/fd/FD`, not while calling `isatty(3)` on it. –  Jun 03 '19 at 03:49
2

isatty(3) is a library function (you won't find anything about in the linux kernel), and is usually implemented by calling tcgetattr(3) and checking its return value.

For example, in the GNU C library (glibc):

/* Return 1 if FD is a terminal, 0 if not.  */
int
__isatty (int fd)
{
  struct termios term;

  return __tcgetattr (fd, &term) == 0;
}

tcgetattr(3) itself will resolve to some ioctl like TCGETA or TCGETS.

Notice that isatty(3) will also return true for a master side of a pseudo-tty, which isn't really a tty -- most tty related ops performed on it will actually apply to its slave side.

On linux, isatty(3) will also return true for /dev/console, which again, isn't a real tty (it cannot be made the controlling tty of a process).

On linux, you can obtain a list of all the tty drivers on your system with their major and minor numbers via cat /proc/tty/drivers. Of course, that only reflects the modules which have been loaded.