xref: /nrf52832-nimble/rt-thread/tools/win32spawn.py (revision 104654410c56c573564690304ae786df310c91fc)
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