root / tags / release-2.0.1 / twisted / conch / ssh / session.py

Revision 13066, 7.5 kB (checked in by z3p, 4 years ago)

fix silly typos (closes issue904 and issue905)

  • Property svn:executable set to *
Line 
1 # -*- test-case-name: twisted.conch.test.test_conch -*-
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 #
6
7 """This module contains the implementation of SSHSession, which (by default)
8 allows access to a shell and a python interpreter over SSH.
9
10 This module is unstable.
11
12 Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
13 """
14
15 import struct
16
17 from twisted.internet import protocol, reactor
18 from twisted.python import log
19 from twisted.conch.interfaces import ISession
20 import common, channel
21
22 class SSHSession(channel.SSHChannel):
23
24     name = 'session'
25     def __init__(self, *args, **kw):
26         channel.SSHChannel.__init__(self, *args, **kw)
27         self.buf = ''
28         self.client = None
29         self.session = None
30
31     def request_subsystem(self, data):
32         subsystem, ignored= common.getNS(data)
33         log.msg('asking for subsystem "%s"' % subsystem)
34         client = self.avatar.lookupSubsystem(subsystem, data)
35         if client:
36             pp = SSHSessionProcessProtocol(self)
37             proto = wrapProcessProtocol(pp)
38             client.makeConnection(proto)
39             pp.makeConnection(wrapProtocol(client))
40             self.client = pp
41             return 1
42         else:
43             log.msg('failed to get subsystem')
44             return 0
45
46     def request_shell(self, data):
47         log.msg('getting shell')
48         if not self.session:
49             self.session = ISession(self.avatar)
50         try:
51             pp = SSHSessionProcessProtocol(self)
52             self.session.openShell(pp)
53         except:
54             log.deferr()
55             return 0
56         else:
57             self.client = pp
58             return 1
59
60     def request_exec(self, data):
61         if not self.session:
62             self.session = ISession(self.avatar)
63         f,data = common.getNS(data)
64         log.msg('executing command "%s"' % f)
65         try:
66             pp = SSHSessionProcessProtocol(self)
67             self.session.execCommand(pp, f)
68         except:
69             log.deferr()
70             return 0
71         else:
72             self.client = pp
73             return 1
74
75     def request_pty_req(self, data):
76         if not self.session:
77             self.session = ISession(self.avatar)
78         term, windowSize, modes = parseRequest_pty_req(data)
79         log.msg('pty request: %s %s' % (term, windowSize))
80         try:
81             self.session.getPty(term, windowSize, modes)
82         except:
83             log.err()
84             return 0
85         else:
86             return 1
87
88     def request_window_change(self, data):
89         if not self.session:
90             self.session = ISession(self.avatar)
91         import fcntl, tty
92         winSize = parseRequest_window_change(data)
93         try:
94             self.session.windowChanged(winSize)
95         except:
96             log.msg('error changing window size')
97             log.err()
98             return 0
99         else:
100             return 1
101
102     def dataReceived(self, data):
103         if not self.client:
104             #self.conn.sendClose(self)
105             self.buf += data
106             return
107         self.client.transport.write(data)
108
109     def extReceived(self, dataType, data):
110         if dataType == connection.EXTENDED_DATA_STDERR:
111             if self.client and hasattr(self.client.transport, 'writeErr'):
112                 self.client.transport.writeErr(data)
113         else:
114             log.msg('weird extended data: %s'%dataType)
115
116     def eofReceived(self):
117         if self.session:
118             self.session.eofReceived()
119
120     def closed(self):
121         if self.session:
122             self.session.closed()
123
124     #def closeReceived(self):
125     #    self.loseConnection() # don't know what to do with this
126
127     def loseConnection(self):
128         if self.client:
129             self.client.transport.loseConnection()
130         channel.SSHChannel.loseConnection(self)
131
132 class _ProtocolWrapper(protocol.ProcessProtocol):
133     """
134     This class wraps a Protocol instance in a ProcessProtocol instance.
135     """
136     def __init__(self, proto):
137         self.proto = proto
138
139     def connectionMade(self): self.proto.connectionMade()
140    
141     def outReceived(self, data): self.proto.dataReceived(data)
142
143     def processEnded(self, reason): self.proto.connectionLost(reason)
144
145 class _DummyTransport:
146
147     def __init__(self, proto):
148         self.proto = proto
149
150     def dataReceived(self, data):
151         self.proto.transport.write(data)
152
153     def write(self, data):
154         self.proto.dataReceived(data)
155
156     def writeSequence(self, seq):
157         self.write(''.join(seq))
158
159     def loseConnection(self):
160         self.proto.connectionLost(protocol.connectionDone)
161    
162 def wrapProcessProtocol(inst):
163     if isinstance(inst, protocol.Protocol):
164         return _ProtocolWrapper(inst)
165     else:
166         return inst
167
168 def wrapProtocol(proto):
169     return _DummyTransport(proto)
170
171 class SSHSessionProcessProtocol(protocol.ProcessProtocol):
172
173 #    __implements__ = I
174     def __init__(self, session):
175         self.session = session
176
177     def connectionMade(self):
178         if self.session.buf:
179             self.transport.write(self.session.buf)
180             self.session.buf = None
181
182     def outReceived(self, data):
183         self.session.write(data)
184
185     def errReceived(self, err):
186         self.session.writeExtended(connection.EXTENDED_DATA_STDERR, err)
187
188     def inConnectionLost(self):
189         self.session.conn.sendEOF(self.session)
190
191     def connectionLost(self, reason = None):
192         self.session.loseConnection()
193
194     def processEnded(self, reason = None):
195         if reason and hasattr(reason.value, 'exitCode'):
196             log.msg('exitCode: %s' % repr(reason.value.exitCode))
197             self.session.conn.sendRequest(self.session, 'exit-status', struct.pack('!L', reason.value.exitCode))
198         self.session.loseConnection()
199
200     # transport stuff (we are also a transport!)
201
202     def write(self, data):
203         self.session.write(data)
204
205     def writeSequence(self, seq):
206         self.session.write(''.join(seq))
207
208     def loseConnection(self):
209         self.session.loseConnection()
210
211 class SSHSessionClient(protocol.Protocol):
212
213     def dataReceived(self, data):
214         if self.transport:
215             self.transport.write(data)
216
217 # methods factored out to make live easier on server writers
218 def parseRequest_pty_req(data):
219     """Parse the data from a pty-req request into usable data.
220
221     @returns: a tuple of (terminal type, (rows, cols, xpixel, ypixel), modes)
222     """
223     term, rest = common.getNS(data)
224     cols, rows, xpixel, ypixel = struct.unpack('>4L', rest[: 16])
225     modes, ignored= common.getNS(rest[16:])
226     winSize = (rows, cols, xpixel, ypixel)
227     modes = [(ord(modes[i]), struct.unpack('>L', modes[i+1: i+5])[0]) for i in range(0, len(modes)-1, 5)]
228     return term, winSize, modes
229
230 def packRequest_pty_req(term, (rows, cols, xpixel, ypixel), modes):
231     """Pack a pty-req request so that it is suitable for sending.
232
233     NOTE: modes must be packed before being sent here.
234     """
235     termPacked = common.NS(term)
236     winSizePacked = struct.pack('>4L', cols, rows, xpixel, ypixel)
237     modesPacked = common.NS(modes) # depend on the client packing modes
238     return termPacked + winSizePacked + modesPacked
239
240 def parseRequest_window_change(data):
241     """Parse the data from a window-change request into usuable data.
242
243     @returns: a tuple of (rows, cols, xpixel, ypixel)
244     """
245     cols, rows, xpixel, ypixel = struct.unpack('>4L', data)
246     return rows, cols, xpixel, ypixel
247
248 def packRequest_window_change((rows, cols, xpixel, ypixel)):
249     """Pack a window-change request so that it is suitable for sending.
250     """
251     return struct.pack('>4L', cols, rows, xpixel, ypixel)
252
253 import connection
Note: See TracBrowser for help on using the browser.