I'm studying file descriptors and I'm trying to simulate an input for FD 0 (STDIN). I'm testing in a linux environment. My intention is to write, via terminal, simulating an standard input to the code
Here is my python code:
import sys
from os import getpid
print(f'Hello world! Process: { getpid() }')
for line in sys.stdin:
print(f'Echoing: {line}')
When I try to write into the associated FD 0 in another terminal:
echo "Test" >> /proc/<pid>/fd/0
It only prints in the terminal, the program never reads. I tried to add EOF, break line, heredoc, but I still not find a solution.
Is what I'm trying possible?
CodePudding user response:
Thanks to @Ian Aboot's answers I could find some explanation here:
https://unix.stackexchange.com/questions/385771/writing-to-stdin-of-a-process/385782
According to the answer of the post above:
Accessing /proc/PID/fd/0 doesn't access file descriptor 0 of process PID, it accesses the file which PID has open on file descriptor 0. This is a subtle distinction, but it matters. A file descriptor is a connection that a process has to a file. Writing to a file descriptor writes to the file regardless of how the file has been opened.
and
If /proc/PID/fd/0 is a terminal, then writing to it outputs the data on a terminal. A terminal file is bidirectional: writing to it outputs the data, i.e. the terminal displays the text; reading from a terminal inputs the data, i.e. the terminal transmits user input.
Basically I had to control the terminal process to get the input be forwarded into my process. Writing directly to the /dev/pts* didn't work.
Redirecting the input to a fifo, for example, worked as expected. Maybe there is a way to simulate something between the terminal process and the running program itself so I'll keep the research
EDIT
Finally I found a solution:
I was using echo
command, so it was just writing text to the FD, instead we need to properly make the correct simulation as a device input, fake the input.
How to get it working? We need to simulate the input in the FD.
In the linux there is a way to simulate the terminal input, using the iocontrols (ioctl
). One of the argument options is the TIOCSTI (Terminal input/output control - Simulate terminal input) that inserts a character in the input queue. Basically it simplifies the locking/input management of a given character.
We need the CAP_SYS_ADMIN capability to be able to execute tiocsti()
so I started a Python docker container with this linux capability turned on (see reference 4).
#app/echo.py
import sys
from os import getpid
print(f'Hello world! Process: { getpid() }')
for line in sys.stdin:
print(f'Echoing: {line}')
#app/writer.py
from fcntl import ioctl
from termios import TIOCSTI
import sys
with open(f'/proc/{sys.argv[1]}/fd/0', 'w') as fd:
for char in f'{sys.argv[2]}\n':
ioctl(fd, TIOCSTI, char)
version: '3'
services:
python:
container_name: python_fd
image: python:3.11-rc-bullseye
cap_add:
- CAP_SYS_ADMIN
command:
- /bin/sh
- -c
- |
sleep 10000
volumes:
- ./app:/home/app
working_dir: /home/app/
Terminal 1:
$ docker-compose up -d
$ docker exec -it python_fd sh
# python echo.py
Hello world! Process: <pid>
Terminal 2:
$ docker exec -it python_fd sh
# python writer.py <process pid returned in the previous command> "Hello Lais"
Output of Terminal 1:
Hello Lais
Echoing: Hello Lais
References:
https://unix.stackexchange.com/a/345572
https://manpages.debian.org/bullseye/manpages-dev/ioctl.2.en.html
https://man7.org/linux/man-pages/man7/capabilities.7.html
https://github.com/torvalds/linux/blob/master/drivers/tty/tty_io.c#L2278