I have a python script which uses subprocess.Popen
to launch a target program: a webserver. I want to interact with the target, so I assign stdin
, stdout
and stderr
to pipes, and I can now read and write to these.
Now, the webserver links the OpenSSL library for TLS processing. As part of TLS processing, it opens a certificate and prompts for a password. Generally, on typing in the password and hitting enter, the webserver starts serving. Now, as you might have guessed, I want to enter this password from the python script. Usually, what I would need to do is write to the server's stdin
. However, there is a problem. When OpenSSL prompts for and reads the password, it does not use stdin
/stdout
. It opens /dev/tty
and uses that instead. As a result, I have to type in the password and enter manually.
The following figure demonstrates the situation.
Below, you can see the a snippet of the output of lsof
for the server:
memcached 25279 USER 0r FIFO 0,13 0t0 7771739 pipe
memcached 25279 USER 1w FIFO 0,13 0t0 7771740 pipe
memcached 25279 USER 2w FIFO 0,13 0t0 7771740 pipe
memcached 25279 USER 3r REG 8,1 3414 3414276 ....key.pem
memcached 25279 USER 4r CHR 5,0 0t0 13 /dev/tty
memcached 25279 USER 5w CHR 5,0 0t0 13 /dev/tty
Is there a way to intercept the call to /dev/tty
for the server, so that I can write to it directly from the script?
The standard way to achieve what you are describing it by using a pty - a pseudo terminal. A pty looks exactly like a normal tty, with the exception of it being controlled by software instead of an actual terminal or a terminal emulator.
In your case you could easily use the pty
module in the python standard library, that provides a few utilities for using pty. Specifically, look at the pty.spawn() function:
Spawn a process, and connect its controlling terminal with the current process’s standard io. This is often used to baffle programs which insist on reading from the controlling terminal. It is expected that the process spawned behind the pty will eventually terminate, and when it does spawn will return.
Example usage:
import subprocess, sys
openssl_cmd = ['openssl', 'foo', 'bar']
p = subprocess.Popen([sys.executable, '-c', 'import pty, sys; pty.spawn(sys.argv[1:])', *openssl_cmd])
p.stdin.write('password\n')
print(p.wait())
In this example, instead of running openssl directly, we run it using spawn
, which means that whatever we write into the process's stdin will be read by openssl from its /dev/tty
.