Reputation: 150
Background: I am using python and paramiko to automate the process I go through everytime I have to hand in a program for a class. We use a command called "handin" to submit source code, but this must be done from a school computer. So when I submit code from home, I have to: sftp into school servers, put the files in a dir, ssh into school computer, use 'handin' command
I can successfully put files onto the school machines. The problem occurs when I try to use exec_command('handin my files') and then read the output to determine the next action.
so I have
try:
(stdin, stdout, stderr) = client.exec_command(s)
except:
print 'whoops'
sys.exit()
print stdout.readlines()
But this causes a deadlock for some reason, the script appears to be doing nothing and I have to eventually kill the entire process (ctrl+c doesnt work). Im not sure if exec_command is not completing correctly (even though it is getting out of the try/catch block) or if im having network problems or what.
Any ideas?
update:
The problem is with interacting with the handin command during execution. After executing the command, handin may or may not still be running. If its the first time submitting it says success, blah blah, and finishes executing. All is well. But if I am re-submitting I have to authorize an overwrite (stdin.write('y')) for each file.
TL/DR:
How do I check if a exec_command() is still running, waiting for input, and readline() from stdout accordingly?
Upvotes: 11
Views: 49637
Reputation: 41
Okay so I was recently stuck with this problem too. Checking the docs and a bit more searching let me to a acceptable answer that I can use to send inputs and check output for simple scripts that might require user input
instead of trying to use .read()
or .readline()
which waits until the program is finished running.
query them asking for just one byte like .read(1)
or .readline(1)
until stdout.channel.recv_ready()
returns True
which says if there is something in
stdout to read
it might be better to use stdout.channel.recv(9999)
instead of realine(1)
since it lets you ask for 9999 bytes instead of just 1 with read/ readline and you won't be deadlocked if you ask for more bytes than what exists.
http://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.recv_ready
below is just the boilerplate code to see a basic sudo command executed
ssh.connect(
hostname=host,
port=port,
username=username,
password=password,
)
def readlines(stdout):
line = ''
while sout.channel.recv_ready():
line += sout.readline(1)
return line
>> stdin, stdout, stderr = ssh.exec_command('sudo ls -al /root', get_pty=True)
>> output = readlines(stdout)
>> print(output)
'[sudo] password for user: '
>> if "[sudo] password for" in output:
stdin.write(password + '\n')
stdin.flush()
>> output = readlines(stdout)
>> print(output)
total 88
drwx------ 7 root root 14 May 6 21:40 .
drwxr-xr-x 18 root root 24 Apr 29 18:46 ..
-rw------- 1 root root 3619 May 7 13:19 .bash_history
Now you can use this to basically scale it to any checks you want. There might be a better way to implement this with invoke_shell but this works for my purpose.
Upvotes: 0
Reputation: 109
This is an old thread but I have seen this issue as well. This is due to a bug in Paramiko which it cannot process large STDOUT from the remote command so it hangs.
Ref: https://github.com/paramiko/paramiko/issues/515
Possible Solution: https://gist.github.com/matjohn2/2c1c4988e17112a34f310667e0ff6e7e
Upvotes: 0
Reputation: 33
To workaround this and get the output of Ifconfig, you may wanna try to use the option get_pty=True
e.g:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
def MonitorProcess():
ssh.connect('10.2.0.230', username='ibmsys1', password='passw0rd', timeout=5)
stdin, stdout, stderr = ssh.exec_command('/sbin/ifconfig', timeout=3, get_pty=True)
#stdin, stdout, stderr = ssh.exec_command('/sbin/ifconfig') -> simple exec ex.
print stdout.read()
MonitorProcess()
Upvotes: 0
Reputation: 4839
I don't see anything wrong with the code snippit above. The program below is a complete script which logs into a host and runs the ls command to view files. I just tried it and it works for me. Perhaps try this and see if it works for you. If it does not work, I suspect some issue specific to either your ssh server, command you are running, or paramiko installation. If it does work for you, its just a matter of making changes to this to move towards your existing functionality and see where it breaks.
import paramiko
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('<ip address here>',username='<username here>',password='<password here>')
stdin,stdout,stderr = ssh.exec_command("ls /")
print stdout.readlines()
If that works for you my next suggestion would be to try replacing the 'ls /' with the actual handin command you are trying to run. Its possible that command is hanging waiting for user input, etc.
Upvotes: 13
Reputation: 28696
The problem might be that the remote command is waiting for input (it expects you to write something to stdin, not knowing that you aren't going to unless you tell so). Try stdin.channel.shutdown_write()
(I believe stdin.close()
by itself won't do the trick: that will only cause a flush)
Upvotes: 6