[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-126]
server support for stderr & exec_command for the server side of my stderr blunder, add send_stderr & sendall_stderr, and make the sending side of makefile_stderr work correctly. also, call check_channel_exec_request on a server object for exec requests on a channel.
This commit is contained in:
parent
83a932a1b3
commit
a3971274e8
|
@ -566,42 +566,54 @@ class Channel (object):
|
|||
@raise socket.timeout: if no data could be sent before the timeout set
|
||||
by L{settimeout}.
|
||||
"""
|
||||
size = 0
|
||||
try:
|
||||
self.lock.acquire()
|
||||
if self.closed or self.eof_sent:
|
||||
return 0
|
||||
if self.out_window_size == 0:
|
||||
# should we block?
|
||||
if self.timeout == 0.0:
|
||||
raise socket.timeout()
|
||||
# loop here in case we get woken up but a different thread has filled the buffer
|
||||
timeout = self.timeout
|
||||
while self.out_window_size == 0:
|
||||
if self.closed or self.eof_sent:
|
||||
return 0
|
||||
then = time.time()
|
||||
self.out_buffer_cv.wait(timeout)
|
||||
if timeout != None:
|
||||
timeout -= time.time() - then
|
||||
if timeout <= 0.0:
|
||||
raise socket.timeout()
|
||||
# we have some window to squeeze into
|
||||
if self.closed:
|
||||
return 0
|
||||
size = len(s)
|
||||
if self.out_window_size < size:
|
||||
size = self.out_window_size
|
||||
if self.out_max_packet_size - 64 < size:
|
||||
size = self.out_max_packet_size - 64
|
||||
self.lock.acquire()
|
||||
try:
|
||||
size = self._wait_for_send_window(size)
|
||||
if size == 0:
|
||||
# eof or similar
|
||||
return 0
|
||||
m = Message()
|
||||
m.add_byte(chr(MSG_CHANNEL_DATA))
|
||||
m.add_int(self.remote_chanid)
|
||||
m.add_string(s[:size])
|
||||
self.transport._send_user_message(m)
|
||||
self.out_window_size -= size
|
||||
if self.ultra_debug:
|
||||
self._log(DEBUG, 'window down to %d' % self.out_window_size)
|
||||
finally:
|
||||
self.lock.release()
|
||||
return size
|
||||
|
||||
def send_stderr(self, s):
|
||||
"""
|
||||
Send data to the channel on the "stderr" stream. This is normally
|
||||
only used by servers to send output from shell commands -- clients
|
||||
won't use this. Returns the number of bytes sent, or 0 if the channel
|
||||
stream is closed. Applications are responsible for checking that all
|
||||
data has been sent: if only some of the data was transmitted, the
|
||||
application needs to attempt delivery of the remaining data.
|
||||
|
||||
@param s: data to send.
|
||||
@type s: str
|
||||
@return: number of bytes actually sent.
|
||||
@rtype: int
|
||||
|
||||
@raise socket.timeout: if no data could be sent before the timeout set
|
||||
by L{settimeout}.
|
||||
|
||||
@since: 1.1
|
||||
"""
|
||||
size = len(s)
|
||||
self.lock.acquire()
|
||||
try:
|
||||
size = self._wait_for_send_window(size)
|
||||
if size == 0:
|
||||
# eof or similar
|
||||
return 0
|
||||
m = Message()
|
||||
m.add_byte(chr(MSG_CHANNEL_EXTENDED_DATA))
|
||||
m.add_int(self.remote_chanid)
|
||||
m.add_int(1)
|
||||
m.add_string(s[:size])
|
||||
self.transport._send_user_message(m)
|
||||
finally:
|
||||
self.lock.release()
|
||||
return size
|
||||
|
@ -632,6 +644,30 @@ class Channel (object):
|
|||
s = s[sent:]
|
||||
return None
|
||||
|
||||
def sendall_stderr(self, s):
|
||||
"""
|
||||
Send data to the channel's "stderr" stream, without allowing partial
|
||||
results. Unlike L{send_stderr}, this method continues to send data
|
||||
from the given string until all data has been sent or an error occurs.
|
||||
Nothing is returned.
|
||||
|
||||
@param s: data to send to the client as "stderr" output.
|
||||
@type s: str
|
||||
|
||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
||||
set by L{settimeout}.
|
||||
@raise socket.error: if an error occured before the entire string was
|
||||
sent.
|
||||
|
||||
@since: 1.1
|
||||
"""
|
||||
while s:
|
||||
if self.closed:
|
||||
raise socket.error('Socket is closed')
|
||||
sent = self.send_stderr(s)
|
||||
s = s[sent:]
|
||||
return None
|
||||
|
||||
def makefile(self, *params):
|
||||
"""
|
||||
Return a file-like object associated with this channel, without the
|
||||
|
@ -651,9 +687,9 @@ class Channel (object):
|
|||
without a pty will ever have data on the stderr stream.
|
||||
|
||||
The optional C{mode} and C{bufsize} arguments are interpreted the
|
||||
same way as by the built-in C{file()} function in python, except that
|
||||
of course it makes no sense to open this file in any mode other than
|
||||
for reading.
|
||||
same way as by the built-in C{file()} function in python. For a
|
||||
client, it only makes sense to open this file for reading. For a
|
||||
server, it only makes sense to open this file for writing.
|
||||
|
||||
@return: object which can be used for python file I/O.
|
||||
@rtype: L{ChannelFile}
|
||||
|
@ -746,6 +782,44 @@ class Channel (object):
|
|||
def _request_failed(self, m):
|
||||
self.close()
|
||||
|
||||
def _wait_for_send_window(self, size):
|
||||
"""
|
||||
(You are already holding the lock.)
|
||||
Wait for the send window to open up, and allocate up to C{size} bytes
|
||||
for transmission. If no space opens up before the timeout, a timeout
|
||||
exception is raised. Returns the number of bytes available to send
|
||||
(may be less than requested).
|
||||
"""
|
||||
# you are already holding the lock
|
||||
if self.closed or self.eof_sent:
|
||||
return 0
|
||||
if self.out_window_size == 0:
|
||||
# should we block?
|
||||
if self.timeout == 0.0:
|
||||
raise socket.timeout()
|
||||
# loop here in case we get woken up but a different thread has filled the buffer
|
||||
timeout = self.timeout
|
||||
while self.out_window_size == 0:
|
||||
if self.closed or self.eof_sent:
|
||||
return 0
|
||||
then = time.time()
|
||||
self.out_buffer_cv.wait(timeout)
|
||||
if timeout != None:
|
||||
timeout -= time.time() - then
|
||||
if timeout <= 0.0:
|
||||
raise socket.timeout()
|
||||
# we have some window to squeeze into
|
||||
if self.closed:
|
||||
return 0
|
||||
if self.out_window_size < size:
|
||||
size = self.out_window_size
|
||||
if self.out_max_packet_size - 64 < size:
|
||||
size = self.out_max_packet_size - 64
|
||||
self.out_window_size -= size
|
||||
if self.ultra_debug:
|
||||
self._log(DEBUG, 'window down to %d' % self.out_window_size)
|
||||
return size
|
||||
|
||||
def _feed(self, m):
|
||||
if type(m) is str:
|
||||
# passed from _feed_extended
|
||||
|
@ -820,6 +894,12 @@ class Channel (object):
|
|||
ok = False
|
||||
else:
|
||||
ok = server.check_channel_shell_request(self)
|
||||
elif key == 'exec':
|
||||
cmd = m.get_string()
|
||||
if server is None:
|
||||
ok = False
|
||||
else:
|
||||
ok = server.check_channel_exec_request(self, cmd)
|
||||
elif key == 'subsystem':
|
||||
name = m.get_string()
|
||||
if server is None:
|
||||
|
@ -1044,5 +1124,10 @@ class ChannelStderrFile (ChannelFile):
|
|||
def _read(self, size):
|
||||
return self.channel.recv_stderr(size)
|
||||
|
||||
def _write(self, data):
|
||||
self.channel.sendall_stderr(data)
|
||||
return len(data)
|
||||
|
||||
|
||||
|
||||
# vim: set shiftwidth=4 expandtab :
|
||||
|
|
|
@ -162,6 +162,10 @@ class ServerInterface (object):
|
|||
case, L{get_allowed_auths} will be called to report to the client what
|
||||
options it has for continuing the authentication.)
|
||||
|
||||
Note that you don't have to actually verify any key signtature here.
|
||||
If you're willing to accept the key, paramiko will do the work of
|
||||
verifying the client's signature.
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
|
||||
@param username: the username of the authenticating client.
|
||||
|
@ -204,7 +208,7 @@ class ServerInterface (object):
|
|||
@type pixelheight: int
|
||||
@return: C{True} if the psuedo-terminal has been allocated; C{False}
|
||||
otherwise.
|
||||
@rtype: boolean
|
||||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
|
@ -221,7 +225,28 @@ class ServerInterface (object):
|
|||
@type channel: L{Channel}
|
||||
@return: C{True} if this channel is now hooked up to a shell; C{False}
|
||||
if a shell can't or won't be provided.
|
||||
@rtype: boolean
|
||||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_exec_request(self, channel, command):
|
||||
"""
|
||||
Determine if a shell command will be executed for the client. If this
|
||||
method returns C{True}, the channel should be connected to the stdin,
|
||||
stdout, and stderr of the shell command.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
|
||||
@param channel: the L{Channel} the request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@param command: the command to execute.
|
||||
@type command: str
|
||||
@return: C{True} if this channel is now hooked up to the stdin,
|
||||
stdout, and stderr of the executing command; C{False} if the
|
||||
command will not be executed.
|
||||
@rtype: bool
|
||||
|
||||
@since: 1.1
|
||||
"""
|
||||
return False
|
||||
|
||||
|
@ -247,7 +272,7 @@ class ServerInterface (object):
|
|||
@type name: str
|
||||
@return: C{True} if this channel is now hooked up to the requested
|
||||
subsystem; C{False} if that subsystem can't or won't be provided.
|
||||
@rtype: boolean
|
||||
@rtype: bool
|
||||
"""
|
||||
handler_class, larg, kwarg = channel.get_transport()._get_subsystem_handler(name)
|
||||
if handler_class is None:
|
||||
|
@ -276,6 +301,7 @@ class ServerInterface (object):
|
|||
if unknown).
|
||||
@type pixelheight: int
|
||||
@return: C{True} if the terminal was resized; C{False} if not.
|
||||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
|
|
Loading…
Reference in New Issue