Please tell me why this deadlocks: Reading and writing from Paramiko exec_command() file-like objects -- ChannelFile.close() does not work
<pre><code>import paramiko, threading

def Reader(src):
    while True:
        data = src.readline()
        if not data: break
        print data


client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect("172.17.0.2", username="test", password="test")

stdin, stdout, stderr = client.exec_command("dd of=readme.txt")

reader = threading.Thread(target=Reader, args=(stderr, ))
reader.start()

stdin.write("some data")
stdin.flush()
stdin.close()

reader.join()
</code></pre>

<p>This code waits forever at the join(). If I remove the stderr writing thread it works as expected, with the correct text being written to the file 'readme.txt', but I then lose the dd stderr messages.</p>

<p>The probblem is in <code>stdin.close()</code>. According to Paramiko's doc (v1.16):</p>

<blockquote>
  <p><strong>Warning:</strong> To correctly emulate the file object created from a socketâs <code>makefile()</code> method, a <code>Channel</code> and its <code>ChannelFile</code> should be able to be closed or garbage-collected independently. <strong>Currently, closing the <code>ChannelFile</code> does nothing but <em>flush</em> the buffer.</strong></p>
</blockquote>

<p>So you have to use <code>stdin.channel.close()</code>.</p>

<hr>

<p><strong>UPDATE:</strong></p>

<p>Since <em>stdin</em>, <em>stdout</em> and <em>stderr</em> all share one single channel, <code>stdin.channel.close()</code> will also close <em>stderr</em> so your <code>Reader</code> thread may get nothing. The solution is to use <code>stdin.channel.shutdown_write()</code> which disallows writing to the channel but still allows reading from the channel.</p>

<p>Verified it works fine:</p>

<pre class="lang-none prettyprint-override"><code>[STEP 101] # cat foo.py
import paramiko, threading, paramiko

def Reader(src):
    while True:
        data = src.read()
        if not data: break
        print data,

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect("10.11.12.13", username="root", password="password")

stdin, stdout, stderr = client.exec_command("dd of=/tmp/file")

reader = threading.Thread(target=Reader, args=(stderr,) )
reader.start()

stdin.write("some data")
stdin.flush()
stdin.channel.shutdown_write()

reader.join()
[STEP 102] # python foo.py
0+1 records in
0+1 records out
[STEP 103] #
</code></pre>

python paramiko