2

I'm writing a subprocess based python program that acts as a proxy between the user input and the subprocess (trying to go beyond pexpect). I've taken this thread as reference, and some code chunk from pexpect (_read_incoming() method for popen_spawn) to read output (the fcntl method worked, but not satisfactorily).

The code runs but has a problem: There seems to be an additional carriage return being sent to the process. This is causing me issues when I try to do things like sending passwords to ssh etc.

Could you look into what might be the issue? Thanks!

The code is as follows:

from queue import Queue, Empty
from threading import Thread
import subprocess
import signal
import fcntl
import os

global terminating
terminating = False

def setNonBlocking(fd):
    """
    Set the file description of the given file descriptor to non-blocking.
    """
    print(fd)
    flags = fcntl.fcntl(fd, fcntl.F_GETFL)
    flags = flags | os.O_NONBLOCK
    fcntl.fcntl(fd, fcntl.F_SETFL, flags)

def enqueue(out, q):
  fileno = out.fileno()
  while not terminating:
    buf = b''
    try:
      buf = os.read(fileno, 1024)
      if buf and len(buf)>0:
        q.put(buf)
    except OSError as e:
      #print("OS error: {0}".format(e))
      pass
    if not buf:
      q.put(buf)
  # for line in iter(out.readline, b''):
  #   if len(line.strip()) > 0:
  #     print(line)
  #     q.put(line)
  out.close()
  print('Terminating')
  return

def get_output(q):
  out_str = bytes()
  while True:
    try:
      incoming = q.get_nowait()
    except Empty:
      break
    else:
      if incoming is None:
        break
      else:
        out_str +=  incoming

  if out_str:
    return out_str
  else:
    return b''

def explore(cmd="/bin/bash"):
  global terminating
  universal_newlines = False
  p = subprocess.Popen([cmd], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
                              bufsize=0, shell=False, universal_newlines=universal_newlines)
  #setNonBlocking(p.stdout)
  outQueue = Queue()

  outThread = Thread(target=enqueue, args=(p.stdout, outQueue))

  outThread.daemon = True

  outThread.start()

  while True:
    try:
      someInput = input()
      print('[In]:'+someInput)
      someInput += '\n'
      if not universal_newlines:
        p.stdin.write(someInput.encode('utf-8'))
      else:
        p.stdin.write(someInput)
      p.stdin.flush()
      out = get_output(outQueue).decode('utf-8')
      print('[Out]:'+out)
      #p.communicate(someInput+'\n')
    except KeyboardInterrupt:
      print('Interrupting')
      p.send_signal(signal.SIGINT)
      terminating = True
      outThread.join()
      break
  p.wait()

if __name__ == '__main__':
  explore()

Example run:

ls
[In]:ls
[Out]:

[In]:
[Out]:explorer.py
__init__.py

^CInterrupting
Terminating

The second In was an enter from user.


Update:

Tested the alternate using pexpect's popen_spawn module. Same result:

from pexpect.popen_spawn import PopenSpawn as Spawn
import signal

def explore(cmd="/bin/bash"):
  p = Spawn(cmd)
  while True:
    try:
      someInput = input()
      print('[In]:'+someInput)
      p.sendline(someInput)
      out = p.read_nonblocking(size=1024, timeout=-1).decode('utf-8')
      print('[Out]:'+out)
      #p.communicate(someInput+'\n')
    except KeyboardInterrupt:
      print('Interrupting')
      p.sendeof()
      p.kill(signal.SIGINT)
      break

if __name__ == '__main__':
  explore()
Anshul
  • 746
  • 9
  • 22

0 Answers0