/dev/tty
is a "magical" character device which when opened, will return a handle to the current terminal.
Assuming that the controlling terminal is /dev/pts/1
, a file descriptor opened via /dev/pts/1
and one opened via /dev/tty
will refer to the same device; any write, read or other file operation will work the same on either of them.
In particular, they will accept the same set of ioctls, and TIOCNOTTY
is not an extra ioctl only available via /dev/tty
.
ioctl(fd, TIOCNOTTY)
works the same on any file descriptor referring to a terminal, provided that it's the controlling terminal of the process which calls it.
It doesn't matter if the descriptor was obtained by opening /dev/tty
, /dev/pts/1
, /dev/ptmx
(in which case the ioctl will act on its corresponding slave), or more recently, by a call to ioctl(master, TIOCGPTPEER, flags)
.
Example:
$ cat <<'EOT' >tiocnotty.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <err.h>
int main(int ac, char **av){
if(ioctl(0, TIOCNOTTY)) err(1, "io(TIOCNOTTY)");
if(ac < 2) return 0;
execvp(av[1], av + 1);
err(1, "execvp %s", av[1]);
}
EOT
$ cc -W -Wall tiocnotty.c -o tiocnotty
$ ./tiocnotty
$ ./tiocnotty </dev/tty
$ tty
/dev/pts/0
$ ./tiocnotty </dev/pts/0
Also, it will not really "detach" the current process from the tty; the process will still be able to read from it, a ^C
on the terminal will kill it, etc. Its only effect on a process which is not a session leader is that the tty will no longer be accessible via /dev/tty
, and it will no longer be listed as the controlling tty in /proc/PID/stat
:
$ ./tiocnotty cat
^C
$ ./tiocnotty cat
^Z
[2]+ Stopped ./tiocnotty cat
$ ./tiocnotty cat
foo
foo
^D
$ ./tiocnotty cat /dev/tty
cat: /dev/tty: No such device or address
$ ./tiocnotty awk '{print$7}' /proc/self/stat
0
[the 7th field of /proc/<pid>/stat
is the device id of the controlling tty, see proc(5)
]
If the process calling it is the session leader, it will really detach the session from the tty and will send a SIGHUP
/SIGCONT
pair to the foreground process group from the session. But even then, the terminal will not be closed, and the process will still be able to read from it, if it survives the SIGHUP
:
$ script /dev/null -c 'trap "" HUP; exec ./tiocnotty cat'
Script started, file is /dev/null
lol
lol
^C^C^C^C^C # no controlling tty anymore
wtf
wtf
^D # but still reading fine from it
Script done, file is /dev/null
/dev/tty
is not a symlink like /dev/stdin
=> /dev/fd/0
=> /proc/self/fd/0
=> /dev/pts/0
because it was invented long before virtual dynamic filesystems like procfs (and long before symlinks in general). And many programs have come to depend on its particular semantics (eg. /dev/tty
failing with ENODEV
when the controlling terminal is not accessible).
tty
doesn't report the controlling terminal, but the terminal open on stdin if any. Seeps -o tty= -p "$$"
for the controlling terminal (in practice, they are generally the same at the prompt of an interactive shell). – Stéphane Chazelas Jul 01 '18 at 05:30ps -o tty= -p "$$"
works well! Is it not possible to get the controlling terminal of the current shell from/dev/tty
? – Tim Dec 01 '18 at 02:59/dev/tty
, callingioctl(TIOCGDEV, &dev)
to obtain the maj:min devnum of the real tty, and then looking for it through/dev
. But that will give ambiguous results when more than one devpts filesystem is mounted. – Oct 09 '19 at 15:50