Given the imminent end-of-life for Python 2, I didn't bother making the codebase compatible with both. Signed-off-by: Drew DeVault <sir@cmpwn.com>
177 lines
4.7 KiB
Python
177 lines
4.7 KiB
Python
# -*- Mode: Python -*-
|
|
# vi:si:et:sw=4:sts=4:ts=4
|
|
|
|
# from http://code.activestate.com/recipes/440554/
|
|
|
|
import os
|
|
import subprocess
|
|
import errno
|
|
import time
|
|
import sys
|
|
|
|
PIPE = subprocess.PIPE
|
|
|
|
if sys.platform == 'win32':
|
|
from win32file import ReadFile, WriteFile
|
|
from win32pipe import PeekNamedPipe
|
|
import msvcrt
|
|
else:
|
|
import select
|
|
import fcntl
|
|
|
|
|
|
class Popen(subprocess.Popen):
|
|
|
|
def recv(self, maxsize=None):
|
|
return self._recv('stdout', maxsize)
|
|
|
|
def recv_err(self, maxsize=None):
|
|
return self._recv('stderr', maxsize)
|
|
|
|
def send_recv(self, in_put='', maxsize=None):
|
|
return self.send(in_put), self.recv(maxsize), self.recv_err(maxsize)
|
|
|
|
def get_conn_maxsize(self, which, maxsize):
|
|
if maxsize is None:
|
|
maxsize = 1024
|
|
elif maxsize < 1:
|
|
maxsize = 1
|
|
return getattr(self, which), maxsize
|
|
|
|
def _close(self, which):
|
|
getattr(self, which).close()
|
|
setattr(self, which, None)
|
|
|
|
if sys.platform == 'win32':
|
|
|
|
def send(self, in_put):
|
|
if not self.stdin:
|
|
return None
|
|
|
|
try:
|
|
x = msvcrt.get_osfhandle(self.stdin.fileno())
|
|
(errCode, written) = WriteFile(x, in_put)
|
|
except ValueError:
|
|
return self._close('stdin')
|
|
except (subprocess.pywintypes.error, Exception) as why:
|
|
if why.args[0] in (109, errno.ESHUTDOWN):
|
|
return self._close('stdin')
|
|
raise
|
|
|
|
return written
|
|
|
|
def _recv(self, which, maxsize):
|
|
conn, maxsize = self.get_conn_maxsize(which, maxsize)
|
|
if conn is None:
|
|
return None
|
|
|
|
try:
|
|
x = msvcrt.get_osfhandle(conn.fileno())
|
|
(read, nAvail, nMessage) = PeekNamedPipe(x, 0)
|
|
if maxsize < nAvail:
|
|
nAvail = maxsize
|
|
if nAvail > 0:
|
|
(errCode, read) = ReadFile(x, nAvail, None)
|
|
except ValueError:
|
|
return self._close(which)
|
|
except (subprocess.pywintypes.error, Exception) as why:
|
|
if why.args[0] in (109, errno.ESHUTDOWN):
|
|
return self._close(which)
|
|
raise
|
|
|
|
if self.universal_newlines:
|
|
read = self._translate_newlines(read)
|
|
return read
|
|
|
|
else:
|
|
|
|
def send(self, in_put):
|
|
if not self.stdin:
|
|
return None
|
|
|
|
if not select.select([], [self.stdin], [], 0)[1]:
|
|
return 0
|
|
|
|
try:
|
|
written = os.write(self.stdin.fileno(), in_put)
|
|
except OSError as why:
|
|
if why.args[0] == errno.EPIPE: # broken pipe
|
|
return self._close('stdin')
|
|
raise
|
|
|
|
return written
|
|
|
|
def _recv(self, which, maxsize):
|
|
conn, maxsize = self.get_conn_maxsize(which, maxsize)
|
|
if conn is None:
|
|
return None
|
|
|
|
flags = fcntl.fcntl(conn, fcntl.F_GETFL)
|
|
if not conn.closed:
|
|
fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
|
|
|
try:
|
|
if not select.select([conn], [], [], 0)[0]:
|
|
return ''
|
|
|
|
r = conn.read(maxsize)
|
|
if not r:
|
|
return self._close(which)
|
|
|
|
if self.universal_newlines:
|
|
r = self._translate_newlines(r)
|
|
return r
|
|
finally:
|
|
if not conn.closed:
|
|
fcntl.fcntl(conn, fcntl.F_SETFL, flags)
|
|
|
|
|
|
message = "Other end disconnected!"
|
|
|
|
|
|
def recv_some(p, t=.1, e=1, tr=5, stderr=0):
|
|
if tr < 1:
|
|
tr = 1
|
|
x = time.time() + t
|
|
y = []
|
|
r = ''
|
|
pr = p.recv
|
|
if stderr:
|
|
pr = p.recv_err
|
|
while time.time() < x or r:
|
|
r = pr()
|
|
if r is None:
|
|
if e:
|
|
raise Exception(message)
|
|
else:
|
|
break
|
|
elif r:
|
|
y.append(r)
|
|
else:
|
|
time.sleep(max((x - time.time()) / tr, 0))
|
|
return ''.join(x.decode() for x in y).encode()
|
|
|
|
|
|
def send_all(p, data):
|
|
while data:
|
|
sent = p.send(data)
|
|
if sent is None:
|
|
raise Exception(message)
|
|
data = buffer(data, sent)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if sys.platform == 'win32':
|
|
shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
|
|
else:
|
|
shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')
|
|
|
|
a = Popen(shell, stdin=PIPE, stdout=PIPE)
|
|
print(recv_some(a))
|
|
for cmd in commands:
|
|
send_all(a, cmd + tail)
|
|
print(recv_some(a))
|
|
send_all(a, 'exit' + tail)
|
|
print(recv_some(a, e=0))
|
|
a.wait()
|