-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Since I had some trouble getting scp working, I'd thought I'd better share my examples.
In the regular examples there is only a scp_update, which doesn't work, not on OpenBSD at least, and getting a scp_download working was even a bit more awkward. (it now looks so simple though, but I didn't want to use sftp at first...)
Because no EOF is set when downloading, I had to keep an eye on filesize. In the C examples of the libssh2 package, that is the way it is done anyhow.
One would expect that after reading the last block of a file the next read would return "", which could be used to detect the eof. But it doesn't. It just hangs somewhere in a waiting loop, and I couldn't determine how to prevent that.
channel.close() will close the channel, but might generate an error. This is caused by a bug in channel.c line 46, which schould be: if (rc && rc != LIBSSH2_ERROR_EAGAIN)
To upload a file I've rewritten the send routine in the scp_upload.py example as follows. Reading and writing blocks instead of lines.
import os
...
def send(self, remote_path, mode=0644):
file_size = os.path.getsize(remote_path)
channel = self.session.scp_send(remote_path, mode, file_size)
f=open(remote_path, "rb")
send = 0
while True:
data = f.read(1024)
channel.write(data)
send = send + len(data)
if send >= file_size:
break
f.close()
channel.close()
To download a file I used the same scp_upload.py example with putting in a receive routine, reading blocks over the channel.
The last block get an extra 0 - eof byte, increasing the filesize, so I strip that (checking if it really is a 0, which can probably be omitted)
The only problem now is to determine the filesize. In C, it comes with the scp_recv call, but pylibssh2 puts a NULL in the fileinfo field :-(. So it has to be done with sftp:
import os
def _prepare_sock(self):
...
self.sftp = self.session._session.sftp_init()
...
def receive(self, remote_path, mode=0644):
file_size = self.sftp.get_stat(remote_path)[0]
channel = self.session.scp_recv(remote_path)
f=open(remote_path, "wb")
got = 0
while True:
data = channel.read(1024)
got = got + len(data)
if got > file_size:
if (data[-1:]=="\0"): # probably not needed
f.write(data[:-1])
break
else:
f.write(data)
f.close()
channel.close()
os.chmod(remote_path,mode)
...
...
myscp.receive(sys.argv[4])
It would be nice if the regular examples can be updated with these.
Regards