1 diff -urN /src/buildbot-slave-0.8.6p1.orig/buildslave/runprocess.py /src/buildbot-slave-0.8.6p1/buildslave/runprocess.py
2 --- buildbot-slave-0.8.6p1.orig/buildslave/runprocess.py 2012-03-26 04:09:10 +0400
3 +++ buildbot-slave-0.8.6p1/buildslave/runprocess.py 2013-03-31 05:18:55 +0400
10 from collections import deque
13 if runtime.platformType == 'posix':
14 from twisted.internet.process import Process
22 +def safe_terminate_process (proc, code):
24 + log.msg ("Obtaining current process handle")
25 + cp = win32api.GetCurrentProcess ()
27 + log.msg ("Expanding target process handle permissions")
28 + dupproc = win32api.DuplicateHandle (cp, proc._handle, cp, 2 | 1024 | 8 | 32 | 16 | 0x100000, 0, 0)
29 + log.msg ("Expanded.")
31 + log.msg ("Checking exit code of target process")
32 + exitcode = win32process.GetExitCodeProcess (dupproc)
33 + log.msg ("Exit code is %d" % exitcode)
34 + if exitcode == 0x103:
35 + log.msg ("Opening kernel32.dll")
36 + kernel32 = win32api.GetModuleHandle ("kernel32")
37 + log.msg ("Getting ExitProcess() address")
38 + exitprocess = win32api.GetProcAddress (kernel32, "ExitProcess")
40 + log.msg ("Creating remote thread")
44 + th, tid = win32process.CreateRemoteThread (dupproc, None, 0, exitprocess, code, 0)
45 + log.msg ("Created remote thread %d" % tid)
46 + except pywintypes.error as e:
48 + log.msg ("Access denied. It still might die, so don't fail yet")
51 + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
53 + except Exception as e:
54 + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
57 + log.msg ("Wait for 5 seconds or until it dies (usually takes around 1 microsecond)")
58 + waitresult = win32event.WaitForSingleObject (dupproc, 5)
59 + log.msg ("Result of waiting: %d" % waitresult)
60 + win32api.CloseHandle (th)
66 + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
68 + win32api.CloseHandle (dupproc)
74 + def SetHandle (self, h):
77 +def safe_terminate_process_by_pid (proc, code):
80 + log.msg("Opening process %d" % proc)
81 + openproc = win32api.OpenProcess (2 | 1024 | 8 | 32 | 16 | 0x100000, 0, proc)
82 + log.msg("Opened process %d" % proc)
85 + d.SetHandle (openproc)
86 + log.msg("Terminating it safely")
87 + safe_terminate_process (d, code)
88 + log.msg("Finished terminating")
90 + log.msg("Closing process handle")
91 + win32api.CloseHandle (openproc)
93 + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
96 + return os.kill (proc, code)
99 def shell_quote(cmd_list):
100 # attempt to quote cmd_list such that a shell will properly re-interpret
101 # it. The pipes module is only available on UNIX, and Windows "shell"
103 self.pending_stdin = ""
104 self.stdin_finished = False
106 + self.scriptfile = ""
108 def setStdin(self, data):
109 assert not self.connected
114 + if self.scriptfile:
116 + os.remove (self.scriptfile)
119 self.command.finished(sig, rc)
124 if type(self.command) in types.StringTypes:
125 if runtime.platformType == 'win32':
126 - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
127 - if '/c' not in argv: argv += ['/c']
128 - argv += [self.command]
129 + if os.environ['BUILDSLAVE_SHELL']:
130 + argv = os.environ['BUILDSLAVE_SHELL'].split() # allow %COMSPEC% to have args
131 + argv += [self.command]
133 + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
134 + if '/c' not in argv:
136 + argv += [self.command]
138 # for posix, use /bin/sh. for other non-posix, well, doesn't
141 # handle path searching, etc.
142 if runtime.platformType == 'win32' and not \
143 (self.command[0].lower().endswith(".exe") and os.path.isabs(self.command[0])):
144 - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
145 - if '/c' not in argv: argv += ['/c']
146 - argv += list(self.command)
147 + if os.environ['BUILDSLAVE_SHELL']:
148 + argv = os.environ['BUILDSLAVE_SHELL'].split()
149 + # Create a temporary script file that changes current directory
150 + # and runs the command we want
151 + # It will be deleted after command is finished running (see RunProcessPP)
152 + tf, tf_name = tempfile.mkstemp ()
153 + f = os.fdopen (tf, 'wb')
154 + fcontents = '#!/bin/sh\ncd {}\n{}'.format (
155 + re.sub(r'(?<!\\) ','\\ ', self.workdir.replace('\\','/')),
156 + ' '.join (self.command))
157 + f.write (fcontents)
158 + log.msg("Script: {}".format (fcontents))
160 + self.pp.scriptfile = tf_name
161 + argv += [tf_name.replace('\\','/')]
163 + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
164 + if '/c' not in argv:
166 + argv += list(self.command)
169 # Attempt to format this for use by a shell, although the process isn't perfect
171 self.environ['PWD'] = os.path.abspath(self.workdir)
173 # self.stdin is handled in RunProcessPP.connectionMade
175 + log.msg("Running {}".format (argv))
176 log.msg(" " + display)
177 self._addToBuffers('header', display+"\n")
180 if self.interruptSignal == None:
181 log.msg("self.interruptSignal==None, only pretending to kill child")
183 - log.msg("using TASKKILL /F PID /T to kill pid %s" % self.process.pid)
184 - subprocess.check_call("TASKKILL /F /PID %s /T" % self.process.pid)
185 - log.msg("taskkill'd pid %s" % self.process.pid)
186 + safe_terminate_process_by_pid (self.process.pid, 1)
189 # try signalling the process itself (works on Windows too, sorta)
190 @@ -795,10 +905,11 @@
192 log.msg("signalProcess/os.kill failed both times")
194 - if runtime.platformType == "posix":
195 + if runtime.platformType == "posix" or runtime.platformType == "win32":
196 # we only do this under posix because the win32eventreactor
197 # blocks here until the process has terminated, while closing
198 # stderr. This is weird.
199 + # LRN: Turns out, things don't work without this on W32. At all.
200 self.pp.transport.loseConnection()