I am trying to write to a custom program's stdin with paramiko. Here is a minimal (non-)working example:
~/stdin_to_file.py:
#! /usr/bin/python
import time, sys
f = open('/home/me/LOG','w')
while True:
sys.stdin.flush()
data = sys.stdin.read()
f.write(data+'\n\n')
f.flush()
time.sleep(0.01)
Then I do these commands in IPython:
import paramiko
s = paramiko.client.SSHClient
s.load_system_host_keys()
s.connect('myserver')
stdin, stdout, stderr = s.exec_command('/home/me/stdin_to_file.py')
stdin.write('Hello!')
stdin.flush()
Unfortunately, nothing then appears in ~/LOG. However, if I do
$ ~/stdin_to_file.py < some_other_file
The contents of some_other_file appear in ~/LOG.
Can anyone suggest where I've gone wrong? It seems like I'm doing the logical thing. None of these work either:
stdin.channel.send('hi')
using the get_pty parameter
sending the output of cat - to stdin_to_file.py
sys.stdin.read()
will keep reading until EOF so in your paramiko script you need to close the stdin
(returned from exec_command()
). But how?
stdin.close()
would not work.According to Paramiko's doc (v1.16):
Warning: To correctly emulate the file object created from a socket’s
makefile()
method, aChannel
and itsChannelFile
should be able to be closed or garbage-collected independently. Currently, closing theChannelFile
does nothing but flush the buffer.
stdin.channel.close() also has problem.
Since stdin, stdout and stderr all share one single channel, stdin.channel.close()
will also close stdout and stderr which is not expected.
stdin.channel.shutdown_write()
The correct solution is to use stdin.channel.shutdown_write()
which disallows writing to the channel but still allows reading from the channel so stdout.read()
and stderr.read()
would still work.
See following example to see the difference between stdin.channel.close()
and stdin.channel.shutdown_write()
.
[STEP 101] # cat foo.py
import paramiko, sys, time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy() )
ssh.connect(hostname='127.0.0.1', username='root', password='password')
cmd = "sh -c 'read v; sleep 1; echo $v'"
stdin, stdout, stderr = ssh.exec_command(cmd)
if sys.argv[1] == 'close':
stdin.write('hello world\n')
stdin.flush()
stdin.channel.close()
elif sys.argv[1] == 'shutdown_write':
stdin.channel.send('hello world\n')
stdin.channel.shutdown_write()
else:
raise Exception()
sys.stdout.write(stdout.read() )
[STEP 102] # python foo.py close # It outputs nothing.
[STEP 103] # python foo.py shutdown_write # This works fine.
hello world
[STEP 104] #