|
@@ -1,202 +0,0 @@
|
|
|
-diff -urN /src/buildbot-slave-0.8.6p1.orig/buildslave/runprocess.py /src/buildbot-slave-0.8.6p1/buildslave/runprocess.py
|
|
|
---- buildbot-slave-0.8.6p1.orig/buildslave/runprocess.py 2012-03-26 04:09:10 +0400
|
|
|
-+++ buildbot-slave-0.8.6p1/buildslave/runprocess.py 2013-03-31 05:18:55 +0400
|
|
|
-@@ -24,6 +24,7 @@
|
|
|
- import re
|
|
|
- import subprocess
|
|
|
- import traceback
|
|
|
-+import tempfile
|
|
|
- import stat
|
|
|
- from collections import deque
|
|
|
-
|
|
|
-@@ -36,6 +37,89 @@
|
|
|
- if runtime.platformType == 'posix':
|
|
|
- from twisted.internet.process import Process
|
|
|
-
|
|
|
-+if os.name == 'nt':
|
|
|
-+ import win32api
|
|
|
-+ import win32process
|
|
|
-+ import win32event
|
|
|
-+ import pywintypes
|
|
|
-+
|
|
|
-+def safe_terminate_process (proc, code):
|
|
|
-+ if os.name == 'nt':
|
|
|
-+ log.msg ("Obtaining current process handle")
|
|
|
-+ cp = win32api.GetCurrentProcess ()
|
|
|
-+ result = False
|
|
|
-+ log.msg ("Expanding target process handle permissions")
|
|
|
-+ dupproc = win32api.DuplicateHandle (cp, proc._handle, cp, 2 | 1024 | 8 | 32 | 16 | 0x100000, 0, 0)
|
|
|
-+ log.msg ("Expanded.")
|
|
|
-+ try:
|
|
|
-+ log.msg ("Checking exit code of target process")
|
|
|
-+ exitcode = win32process.GetExitCodeProcess (dupproc)
|
|
|
-+ log.msg ("Exit code is %d" % exitcode)
|
|
|
-+ if exitcode == 0x103:
|
|
|
-+ log.msg ("Opening kernel32.dll")
|
|
|
-+ kernel32 = win32api.GetModuleHandle ("kernel32")
|
|
|
-+ log.msg ("Getting ExitProcess() address")
|
|
|
-+ exitprocess = win32api.GetProcAddress (kernel32, "ExitProcess")
|
|
|
-+ try:
|
|
|
-+ log.msg ("Creating remote thread")
|
|
|
-+ th = 0
|
|
|
-+ tid = 0
|
|
|
-+ failed = False
|
|
|
-+ th, tid = win32process.CreateRemoteThread (dupproc, None, 0, exitprocess, code, 0)
|
|
|
-+ log.msg ("Created remote thread %d" % tid)
|
|
|
-+ except pywintypes.error as e:
|
|
|
-+ if e[0] == 5:
|
|
|
-+ log.msg ("Access denied. It still might die, so don't fail yet")
|
|
|
-+ pass
|
|
|
-+ else:
|
|
|
-+ log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
|
|
|
-+ failed = True
|
|
|
-+ except Exception as e:
|
|
|
-+ log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
|
|
|
-+ failed = True
|
|
|
-+ if not failed:
|
|
|
-+ log.msg ("Wait for 5 seconds or until it dies (usually takes around 1 microsecond)")
|
|
|
-+ waitresult = win32event.WaitForSingleObject (dupproc, 5)
|
|
|
-+ log.msg ("Result of waiting: %d" % waitresult)
|
|
|
-+ win32api.CloseHandle (th)
|
|
|
-+ if waitresult == 0:
|
|
|
-+ result = True
|
|
|
-+ else:
|
|
|
-+ result = True
|
|
|
-+ except:
|
|
|
-+ log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
|
|
|
-+ finally:
|
|
|
-+ win32api.CloseHandle (dupproc)
|
|
|
-+ return result
|
|
|
-+ else:
|
|
|
-+ return proc.kill ()
|
|
|
-+
|
|
|
-+class Dummy(object):
|
|
|
-+ def SetHandle (self, h):
|
|
|
-+ self._handle = h
|
|
|
-+
|
|
|
-+def safe_terminate_process_by_pid (proc, code):
|
|
|
-+ if os.name == 'nt':
|
|
|
-+ try:
|
|
|
-+ log.msg("Opening process %d" % proc)
|
|
|
-+ openproc = win32api.OpenProcess (2 | 1024 | 8 | 32 | 16 | 0x100000, 0, proc)
|
|
|
-+ log.msg("Opened process %d" % proc)
|
|
|
-+ try:
|
|
|
-+ d = Dummy ()
|
|
|
-+ d.SetHandle (openproc)
|
|
|
-+ log.msg("Terminating it safely")
|
|
|
-+ safe_terminate_process (d, code)
|
|
|
-+ log.msg("Finished terminating")
|
|
|
-+ finally:
|
|
|
-+ log.msg("Closing process handle")
|
|
|
-+ win32api.CloseHandle (openproc)
|
|
|
-+ except:
|
|
|
-+ log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
|
|
|
-+ pass
|
|
|
-+ else:
|
|
|
-+ return os.kill (proc, code)
|
|
|
-+
|
|
|
-+
|
|
|
- def shell_quote(cmd_list):
|
|
|
- # attempt to quote cmd_list such that a shell will properly re-interpret
|
|
|
- # it. The pipes module is only available on UNIX, and Windows "shell"
|
|
|
-@@ -148,6 +232,7 @@
|
|
|
- self.pending_stdin = ""
|
|
|
- self.stdin_finished = False
|
|
|
- self.killed = False
|
|
|
-+ self.scriptfile = ""
|
|
|
-
|
|
|
- def setStdin(self, data):
|
|
|
- assert not self.connected
|
|
|
-@@ -198,6 +283,11 @@
|
|
|
- rc = 1
|
|
|
- else:
|
|
|
- rc = -1
|
|
|
-+ if self.scriptfile:
|
|
|
-+ try:
|
|
|
-+ os.remove (self.scriptfile)
|
|
|
-+ except:
|
|
|
-+ pass
|
|
|
- self.command.finished(sig, rc)
|
|
|
-
|
|
|
-
|
|
|
-@@ -408,9 +498,14 @@
|
|
|
-
|
|
|
- if type(self.command) in types.StringTypes:
|
|
|
- if runtime.platformType == 'win32':
|
|
|
-- argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
|
|
|
-- if '/c' not in argv: argv += ['/c']
|
|
|
-- argv += [self.command]
|
|
|
-+ if os.environ['BUILDSLAVE_SHELL']:
|
|
|
-+ argv = os.environ['BUILDSLAVE_SHELL'].split() # allow %COMSPEC% to have args
|
|
|
-+ argv += [self.command]
|
|
|
-+ else:
|
|
|
-+ argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
|
|
|
-+ if '/c' not in argv:
|
|
|
-+ argv += ['/c']
|
|
|
-+ argv += [self.command]
|
|
|
- else:
|
|
|
- # for posix, use /bin/sh. for other non-posix, well, doesn't
|
|
|
- # hurt to try
|
|
|
-@@ -424,9 +519,26 @@
|
|
|
- # handle path searching, etc.
|
|
|
- if runtime.platformType == 'win32' and not \
|
|
|
- (self.command[0].lower().endswith(".exe") and os.path.isabs(self.command[0])):
|
|
|
-- argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
|
|
|
-- if '/c' not in argv: argv += ['/c']
|
|
|
-- argv += list(self.command)
|
|
|
-+ if os.environ['BUILDSLAVE_SHELL']:
|
|
|
-+ argv = os.environ['BUILDSLAVE_SHELL'].split()
|
|
|
-+ # Create a temporary script file that changes current directory
|
|
|
-+ # and runs the command we want
|
|
|
-+ # It will be deleted after command is finished running (see RunProcessPP)
|
|
|
-+ tf, tf_name = tempfile.mkstemp ()
|
|
|
-+ f = os.fdopen (tf, 'wb')
|
|
|
-+ fcontents = '#!/bin/sh\ncd {}\n{}'.format (
|
|
|
-+ re.sub(r'(?<!\\) ','\\ ', self.workdir.replace('\\','/')),
|
|
|
-+ ' '.join (self.command))
|
|
|
-+ f.write (fcontents)
|
|
|
-+ log.msg("Script: {}".format (fcontents))
|
|
|
-+ f.close ()
|
|
|
-+ self.pp.scriptfile = tf_name
|
|
|
-+ argv += [tf_name.replace('\\','/')]
|
|
|
-+ else:
|
|
|
-+ argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
|
|
|
-+ if '/c' not in argv:
|
|
|
-+ argv += ['/c']
|
|
|
-+ argv += list(self.command)
|
|
|
- else:
|
|
|
- argv = self.command
|
|
|
- # Attempt to format this for use by a shell, although the process isn't perfect
|
|
|
-@@ -439,7 +551,7 @@
|
|
|
- self.environ['PWD'] = os.path.abspath(self.workdir)
|
|
|
-
|
|
|
- # self.stdin is handled in RunProcessPP.connectionMade
|
|
|
--
|
|
|
-+ log.msg("Running {}".format (argv))
|
|
|
- log.msg(" " + display)
|
|
|
- self._addToBuffers('header', display+"\n")
|
|
|
-
|
|
|
-@@ -770,9 +882,7 @@
|
|
|
- if self.interruptSignal == None:
|
|
|
- log.msg("self.interruptSignal==None, only pretending to kill child")
|
|
|
- else:
|
|
|
-- log.msg("using TASKKILL /F PID /T to kill pid %s" % self.process.pid)
|
|
|
-- subprocess.check_call("TASKKILL /F /PID %s /T" % self.process.pid)
|
|
|
-- log.msg("taskkill'd pid %s" % self.process.pid)
|
|
|
-+ safe_terminate_process_by_pid (self.process.pid, 1)
|
|
|
- hit = 1
|
|
|
-
|
|
|
- # try signalling the process itself (works on Windows too, sorta)
|
|
|
-@@ -795,10 +905,11 @@
|
|
|
- if not hit:
|
|
|
- log.msg("signalProcess/os.kill failed both times")
|
|
|
-
|
|
|
-- if runtime.platformType == "posix":
|
|
|
-+ if runtime.platformType == "posix" or runtime.platformType == "win32":
|
|
|
- # we only do this under posix because the win32eventreactor
|
|
|
- # blocks here until the process has terminated, while closing
|
|
|
- # stderr. This is weird.
|
|
|
-+ # LRN: Turns out, things don't work without this on W32. At all.
|
|
|
- self.pp.transport.loseConnection()
|
|
|
-
|
|
|
- if self.deferred:
|