minimal 21

This commit is contained in:
Benedek Racz 2020-02-04 11:56:19 +01:00
parent fd30f82dcb
commit 38c6970c73

View File

@ -562,34 +562,11 @@ class Wtty:
logger.info(f"Fetch child's process and pid...")
# while True:
# msg = win32gui.GetMessage(0, 0, 0)
# childPid = msg[1][2]
# Sometimes win32gui.GetMessage returns a bogus PID, so keep calling it
# until we can successfully connect to the child or timeout is
# reached
# if childPid:
# try:
# self.__childProcess = win32api.OpenProcess(
# win32con.PROCESS_TERMINATE | win32con.PROCESS_QUERY_INFORMATION, False, childPid)
self.__conProcess = win32api.OpenProcess(
win32con.PROCESS_TERMINATE | win32con.PROCESS_QUERY_INFORMATION, False, self.conpid)
# except pywintypes.error:
# if time.time() > ts + self.timeout:
# break
# else:
# self.pid = childPid
# break
# time.sleep(.05)
logger.info(f"Child's pid: {self.pid}")
# if not self.__childProcess:
# logger.info('ExceptionPexpect: The process ' + args[0] + ' could not be started.')
# raise ExceptionPexpect ('The process ' + args[0] + ' could not be started.')
winHandle = int(win32console.GetConsoleWindow())
self.__switch = True
@ -656,7 +633,7 @@ class Wtty:
' '.join(pyargs),
f"import sys; sys.path = {spath} + sys.path;"
f"args = {args}; import wexpect;"
f"wexpect.ConsoleReaderPipe(wexpect.join_args(args), {pid}, {tid}, cp={cp}, logdir={logdir}, just_init=True)"
f"wexpect.ConsoleReaderPipe(wexpect.join_args(args), {pid}, cp={cp}, just_init=True)"
)
logger.info(f'CreateProcess: {commandLine}')
@ -673,297 +650,14 @@ class Wtty:
win32api.TerminateProcess(self.__childProcess, 1)
# win32api.win32process.TerminateProcess(self.__childProcess, 1)
class ConsoleReader: # pragma: no cover
def __init__(self, path, pid, tid, env = None, cp=None, logdir=None):
self.logdir = logdir
logger.info(f"ConsoleReader started: {self.__class__.__name__}")
logger.info('consolepid: {}'.format(os.getpid()))
logger.debug("OEM code page: %s" % windll.kernel32.GetOEMCP())
logger.debug("ANSI code page: %s" % windll.kernel32.GetACP())
logger.debug("Console output code page: %s" % windll.kernel32.GetConsoleOutputCP())
if cp:
logger.debug("Setting console output code page to %s" % cp)
try:
win32console.SetConsoleOutputCP(cp)
except Exception as e:
logger.info(e)
else:
logger.info("Console output code page: %s" % windll.kernel32.GetConsoleOutputCP())
logger.info('Spawning %s' % path)
try:
try:
consout = self.getConsoleOut()
self.initConsole(consout)
si = win32process.GetStartupInfo()
self.__childProcess, _, childPid, self.__tid = win32process.CreateProcess(None, path, None, None, False,
0, None, None, si)
logger.info('childPid: {} host_pid: {}'.format(childPid, pid))
except Exception:
logger.info(traceback.format_exc())
time.sleep(.1)
win32api.PostThreadMessage(int(tid), win32con.WM_USER, 0, 0)
sys.exit()
time.sleep(.1)
win32api.PostThreadMessage(int(tid), win32con.WM_USER, childPid, 0)
parent = win32api.OpenProcess(win32con.PROCESS_TERMINATE | win32con.PROCESS_QUERY_INFORMATION , 0, int(pid))
paused = False
while True:
consinfo = consout.GetConsoleScreenBufferInfo()
cursorPos = consinfo['CursorPosition']
if win32process.GetExitCodeProcess(parent) != win32con.STILL_ACTIVE or win32process.GetExitCodeProcess(self.__childProcess) != win32con.STILL_ACTIVE:
time.sleep(.1)
try:
win32process.TerminateProcess(self.__childProcess, 0)
except pywintypes.error as e:
# 'Access denied' happens always? Perhaps if not
# running as admin (or UAC enabled under Vista/7).
# Don't log. Child process will exit regardless when
# calling sys.exit
if e.args[0] != winerror.ERROR_ACCESS_DENIED:
logger.info(e)
logger.info('Exiting...')
sys.exit()
if cursorPos.Y > maxconsoleY and not paused:
self.suspendThread()
paused = True
if cursorPos.Y <= maxconsoleY and paused:
self.resumeThread()
paused = False
time.sleep(.1)
except Exception as e:
logger.info(e)
time.sleep(.1)
def handler(self, sig):
logger.info(sig)
return False
def getConsoleOut(self):
consout = win32file.CreateFile('CONOUT$',
win32con.GENERIC_READ | win32con.GENERIC_WRITE,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
0,
0)
return win32console.PyConsoleScreenBufferType(consout)
def initConsole(self, consout):
rect = win32console.PySMALL_RECTType(0, 0, 79, 24)
consout.SetConsoleWindowInfo(True, rect)
size = win32console.PyCOORDType(80, 16000)
consout.SetConsoleScreenBufferSize(size)
pos = win32console.PyCOORDType(0, 0)
# Use NUL as fill char because it displays as whitespace
# (if we interact() with the child)
consout.FillConsoleOutputCharacter(screenbufferfillchar, size.X * size.Y, pos)
def suspendThread(self):
"""Pauses the main thread of the child process."""
handle = windll.kernel32.OpenThread(win32con.THREAD_SUSPEND_RESUME, 0, self.__tid)
win32process.SuspendThread(handle)
def resumeThread(self):
"""Un-pauses the main thread of the child process."""
handle = windll.kernel32.OpenThread(win32con.THREAD_SUSPEND_RESUME, 0, self.__tid)
win32process.ResumeThread(handle)
class ConsoleReader:
pass
class searcher_string (object):
"""This is a plain string search helper for the spawn.expect_any() method.
Attributes:
eof_index - index of EOF, or -1
timeout_index - index of TIMEOUT, or -1
After a successful match by the search() method the following attributes
are available:
start - index into the buffer, first byte of match
end - index into the buffer, first byte after match
match - the matching string itself
"""
def __init__(self, strings):
"""This creates an instance of searcher_string. This argument 'strings'
may be a list; a sequence of strings; or the EOF or TIMEOUT types. """
self.eof_index = -1
self.timeout_index = -1
self._strings = []
for n, s in zip(list(range(len(strings))), strings):
if s is EOF:
self.eof_index = n
continue
if s is TIMEOUT:
self.timeout_index = n
continue
self._strings.append((n, s))
def __str__(self):
"""This returns a human-readable string that represents the state of
the object."""
ss = [ (ns[0],' %d: "%s"' % ns) for ns in self._strings ]
ss.append((-1,'searcher_string:'))
if self.eof_index >= 0:
ss.append ((self.eof_index,' %d: EOF' % self.eof_index))
if self.timeout_index >= 0:
ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index))
ss.sort()
ss = list(zip(*ss))[1]
return '\n'.join(ss)
def search(self, buffer, freshlen, searchwindowsize=None):
"""This searches 'buffer' for the first occurence of one of the search
strings. 'freshlen' must indicate the number of bytes at the end of
'buffer' which have not been searched before. It helps to avoid
searching the same, possibly big, buffer over and over again.
See class spawn for the 'searchwindowsize' argument.
If there is a match this returns the index of that string, and sets
'start', 'end' and 'match'. Otherwise, this returns -1. """
absurd_match = len(buffer)
first_match = absurd_match
# 'freshlen' helps a lot here. Further optimizations could
# possibly include:
#
# using something like the Boyer-Moore Fast String Searching
# Algorithm; pre-compiling the search through a list of
# strings into something that can scan the input once to
# search for all N strings; realize that if we search for
# ['bar', 'baz'] and the input is '...foo' we need not bother
# rescanning until we've read three more bytes.
#
# Sadly, I don't know enough about this interesting topic. /grahn
for index, s in self._strings:
if searchwindowsize is None:
# the match, if any, can only be in the fresh data,
# or at the very end of the old data
offset = -(freshlen+len(s))
else:
# better obey searchwindowsize
offset = -searchwindowsize
n = buffer.find(s, offset)
if n >= 0 and n < first_match:
first_match = n
best_index, best_match = index, s
if first_match == absurd_match:
return -1
self.match = best_match
self.start = first_match
self.end = self.start + len(self.match)
return best_index
pass
class searcher_re (object):
"""This is regular expression string search helper for the
spawn.expect_any() method.
Attributes:
eof_index - index of EOF, or -1
timeout_index - index of TIMEOUT, or -1
After a successful match by the search() method the following attributes
are available:
start - index into the buffer, first byte of match
end - index into the buffer, first byte after match
match - the re.match object returned by a succesful re.search
"""
def __init__(self, patterns):
"""This creates an instance that searches for 'patterns' Where
'patterns' may be a list or other sequence of compiled regular
expressions, or the EOF or TIMEOUT types."""
self.eof_index = -1
self.timeout_index = -1
self._searches = []
for n, s in zip(list(range(len(patterns))), patterns):
if s is EOF:
self.eof_index = n
continue
if s is TIMEOUT:
self.timeout_index = n
continue
self._searches.append((n, s))
def __str__(self):
"""This returns a human-readable string that represents the state of
the object."""
ss = [ (n,' %d: re.compile("%s")' % (n,str(s.pattern))) for n,s in self._searches]
ss.append((-1,'searcher_re:'))
if self.eof_index >= 0:
ss.append ((self.eof_index,' %d: EOF' % self.eof_index))
if self.timeout_index >= 0:
ss.append ((self.timeout_index,' %d: TIMEOUT' % self.timeout_index))
ss.sort()
ss = list(zip(*ss))[1]
return '\n'.join(ss)
def search(self, buffer, freshlen, searchwindowsize=None):
"""This searches 'buffer' for the first occurence of one of the regular
expressions. 'freshlen' must indicate the number of bytes at the end of
'buffer' which have not been searched before.
See class spawn for the 'searchwindowsize' argument.
If there is a match this returns the index of that string, and sets
'start', 'end' and 'match'. Otherwise, returns -1."""
absurd_match = len(buffer)
first_match = absurd_match
# 'freshlen' doesn't help here -- we cannot predict the
# length of a match, and the re module provides no help.
if searchwindowsize is None:
searchstart = 0
else:
searchstart = max(0, len(buffer)-searchwindowsize)
for index, s in self._searches:
match = s.search(buffer, searchstart)
if match is None:
continue
n = match.start()
if n < first_match:
first_match = n
the_match = match
best_index = index
if first_match == absurd_match:
return -1
self.start = first_match
self.match = the_match
self.end = self.match.end()
return best_index
pass
def join_args(args):
"""Joins arguments into a command line. It quotes all arguments that contain