1 diff -urN buildbot-slave-0.8.5.orig/buildslave/runprocess.py buildbot-slave-0.8.5/buildslave/runprocess.py
2 --- buildbot-slave-0.8.5.orig/buildslave/runprocess.py 2011-09-03 23:59:10 +0400
3 +++ buildbot-slave-0.8.5/buildslave/runprocess.py 2012-11-02 03:08:05 +0400
10 from collections import deque
13 if runtime.platformType == 'posix':
14 from twisted.internet.process import Process
20 +def safe_terminate_process (proc, code):
22 + cp = win32api.GetCurrentProcess ()
24 + dupproc = win32api.DuplicateHandle (cp, proc._handle, cp, 2 | 1024 | 8 | 32 | 16, 0, 0)
26 + exitcode = win32process.GetExitCodeProcess (dupproc)
27 + if exitcode == 0x103:
28 + kernel32 = win32api.GetModuleHandle ("kernel32")
29 + exitprocess = win32api.GetProcAddress (kernel32, "ExitProcess")
31 + th, tid = win32process.CreateRemoteThread (dupproc, None, 0, exitprocess, code, 0)
32 + win32api.CloseHandle (th)
38 + # except failed to get exit code? failed to get module handle?
40 + win32api.CloseHandle (dupproc)
46 + def SetHandle (self, h):
49 +def safe_terminate_process_by_pid (proc, code):
52 + openproc = win32process.OpenProcess (2 | 1024 | 8 | 32 | 16, 0, proc)
55 + d.SetHandle (openproc)
56 + safe_termiate_process (d, code)
58 + win32api.CloseHandle (openproc)
62 + return os.kill (proc, code)
64 def shell_quote(cmd_list):
65 # attempt to quote cmd_list such that a shell will properly re-interpret
66 # it. The pipes module is only available on UNIX, and Windows "shell"
68 self.pending_stdin = ""
69 self.stdin_finished = False
71 + self.scriptfile = ""
73 def setStdin(self, data):
74 assert not self.connected
81 + os.remove (self.scriptfile)
84 self.command.finished(sig, rc)
89 self.pp = RunProcessPP(self)
91 - if type(self.command) in types.StringTypes:
92 - if runtime.platformType == 'win32':
93 - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
94 - if '/c' not in argv: argv += ['/c']
95 - argv += [self.command]
97 - # for posix, use /bin/sh. for other non-posix, well, doesn't
99 - argv = ['/bin/sh', '-c', self.command]
100 - display = self.fake_command
102 - # On windows, CreateProcess requires an absolute path to the executable.
103 - # When we call spawnProcess below, we pass argv[0] as the executable.
104 - # So, for .exe's that we have absolute paths to, we can call directly
105 - # Otherwise, we should run under COMSPEC (usually cmd.exe) to
106 - # handle path searching, etc.
107 - if runtime.platformType == 'win32' and not \
108 - (self.command[0].lower().endswith(".exe") and os.path.isabs(self.command[0])):
109 - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
110 - if '/c' not in argv: argv += ['/c']
111 - argv += list(self.command)
113 - argv = self.command
114 - # Attempt to format this for use by a shell, although the process isn't perfect
115 + if type(self.command) in types.StringTypes:
\r
116 + if runtime.platformType == 'win32':
\r
117 + if os.environ['BUILDSLAVE_SHELL']:
\r
118 + argv = os.environ['BUILDSLAVE_SHELL'].split() # allow %COMSPEC% to have args
\r
119 + argv += [self.command]
\r
121 + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
\r
122 + if '/c' not in argv:
\r
124 + argv += [self.command]
\r
126 + # for posix, use /bin/sh. for other non-posix, well, doesn't
\r
128 + argv = ['/bin/sh', '-c', self.command]
\r
129 + display = self.fake_command
\r
131 + # On windows, CreateProcess requires an absolute path to the executable.
\r
132 + # When we call spawnProcess below, we pass argv[0] as the executable.
\r
133 + # So, for .exe's that we have absolute paths to, we can call directly
\r
134 + # Otherwise, we should run under COMSPEC (usually cmd.exe) to
\r
135 + # handle path searching, etc.
\r
136 + if runtime.platformType == 'win32' and not \
\r
137 + (self.command[0].lower().endswith(".exe") and os.path.isabs(self.command[0])):
\r
138 + if os.environ['BUILDSLAVE_SHELL']:
\r
139 + argv = os.environ['BUILDSLAVE_SHELL'].split()
\r
140 + # Create a temporary script file that changes current directory
\r
141 + # and runs the command we want
\r
142 + # It will be deleted after command is finished running (see RunProcessPP)
\r
143 + tf, tf_name = tempfile.mkstemp ()
\r
144 + f = os.fdopen (tf, 'wb')
\r
145 + fcontents = '#!/bin/sh\ncd {}\n{}'.format (
\r
146 + re.sub(r'(?<!\\) ','\\ ', self.workdir.replace('\\','/')),
\r
147 + ' '.join (self.command))
\r
148 + f.write (fcontents)
\r
149 + log.msg("Script: {}".format (fcontents))
\r
151 + self.pp.scriptfile = tf_name
\r
152 + argv += [tf_name.replace('\\','/')]
\r
154 + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
\r
155 + if '/c' not in argv:
\r
157 + argv += list(self.command)
\r
159 + argv = self.command
\r
160 + # Attempt to format this for use by a shell, although the process isn't perfect
\r
161 display = shell_quote(self.fake_command)
163 # $PWD usually indicates the current directory; spawnProcess may not
165 self.environ['PWD'] = os.path.abspath(self.workdir)
167 # self.stdin is handled in RunProcessPP.connectionMade
169 + log.msg("Running {}".format (argv))
170 log.msg(" " + display)
171 self._addToBuffers('header', display+"\n")
174 if self.KILL == None:
175 log.msg("self.KILL==None, only pretending to kill child")
177 - log.msg("using TASKKILL /F PID /T to kill pid %s" % self.process.pid)
178 - subprocess.check_call("TASKKILL /F /PID %s /T" % self.process.pid)
179 - log.msg("taskkill'd pid %s" % self.process.pid)
180 + safe_terminate_process_by_pid (self.process.pid, 1)
183 # try signalling the process itself (works on Windows too, sorta)