1*10465441SEvalZero# 2*10465441SEvalZero# File : win32spawn.py 3*10465441SEvalZero# This file is part of RT-Thread RTOS 4*10465441SEvalZero# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team 5*10465441SEvalZero# 6*10465441SEvalZero# This program is free software; you can redistribute it and/or modify 7*10465441SEvalZero# it under the terms of the GNU General Public License as published by 8*10465441SEvalZero# the Free Software Foundation; either version 2 of the License, or 9*10465441SEvalZero# (at your option) any later version. 10*10465441SEvalZero# 11*10465441SEvalZero# This program is distributed in the hope that it will be useful, 12*10465441SEvalZero# but WITHOUT ANY WARRANTY; without even the implied warranty of 13*10465441SEvalZero# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*10465441SEvalZero# GNU General Public License for more details. 15*10465441SEvalZero# 16*10465441SEvalZero# You should have received a copy of the GNU General Public License along 17*10465441SEvalZero# with this program; if not, write to the Free Software Foundation, Inc., 18*10465441SEvalZero# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19*10465441SEvalZero# 20*10465441SEvalZero# Change Logs: 21*10465441SEvalZero# Date Author Notes 22*10465441SEvalZero# 2015-01-20 Bernard Add copyright information 23*10465441SEvalZero# 24*10465441SEvalZero 25*10465441SEvalZeroimport os 26*10465441SEvalZeroimport threading 27*10465441SEvalZeroimport Queue 28*10465441SEvalZero 29*10465441SEvalZero# Windows import 30*10465441SEvalZeroimport win32file 31*10465441SEvalZeroimport win32pipe 32*10465441SEvalZeroimport win32api 33*10465441SEvalZeroimport win32con 34*10465441SEvalZeroimport win32security 35*10465441SEvalZeroimport win32process 36*10465441SEvalZeroimport win32event 37*10465441SEvalZero 38*10465441SEvalZeroclass Win32Spawn(object): 39*10465441SEvalZero def __init__(self, cmd, shell=False): 40*10465441SEvalZero self.queue = Queue.Queue() 41*10465441SEvalZero self.is_terminated = False 42*10465441SEvalZero self.wake_up_event = win32event.CreateEvent(None, 0, 0, None) 43*10465441SEvalZero 44*10465441SEvalZero exec_dir = os.getcwd() 45*10465441SEvalZero comspec = os.environ.get("COMSPEC", "cmd.exe") 46*10465441SEvalZero cmd = comspec + ' /c ' + cmd 47*10465441SEvalZero 48*10465441SEvalZero win32event.ResetEvent(self.wake_up_event) 49*10465441SEvalZero 50*10465441SEvalZero currproc = win32api.GetCurrentProcess() 51*10465441SEvalZero 52*10465441SEvalZero sa = win32security.SECURITY_ATTRIBUTES() 53*10465441SEvalZero sa.bInheritHandle = 1 54*10465441SEvalZero 55*10465441SEvalZero child_stdout_rd, child_stdout_wr = win32pipe.CreatePipe(sa, 0) 56*10465441SEvalZero child_stdout_rd_dup = win32api.DuplicateHandle(currproc, child_stdout_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS) 57*10465441SEvalZero win32file.CloseHandle(child_stdout_rd) 58*10465441SEvalZero 59*10465441SEvalZero child_stderr_rd, child_stderr_wr = win32pipe.CreatePipe(sa, 0) 60*10465441SEvalZero child_stderr_rd_dup = win32api.DuplicateHandle(currproc, child_stderr_rd, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS) 61*10465441SEvalZero win32file.CloseHandle(child_stderr_rd) 62*10465441SEvalZero 63*10465441SEvalZero child_stdin_rd, child_stdin_wr = win32pipe.CreatePipe(sa, 0) 64*10465441SEvalZero child_stdin_wr_dup = win32api.DuplicateHandle(currproc, child_stdin_wr, currproc, 0, 0, win32con.DUPLICATE_SAME_ACCESS) 65*10465441SEvalZero win32file.CloseHandle(child_stdin_wr) 66*10465441SEvalZero 67*10465441SEvalZero startup_info = win32process.STARTUPINFO() 68*10465441SEvalZero startup_info.hStdInput = child_stdin_rd 69*10465441SEvalZero startup_info.hStdOutput = child_stdout_wr 70*10465441SEvalZero startup_info.hStdError = child_stderr_wr 71*10465441SEvalZero startup_info.dwFlags = win32process.STARTF_USESTDHANDLES 72*10465441SEvalZero 73*10465441SEvalZero cr_flags = 0 74*10465441SEvalZero cr_flags = win32process.CREATE_NEW_PROCESS_GROUP 75*10465441SEvalZero 76*10465441SEvalZero env = os.environ.copy() 77*10465441SEvalZero self.h_process, h_thread, dw_pid, dw_tid = win32process.CreateProcess(None, cmd, None, None, 1, 78*10465441SEvalZero cr_flags, env, os.path.abspath(exec_dir), 79*10465441SEvalZero startup_info) 80*10465441SEvalZero 81*10465441SEvalZero win32api.CloseHandle(h_thread) 82*10465441SEvalZero 83*10465441SEvalZero win32file.CloseHandle(child_stdin_rd) 84*10465441SEvalZero win32file.CloseHandle(child_stdout_wr) 85*10465441SEvalZero win32file.CloseHandle(child_stderr_wr) 86*10465441SEvalZero 87*10465441SEvalZero self.__child_stdout = child_stdout_rd_dup 88*10465441SEvalZero self.__child_stderr = child_stderr_rd_dup 89*10465441SEvalZero self.__child_stdin = child_stdin_wr_dup 90*10465441SEvalZero 91*10465441SEvalZero self.exit_code = -1 92*10465441SEvalZero 93*10465441SEvalZero def close(self): 94*10465441SEvalZero win32file.CloseHandle(self.__child_stdout) 95*10465441SEvalZero win32file.CloseHandle(self.__child_stderr) 96*10465441SEvalZero win32file.CloseHandle(self.__child_stdin) 97*10465441SEvalZero win32api.CloseHandle(self.h_process) 98*10465441SEvalZero win32api.CloseHandle(self.wake_up_event) 99*10465441SEvalZero 100*10465441SEvalZero def kill_subprocess(): 101*10465441SEvalZero win32event.SetEvent(self.wake_up_event) 102*10465441SEvalZero 103*10465441SEvalZero def sleep(secs): 104*10465441SEvalZero win32event.ResetEvent(self.wake_up_event) 105*10465441SEvalZero timeout = int(1000 * secs) 106*10465441SEvalZero val = win32event.WaitForSingleObject(self.wake_up_event, timeout) 107*10465441SEvalZero if val == win32event.WAIT_TIMEOUT: 108*10465441SEvalZero return True 109*10465441SEvalZero else: 110*10465441SEvalZero # The wake_up_event must have been signalled 111*10465441SEvalZero return False 112*10465441SEvalZero 113*10465441SEvalZero def get(self, block=True, timeout=None): 114*10465441SEvalZero return self.queue.get(block=block, timeout=timeout) 115*10465441SEvalZero 116*10465441SEvalZero def qsize(self): 117*10465441SEvalZero return self.queue.qsize() 118*10465441SEvalZero 119*10465441SEvalZero def __wait_for_child(self): 120*10465441SEvalZero # kick off threads to read from stdout and stderr of the child process 121*10465441SEvalZero threading.Thread(target=self.__do_read, args=(self.__child_stdout, )).start() 122*10465441SEvalZero threading.Thread(target=self.__do_read, args=(self.__child_stderr, )).start() 123*10465441SEvalZero 124*10465441SEvalZero while True: 125*10465441SEvalZero # block waiting for the process to finish or the interrupt to happen 126*10465441SEvalZero handles = (self.wake_up_event, self.h_process) 127*10465441SEvalZero val = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE) 128*10465441SEvalZero 129*10465441SEvalZero if val >= win32event.WAIT_OBJECT_0 and val < win32event.WAIT_OBJECT_0 + len(handles): 130*10465441SEvalZero handle = handles[val - win32event.WAIT_OBJECT_0] 131*10465441SEvalZero if handle == self.wake_up_event: 132*10465441SEvalZero win32api.TerminateProcess(self.h_process, 1) 133*10465441SEvalZero win32event.ResetEvent(self.wake_up_event) 134*10465441SEvalZero return False 135*10465441SEvalZero elif handle == self.h_process: 136*10465441SEvalZero # the process has ended naturally 137*10465441SEvalZero return True 138*10465441SEvalZero else: 139*10465441SEvalZero assert False, "Unknown handle fired" 140*10465441SEvalZero else: 141*10465441SEvalZero assert False, "Unexpected return from WaitForMultipleObjects" 142*10465441SEvalZero 143*10465441SEvalZero # Wait for job to finish. Since this method blocks, it can to be called from another thread. 144*10465441SEvalZero # If the application wants to kill the process, it should call kill_subprocess(). 145*10465441SEvalZero def wait(self): 146*10465441SEvalZero if not self.__wait_for_child(): 147*10465441SEvalZero # it's been killed 148*10465441SEvalZero result = False 149*10465441SEvalZero else: 150*10465441SEvalZero # normal termination 151*10465441SEvalZero self.exit_code = win32process.GetExitCodeProcess(self.h_process) 152*10465441SEvalZero result = self.exit_code == 0 153*10465441SEvalZero self.close() 154*10465441SEvalZero self.is_terminated = True 155*10465441SEvalZero 156*10465441SEvalZero return result 157*10465441SEvalZero 158*10465441SEvalZero # This method gets called on a worker thread to read from either a stderr 159*10465441SEvalZero # or stdout thread from the child process. 160*10465441SEvalZero def __do_read(self, handle): 161*10465441SEvalZero bytesToRead = 1024 162*10465441SEvalZero while 1: 163*10465441SEvalZero try: 164*10465441SEvalZero finished = 0 165*10465441SEvalZero hr, data = win32file.ReadFile(handle, bytesToRead, None) 166*10465441SEvalZero if data: 167*10465441SEvalZero self.queue.put_nowait(data) 168*10465441SEvalZero except win32api.error: 169*10465441SEvalZero finished = 1 170*10465441SEvalZero 171*10465441SEvalZero if finished: 172*10465441SEvalZero return 173*10465441SEvalZero 174*10465441SEvalZero def start_pipe(self): 175*10465441SEvalZero def worker(pipe): 176*10465441SEvalZero return pipe.wait() 177*10465441SEvalZero 178*10465441SEvalZero thrd = threading.Thread(target=worker, args=(self, )) 179*10465441SEvalZero thrd.start() 180