Take action if login is redirected
You cannot, in general, tell if the input was redirected; but you can differentiate depending on the type of stdin file. If there was no redirect, it will be the terminal; or it could be configured as a pipe cat foo | ./prog
or redirect from a regular file (like your example) or a redirect from one of several types of special files ( ./prog </dev/sda1
redirecting it from a special file block, etc.).
So, if you want to determine if stdin is a terminal (TTY), you can use isatty
:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (isatty(STDIN_FILENO))
printf("stdin is a tty\n");
else
printf("stdin is not a tty\n");
return 0;
}
If you want to distinguish between other cases (eg pipe, block special file, etc.), you can use fstat
some more information about the file to extract. Note that this doesn't actually tell you if it's a terminal, you still need it for that isatty
(which is a wrapper around ioctl
that fetches terminal information, at least on Linux). You can add the following program above (along with #include <sys/stat.h>
) for more information on what the stdin file is.
if (fstat(STDIN_FILENO, &sb) == 0) {
if (S_ISBLK(sb.st_mode))
printf("stdin is a block special file\n");
else if (S_ISCHR(sb.st_mode))
printf("stdin is a character special file\n");
else if (S_ISDIR(sb.st_mode))
printf("stdin is a directory\n");
else if (S_ISFIFO(sb.st_mode))
printf("stdin is a FIFO (pipe)\n");
else if (S_ISREG(sb.st_mode))
printf("stdin is a regular file\n");
else if (S_ISLNK(sb.st_mode))
printf("stdin is a symlink\n");
else if (S_ISSOCK(sb.st_mode))
printf("stdin is a socket\n");
} else {
printf("failed to stat stdin\n");
}
Note that you will never see the symbolic link from the redirect, since the shell has already flattened the symbolic link before opening the file on behalf of your program; and I was unable to get Bash to open a Unix domain socket. But everything else is possible, including, surprisingly, the catalog.
$ ./isatty
stdin is a tty
stdin is a character special file
$ ./isatty < ./isatty
stdin is not a tty
stdin is a regular file
$ sudo sh -c './isatty < /dev/sda'
stdin is not a tty
stdin is a block special file
$ sudo sh -c './isatty < /dev/console'
stdin is a tty
stdin is a character special file
$ cat isatty | ./isatty
stdin is not a tty
stdin is a FIFO (pipe)
$ mkfifo fifo
$ echo > fifo & # Need to do this or else opening the fifo for read will block
[1] 27931
$ ./isatty < fifo
stdin is not a tty
stdin is a FIFO (pipe)
[1]+ Done echo > fifo
$ ./isatty < .
stdin is not a tty
stdin is a directory
$ socat /dev/null UNIX-LISTEN:./unix-socket &
[1] 28044
$ ./isatty < ./unix-socket
bash: ./unix-socket: No such device or address
$ kill $!
[1]+ Exit 143 socat /dev/null UNIX-LISTEN:./unix-socket
$ ln -s isatty symlink
$ ./isatty < symlink
stdin is not a tty
stdin is a regular file
$ ln -s no-such-file broken-link
$ ./isatty < broken-link
bash: broken-link: No such file or directory
source to share