Skip to content

Commit 2abc1cc

Browse files
author
Pan
committed
Added public key, agent, password auth, sftp read/write and non-blocking execute and sftp read example scripts.
Added example directory readme. Updated utils.wait_socket for updated API. Updated changelog. Added examples readme. Updated compiler flags in setup.py.
1 parent 93d754b commit 2abc1cc

16 files changed

+2044
-1086
lines changed

Changelog.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
Change Log
22
=============
33

4+
0.5.0
5+
------
6+
7+
Changes
8+
_________
9+
10+
* Implemented SFTP statvfs and SFTP handle fstatvfs methods and integration tests.
11+
* Implemented SFTPStatVFS extension class for file system statistics.
12+
* SFTP read and readdir functions now return size/error code along with data.
13+
* SFTP handle fstat now returns attributes.
14+
* Implemented SFTP handle readdir* methods as python generators,
15+
* SFTP openddir can now be used as a context manager.
16+
* Block directions function renamed to match libssh2.
17+
* Example scripts.
18+
* All session authentication methods now raise ``AuthenticationError`` on failure.
19+
20+
421
0.4.0
522
------
623

README.rst

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ Super fast SSH2 protocol library. ``ssh2-python`` provides Python bindings for `
1111
:alt: Latest Version
1212
.. image:: https://travis-ci.org/ParallelSSH/ssh2-python.svg?branch=master
1313
:target: https://travis-ci.org/ParallelSSH/ssh2-python
14-
.. image:: https://readthedocs.org/projects/ssh2-python/badge/?version=latest
15-
:target: http://ssh2-python.readthedocs.io/en/latest/?badge=latest
16-
:alt: Documentation
14+
1715

1816
Installation
1917
______________
2018

19+
2120
System packages are available on the `latest releases page <https://github.com/ParallelSSH/ssh2-python/releases/latest>`_ built on Centos/RedHat 6/7, Ubuntu 14.04/16.04, Debian 7/8 and Fedora 22/23/24.
2221

22+
2323
Installation from Source
2424
_________________________
2525

@@ -46,15 +46,18 @@ RedHat
4646
API Feature Set
4747
________________
4848

49-
Currently the entirety of the `libssh2`_ API has been implemented. `API documentation is available <https://readthedocs.org/projects/ssh2-python/badge/?version=latest>`_ for ``ssh2-python``.
5049

51-
Complete example scripts for various operations can be found in the `examples directory`.
50+
Currently the vast majority of the `libssh2`_ API has been implemented with only few exceptions.
51+
52+
Complete example scripts for various operations can be found in the `examples directory`_.
53+
54+
In addition, as ``ssh2-python`` is a thin wrapper of ``libssh2`` with Python semantics, its code examples can be ported straight over to Python with only minimal changes.
5255

53-
In addition, ``ssh2-python`` is a thin wrapper of ``libssh2`` with Python semantics - its code examples can be ported straight over to Python with only minimal changes.
5456

5557
Quick Start
5658
_____________
5759

60+
5861
Both byte and unicode strings are accepted as arguments and encoded appropriately. To change default encoding change the value of ``ssh2.utils.ENCODING``. Channel output is always byte strings.
5962

6063
See `Complete Example`_ for a complete example including socket connect.
@@ -124,7 +127,7 @@ Exit Code
124127

125128
.. code-block:: python
126129
127-
print("Exit status: {}".format(channel.get_exit_status()))
130+
print("Exit status: %s" % (channel.get_exit_status()))
128131
129132
130133
.. code-block:: python
@@ -161,7 +164,7 @@ SFTP Read
161164
sftp = session.sftp_init()
162165
with sftp.open(<remote file to read>, 0, 0) as remote_fh, \
163166
open(<file to write>, 'wb') as local_fh:
164-
for data in remote_fh:
167+
for size, data in remote_fh:
165168
local_fh.write(data)
166169
167170
@@ -170,6 +173,8 @@ __________________
170173

171174
A simple usage example looks very similar to ``libssh2`` `usage examples <https://www.libssh2.org/examples/>`_.
172175

176+
See `examples directory <https://github.com/ParallelSSH/ssh2-python/tree/master/examples>`_ for more complete example scripts.
177+
173178
As mentioned, ``ssh2-python`` is intentially a thin wrapper over ``libssh2`` and directly maps most of its API.
174179

175180
Clients using this library can be much simpler to use than interfacing with the ``libssh2`` API directly.
@@ -239,13 +244,13 @@ Extension features:
239244
* Very low overhead
240245
* Super fast as a consequence of the excellent C library it uses and that it uses native code prodigiously
241246
* Object oriented - memory freed automatically and safely as objects expire
242-
* Use Python semantics where applicable, such as iterator support for SFTP file handles
247+
* Use Python semantics where applicable, such as iterator support for reading from SFTP file handles
243248
* Expose errors as Python exceptions where possible
244249
* Provide access to ``libssh2`` error code definitions
245250

246251

247-
Comparison with other Python SSH2 libraries
248-
---------------------------------------------
252+
Comparison with other Python SSH libraries
253+
-------------------------------------------
249254

250255
Performance of above example, compared with Paramiko.
251256

@@ -269,7 +274,6 @@ Performance of above example, compared with Paramiko.
269274
sys 0m0.021s
270275

271276

272-
See `examples directory <https://github.com/ParallelSSH/ssh2-python/tree/master/examples>`_ for more complete example scripts.
273-
274277
.. _libssh2: https://www.libssh2.org
275278
.. _Cython: https://www.cython.org
279+
.. _`examples directory`: https://github.com/ParallelSSH/ssh2-python/tree/master/examples

examples/README.rst

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
Examples
2+
==========
3+
4+
In this directory can be found example scripts using ``ssh2-python`` for various operations.
5+
6+
To try them out, install the library and run the scripts like so:
7+
8+
Pkey from file
9+
---------------
10+
11+
.. code-block:: shell
12+
13+
python examples/publickey_fromfile.py ~/.ssh/id_rsa 'echo me'
14+
me
15+
16+
17+
Non-blocking execute
18+
----------------------
19+
20+
.. code-block:: shell
21+
22+
python examples/nonblocking_execute.py 'echo me'
23+
Would block, waiting for socket to be ready
24+
Would block, waiting for socket to be ready
25+
Waiting for command to finish
26+
Waiting for command to finish
27+
me
28+
29+
30+
SFTP write
31+
-----------
32+
33+
.. code-block:: shell
34+
35+
python examples/sftp_write.py ~/<my source file> ~/<my dest file>
36+
Starting copy of local file <source file> to remote localhost:<dest file>
37+
Finished writing remote file in 0:00:00.006304
38+
39+
Do *not* use the same filename for source and destination when connecting to localhost if you want to keep your file intact.
40+
41+
SFTP read
42+
-----------
43+
44+
.. code-block:: shell
45+
46+
python examples/sftp_read.py ~/<remote file>
47+
Starting read for remote file <remote file>
48+
Finished file read in 0:00:00.045763
49+
50+
Non-blocking SFTP read
51+
-----------------------
52+
53+
Note there is no error checking and file is assumed to exist. The script will hang if it does not.
54+
55+
.. code-block:: shell
56+
57+
python examples/nonblocking_sftp_read.py <remote file>
58+
Would block on sftp init, waiting for socket to be ready
59+
<..>
60+
Would block on sftp init, waiting for socket to be ready
61+
Starting read for remote file <remote file>
62+
Would block on handle open
63+
Would block on read, waiting..
64+
Finished file read in 0:00:00.056730
65+
66+
67+
Password authentication
68+
-------------------------
69+
70+
Authentication with wrong password raises ``AuthenticationError`` exception.
71+
72+
.. code-block:: shell
73+
74+
python examples/password_auth.py 'asdfadf' 'echo me'
75+
Traceback (most recent call last):
76+
File "examples/password_auth.py", line 45, in <module>
77+
main()
78+
File "examples/password_auth.py", line 35, in main
79+
s.userauth_password(args.user, args.password)
80+
File "ssh2/session.pyx", line 250, in ssh2.session.Session.userauth_password
81+
raise AuthenticationError(
82+
ssh2.exceptions.AuthenticationError: ('Error authenticating user %s with password', '<user>')
83+
84+
85+
SSH Agent authentication
86+
--------------------------
87+
88+
.. code-block:: shell
89+
90+
python examples/agent_auth.py 'echo me'
91+
me

examples/agent_auth.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Example script for authentication with SSH Agent"""
2+
3+
from __future__ import print_function
4+
5+
import argparse
6+
import socket
7+
import os
8+
import pwd
9+
10+
from ssh2.session import Session
11+
12+
13+
USERNAME = pwd.getpwuid(os.geteuid()).pw_name
14+
15+
parser = argparse.ArgumentParser()
16+
17+
parser.add_argument('cmd', help="Command to run")
18+
parser.add_argument('--host', dest='host',
19+
default='localhost',
20+
help='Host to connect to')
21+
parser.add_argument('--port', dest='port', default=22,
22+
help="Port to connect on", type=int)
23+
parser.add_argument('-u', dest='user', default=USERNAME,
24+
help="User name to authenticate as")
25+
26+
27+
def main():
28+
args = parser.parse_args()
29+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
30+
sock.connect((args.host, args.port))
31+
s = Session()
32+
s.handshake(sock)
33+
s.agent_auth(args.user)
34+
chan = s.open_session()
35+
chan.execute(args.cmd)
36+
size, data = chan.read()
37+
while size > 0:
38+
print(data)
39+
size, data = chan.read()
40+
41+
42+
if __name__ == "__main__":
43+
main()

examples/nonblocking_execute.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Example script for non-blocking execute.
2+
3+
Note that `ssh2.utils.wait_socket` is not a co-operative routine and will block
4+
the main thread for up to <timeout> (default 1sec). Use for testing purposes
5+
only."""
6+
7+
from __future__ import print_function
8+
9+
import argparse
10+
import socket
11+
import os
12+
import pwd
13+
from select import select
14+
15+
from ssh2.session import Session
16+
# from ssh2.utils import wait_socket
17+
from ssh2.error_codes import LIBSSH2_ERROR_EAGAIN
18+
from ssh2.session import LIBSSH2_SESSION_BLOCK_INBOUND, \
19+
LIBSSH2_SESSION_BLOCK_OUTBOUND
20+
21+
22+
USERNAME = pwd.getpwuid(os.geteuid()).pw_name
23+
24+
parser = argparse.ArgumentParser()
25+
26+
parser.add_argument('cmd', help="Command to run")
27+
parser.add_argument('--host', dest='host',
28+
default='localhost',
29+
help='Host to connect to')
30+
parser.add_argument('--port', dest='port', default=22,
31+
help="Port to connect on", type=int)
32+
parser.add_argument('-u', dest='user', default=USERNAME,
33+
help="User name to authenticate as")
34+
35+
36+
def wait_socket(_socket, session, timeout=1):
37+
"""Helper function for testing non-blocking mode.
38+
39+
This function blocks the calling thread for <timeout> seconds -
40+
to be used only for testing purposes.
41+
42+
Also available at `ssh2.utils.wait_socket`
43+
"""
44+
directions = session.block_directions()
45+
if directions == 0:
46+
return 0
47+
readfds = [_socket] \
48+
if (directions & LIBSSH2_SESSION_BLOCK_INBOUND) else ()
49+
writefds = [_socket] \
50+
if (directions & LIBSSH2_SESSION_BLOCK_OUTBOUND) else ()
51+
return select(readfds, writefds, (), timeout)
52+
53+
54+
def main():
55+
args = parser.parse_args()
56+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
57+
sock.connect((args.host, args.port))
58+
s = Session()
59+
s.handshake(sock)
60+
# Agent connections cannot be used as non-blocking
61+
s.agent_auth(args.user)
62+
# Now we can set non-blocking mode
63+
s.set_blocking(False)
64+
chan = s.open_session()
65+
while chan is None:
66+
print("Would block on session open, waiting for socket to be ready")
67+
wait_socket(sock, s)
68+
chan = s.open_session()
69+
while chan.execute(args.cmd) == LIBSSH2_ERROR_EAGAIN:
70+
print("Would block on channel execute, waiting for socket to be ready")
71+
wait_socket(sock, s)
72+
while chan.wait_eof() == LIBSSH2_ERROR_EAGAIN:
73+
print("Waiting for command to finish")
74+
wait_socket(sock, s)
75+
size, data = chan.read()
76+
while size == LIBSSH2_ERROR_EAGAIN:
77+
print("Waiting to read data from channel")
78+
wait_socket(sock, s)
79+
size, data = chan.read()
80+
while size > 0:
81+
print(data)
82+
size, data = chan.read()
83+
84+
85+
if __name__ == "__main__":
86+
main()

0 commit comments

Comments
 (0)