3

Taken from this answer:

Terminal 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

Terminal 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

I don't quite understand why writing to the file descriptor corresponding to the stdin of the cat process bypasses the process itself, but appears on the terminal. The relation among the terminal, file descriptor, device file, console are confusing to me. Also, I feel sometimes these are abused in technical writing. Can someone enlighten me?

sherlock
  • 141
  • 5

2 Answers2

4

You're thinking about the standard file descriptors the wrong way.

STDIN points to an input device. If you obtain the STDIN belonging to another process, you could use it to steal that process's input. You're not supposed to write to it, and if you do, there's no particular reason to expect that output to wind up being read by the target process; that will only happen if the device happens to loops its output back to its input.

Similarly, STDOUT points to an output device. If you obtain the STDOUT belonging to another process, you could use it to generate output that will go to the same file or terminal that the other process is using. You shouldn't read from it, and if you do, you're unlikely to see the process's output.

In this particular scenario, as Håkan already pointed out, both STDIN and STDOUT point to the same device, so while you shouldn't write to STDIN, if you do it has the same effect as writing to STDOUT, i.e., it sends your output directly to the terminal. If the standard file descriptors were anything else, e.g., if you were using file redirections or piping, then writing to STDIN would not be the same as writing to STDOUT.

Think of it this way: if your process wrote to its own standard input, you wouldn't in general expect to see that output as new input. The fact that it is a different process doing the writing doesn't change that.

In particular, if your process is running on a virtual terminal (without any redirections) then when you write to standard output you expect that output to be displayed on the terminal, not fed back into your input. The fact that you incorrectly use the standard input file descriptor for the terminal rather than the standard output file descriptor won't change that, and it won't matter which process is doing the writing either. (*)


It may be instructive to consider some specific examples where a process writes to its own standard input.

What if standard input has been redirected from a file?

foo@bar:~/test$ cat hello
hi
foo@bar:~/test$ (cat; echo hello there > /dev/stdin; cat) < hello
hi
lo there

The contents of the file are overwritten; if the process did not finish reading the file first, or if the file is now longer than it was before, it will then read some or all of the content that it just wrote. Note that because we've already read "hi" and a newline, when we keep reading we skip the first three characters of "hello there".

What if standard input is a pipe?

foo@bar:~/test$ echo weird | (echo hello > /dev/stdin; cat)
weird
hello

Apparently a Linux pipe device does in fact loop its output back to its input, but I'm not sure whether this behaviour is guaranteed by POSIX or depends on the specific implementation. I'd avoid making use of this trick if I was you!


So what is the proper way to send a process some input?

Well, one option is described in this answer.

Another is to use a pipe correctly, e.g.,

echo I'm sending some input | cat > myinput

Here the echo process is sending input to the cat process. This is guaranteed to work properly because echo is sending the data to a file descriptor pointing to one end of the pipe and cat is receiving the data from a file descriptor pointing to the other end of the pipe.

In both cases the principle is the same. The target process is reading from a specific device, and we need to cause that device to generate the output we want. How we might do that depends on what the device is.


(*) The virtual terminal could have been designed so as to provide different devices for the shell's standard input and standard output, and the device attached to the standard input could have been programmed to loop any characters written to it back into the input stream. But it doesn't have to work that way, and as it happens, it doesn't, perhaps because the primary design goal for the first virtual terminals was to behave in the same way as the old-fashioned physical terminals which they were replacing.

Harry Johnston
  • 5,875
  • 4
  • 35
  • 52
2

First of all, based on the observed behavior I assume that this is about Linux. Under a different OS, I expect that things may turn out differently (for better or worse).

I believe the most practically useful takeaway from this may be that this is not the proper way of interacting with a process.

What seems to be the main cause of the confusion here is that you have higher expectations of the Linux /proc filesystem than what it actually delivers on.

If you take a closer look at what is actually there in the /proc filesystem, you will see something like this:

$ ls -l /proc/29058/fd/
total 0
lrwx------ 1 user1 user1 64 Jan  1 20:39 0 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 1 -> /dev/pts/2
lrwx------ 1 user1 user1 64 Jan  1 20:39 2 -> /dev/pts/2
$

As you can see, the representation in /proc for all of the "standard" fds (0 / stdin, 1 / stdout, 2 / stderr) of this process are symlinks, all of them linked to the exact same thing, namely the terminal (pseudoterminal slave).

Ie, you were never writing to stdin of your cat process in the first place, you were writing to the terminal where it resides.
And this happened not because the concept of stdin works in some strange way, but rather because the Linux /proc filesystem just links to whatever thing the process is reading as its stdin.
In a case like this, where your process is reading from a terminal as its stdin, the link points to a device node that represents both the input (when reading from the device) and output (when writing to the device) aspects of that terminal, this node therefore only relates to stdin of your process when you read from it, writing to it has no relation to stdin of your process.

Håkan Lindqvist
  • 33,741
  • 5
  • 65
  • 90
  • @HarryJohnston Thank you for your feedback. That paragraph was written very much from the perspective of what happens when writing to said device (as in the question), but it didn't make that distinction clear and it was indeed downright misleading without that context. I hope my edit helps to clarify matters. – Håkan Lindqvist Jan 03 '19 at 03:32