Skip to content

scp examples update #21

@Jhoanor

Description

@Jhoanor

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions