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