Writing to File descriptor 0 (STDIN) only affects terminal. Program doesn't read

Question:

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?

Asked By: Marcos Lima

||

Answers:

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

Answered By: Marcos Lima
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.