[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
|
@raise socket.timeout: if no data could be sent before the timeout set
|
||||||
by L{settimeout}.
|
by L{settimeout}.
|
||||||
"""
|
"""
|
||||||
size = 0
|
size = len(s)
|
||||||
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
size = self._wait_for_send_window(size)
|
||||||
if self.closed or self.eof_sent:
|
if size == 0:
|
||||||
|
# eof or similar
|
||||||
return 0
|
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
|
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_DATA))
|
m.add_byte(chr(MSG_CHANNEL_DATA))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
m.add_string(s[:size])
|
m.add_string(s[:size])
|
||||||
self.transport._send_user_message(m)
|
self.transport._send_user_message(m)
|
||||||
self.out_window_size -= size
|
finally:
|
||||||
if self.ultra_debug:
|
self.lock.release()
|
||||||
self._log(DEBUG, 'window down to %d' % self.out_window_size)
|
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:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
return size
|
return size
|
||||||
|
@ -616,9 +628,9 @@ class Channel (object):
|
||||||
@type s: str
|
@type s: str
|
||||||
|
|
||||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
@raise socket.timeout: if sending stalled for longer than the timeout
|
||||||
set by L{settimeout}.
|
set by L{settimeout}.
|
||||||
@raise socket.error: if an error occured before the entire string was
|
@raise socket.error: if an error occured before the entire string was
|
||||||
sent.
|
sent.
|
||||||
|
|
||||||
@note: If the channel is closed while only part of the data hase been
|
@note: If the channel is closed while only part of the data hase been
|
||||||
sent, there is no way to determine how much data (if any) was sent.
|
sent, there is no way to determine how much data (if any) was sent.
|
||||||
|
@ -632,6 +644,30 @@ class Channel (object):
|
||||||
s = s[sent:]
|
s = s[sent:]
|
||||||
return None
|
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):
|
def makefile(self, *params):
|
||||||
"""
|
"""
|
||||||
Return a file-like object associated with this channel, without the
|
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.
|
without a pty will ever have data on the stderr stream.
|
||||||
|
|
||||||
The optional C{mode} and C{bufsize} arguments are interpreted the
|
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
|
same way as by the built-in C{file()} function in python. For a
|
||||||
of course it makes no sense to open this file in any mode other than
|
client, it only makes sense to open this file for reading. For a
|
||||||
for reading.
|
server, it only makes sense to open this file for writing.
|
||||||
|
|
||||||
@return: object which can be used for python file I/O.
|
@return: object which can be used for python file I/O.
|
||||||
@rtype: L{ChannelFile}
|
@rtype: L{ChannelFile}
|
||||||
|
@ -746,6 +782,44 @@ class Channel (object):
|
||||||
def _request_failed(self, m):
|
def _request_failed(self, m):
|
||||||
self.close()
|
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):
|
def _feed(self, m):
|
||||||
if type(m) is str:
|
if type(m) is str:
|
||||||
# passed from _feed_extended
|
# passed from _feed_extended
|
||||||
|
@ -820,6 +894,12 @@ class Channel (object):
|
||||||
ok = False
|
ok = False
|
||||||
else:
|
else:
|
||||||
ok = server.check_channel_shell_request(self)
|
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':
|
elif key == 'subsystem':
|
||||||
name = m.get_string()
|
name = m.get_string()
|
||||||
if server is None:
|
if server is None:
|
||||||
|
@ -1043,6 +1123,11 @@ class ChannelStderrFile (ChannelFile):
|
||||||
|
|
||||||
def _read(self, size):
|
def _read(self, size):
|
||||||
return self.channel.recv_stderr(size)
|
return self.channel.recv_stderr(size)
|
||||||
|
|
||||||
|
def _write(self, data):
|
||||||
|
self.channel.sendall_stderr(data)
|
||||||
|
return len(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# vim: set shiftwidth=4 expandtab :
|
# 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
|
case, L{get_allowed_auths} will be called to report to the client what
|
||||||
options it has for continuing the authentication.)
|
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}.
|
The default implementation always returns L{AUTH_FAILED}.
|
||||||
|
|
||||||
@param username: the username of the authenticating client.
|
@param username: the username of the authenticating client.
|
||||||
|
@ -204,7 +208,7 @@ class ServerInterface (object):
|
||||||
@type pixelheight: int
|
@type pixelheight: int
|
||||||
@return: C{True} if the psuedo-terminal has been allocated; C{False}
|
@return: C{True} if the psuedo-terminal has been allocated; C{False}
|
||||||
otherwise.
|
otherwise.
|
||||||
@rtype: boolean
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -221,10 +225,31 @@ class ServerInterface (object):
|
||||||
@type channel: L{Channel}
|
@type channel: L{Channel}
|
||||||
@return: C{True} if this channel is now hooked up to a shell; C{False}
|
@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.
|
if a shell can't or won't be provided.
|
||||||
@rtype: boolean
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
return False
|
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
|
||||||
|
|
||||||
def check_channel_subsystem_request(self, channel, name):
|
def check_channel_subsystem_request(self, channel, name):
|
||||||
"""
|
"""
|
||||||
Determine if a requested subsystem will be provided to the client on
|
Determine if a requested subsystem will be provided to the client on
|
||||||
|
@ -247,7 +272,7 @@ class ServerInterface (object):
|
||||||
@type name: str
|
@type name: str
|
||||||
@return: C{True} if this channel is now hooked up to the requested
|
@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.
|
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)
|
handler_class, larg, kwarg = channel.get_transport()._get_subsystem_handler(name)
|
||||||
if handler_class is None:
|
if handler_class is None:
|
||||||
|
@ -275,7 +300,8 @@ class ServerInterface (object):
|
||||||
@param pixelheight: height of screen in pixels, if known (may be C{0}
|
@param pixelheight: height of screen in pixels, if known (may be C{0}
|
||||||
if unknown).
|
if unknown).
|
||||||
@type pixelheight: int
|
@type pixelheight: int
|
||||||
@return: C{True} if the terminal was resized; C{False} if not.
|
@return: C{True} if the terminal was resized; C{False} if not.
|
||||||
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue