[Twisted-Python] Conch: multiple commands

Phil Mayers p.mayers at imperial.ac.uk
Thu Feb 9 20:33:37 EST 2006


Brendan Simon wrote:
> 
> Maybe I just answered my own question.  The sshd server runs "login" (or 
> something) which in turn looks in /etc/passwd for the user and shell, 
> and then invokes that shell (eg. bash).  Is that the way it works?  i.e. 
> the ssh client only sends _one_ command and as far as it is concerned is 
> only invoked one program and it doesn't care that the server has fired 
> off many apps.

Correct

> 
> If that's the case, then maybe I can invoke bash as my command, and just 
> write data to the channel and grab the results ???

It might not be quite that simple. Bash and most shells expect to be 
attached to a TTY with a VT emulation on top, to provide output, 
prompting and so forth. To do it "correctly" you would need some kind of 
TTY client attached to the channel that would present you with prompts.

That said for simple commands that don't cause any complex terminal 
operations, it works fine. I've done it with SSH logins into firewalls 
and routers as an alternative to the (frankly POS) "expect" approach. I 
basically did something like this (though I was talking to an SSH 
process rathern than Twisted's SSH support as I needed SSHv1 - modify as 
appropriate):

class sshSession(TheBases):
   # match "[thing]$" or "[foo at bar]# "
   PROMPT_RE = re.compile(r'[[][^]]+]. ')
   def __init__(self):
     self.stdout = stdout
     self.stdout_lines = []
     self.stderr = stderr
     self.stdin = []
     self.atprompt = False
     self.deferred = None

   def sendCmd(cmd):
     """Queue up the command - returns a deferred callbacked with the
     command stdout or errbacked with stderr if any"""
     d = defer.Deferred()
     self.stdin.append((d, cmd))
     if self.atprompt:
       self.atprompt = False
       self.deferred, cmd = self.stdin.pop(0)
       self.transport.write(cmd+'\r')
     return d

   def errReceived(self, data):
     self.stderr += data

   def outReceived(self, data):
     line = None

     # accumulate stdout
     self.stdout += data
     while self.stdout:
       # look for an end of line in the buffer
       pos = self.stdout.find('\r\n')
       if pos==-1:
         break
       line = self.stdout[:pos]
       self.stdout = self.stdout[pos+2:]
       self.stdout_lines.append(line)

     # ok we've looked at all the complete lines - is whats
     # left in the buffer the prompt?
     if self.PROMPT_RE.match(self.stdout):
       if self.deferred:
         if self.stderr:
           self.deferred.errback(self.stderr)
           self.deferred = None

         if self.stdin:
           # there's another command waiting
           self.atprompt = False
           self.deferred, cmd = self.stdin.pop(0)
           self.transport.write(cmd+'\r\n')
         else:
           self.atprompt = True

...and hopefully that makes sense!

> Is that good or bad???
> 
> Thanks,
> Brendan.
> 
> 
> _______________________________________________
> Twisted-Python mailing list
> Twisted-Python at twistedmatrix.com
> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python





More information about the Twisted-Python mailing list