Question

Using twisted.conch, I'm working with the following SSHChannel implementation, which is fed a command to run when instantiated:

class CommandChannel(channel.SSHChannel):
    name = 'session'

    def __init__(self, command, *args, **kwargs):
        channel.SSHChannel.__init__(self, *args, **kwargs)
        self.command = command

    def channelOpen(self, data):
        d = self.conn.sendRequest(
            self, 'exec', common.NS(self.command), wantReply=True)
        d.addCallback(self._gotResponse)

    def _gotResponse(self, _):
        self.conn.sendEOF(self)
        self.loseConnection()

    def dataReceived(self, data):
        log.msg('dataReceived: data=%r' % data)

    def extReceived(self, dataType, data):
        log.msg('extReceived: dataType=%r, data=%r' % (dataType, data))

    def closed(self):
        reactor.stop()

To this I'm feeding a shell command that should be returning some sort of exit status. Can I read what that exit status is? I had hoped extReceived might be my way in, but it doesn't look like it.

In my logs I see this, which seems like it should be a hint where to look, but I'm not sure how to follow said hint:

2013-04-05 10:39:37-0400 [SSHChannel session (0) on SSHService ssh-connection
    on CommandTransport,client] unhandled request for exit-status
Was it helpful?

Solution

There are two extra callbacks you might want to implement for this. These are callbacks on the channel, so you're pretty close already.

The first is request_exit_status. Once you implement this, Conch will no longer log that the exit-status request is unhandled. Here's an example implementation:

def request_exit_status(self, data):
    """
    When the server sends the command's exit status, record it for later
    delivery to the protocol.

    @param data: The network-order four byte representation of the exit
        status of the command.
    @type data: L{bytes}
    """
    (status,) = unpack('>L', data)
    if status != 0:
        self._reason = ProcessTerminated(status, None, None)

The second is request_exit_signal. exit-signal is sent instead of exit-status if the command is terminated by a signal. Here's an example implementation:

def request_exit_signal(self, data):
    """
    When the server sends the command's exit status, record it for later
    delivery to the protocol.

    @param data: The network-order four byte representation of the exit
        signal of the command.
    @type data: L{bytes}
    """
    (signal,) = unpack('>L', data)
    self._reason = ProcessTerminated(None, signal, None)

The general pattern here is that when SSHChannel receives a request for foo it will try to call request_foo with the bytes making up the request.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top