buildslave-0.8.6p1-gnunet-w32.patch 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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
  4. @@ -24,6 +24,7 @@
  5. import re
  6. import subprocess
  7. import traceback
  8. +import tempfile
  9. import stat
  10. from collections import deque
  11. @@ -36,6 +37,89 @@
  12. if runtime.platformType == 'posix':
  13. from twisted.internet.process import Process
  14. +if os.name == 'nt':
  15. + import win32api
  16. + import win32process
  17. + import win32event
  18. + import pywintypes
  19. +
  20. +def safe_terminate_process (proc, code):
  21. + if os.name == 'nt':
  22. + log.msg ("Obtaining current process handle")
  23. + cp = win32api.GetCurrentProcess ()
  24. + result = False
  25. + log.msg ("Expanding target process handle permissions")
  26. + dupproc = win32api.DuplicateHandle (cp, proc._handle, cp, 2 | 1024 | 8 | 32 | 16 | 0x100000, 0, 0)
  27. + log.msg ("Expanded.")
  28. + try:
  29. + log.msg ("Checking exit code of target process")
  30. + exitcode = win32process.GetExitCodeProcess (dupproc)
  31. + log.msg ("Exit code is %d" % exitcode)
  32. + if exitcode == 0x103:
  33. + log.msg ("Opening kernel32.dll")
  34. + kernel32 = win32api.GetModuleHandle ("kernel32")
  35. + log.msg ("Getting ExitProcess() address")
  36. + exitprocess = win32api.GetProcAddress (kernel32, "ExitProcess")
  37. + try:
  38. + log.msg ("Creating remote thread")
  39. + th = 0
  40. + tid = 0
  41. + failed = False
  42. + th, tid = win32process.CreateRemoteThread (dupproc, None, 0, exitprocess, code, 0)
  43. + log.msg ("Created remote thread %d" % tid)
  44. + except pywintypes.error as e:
  45. + if e[0] == 5:
  46. + log.msg ("Access denied. It still might die, so don't fail yet")
  47. + pass
  48. + else:
  49. + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
  50. + failed = True
  51. + except Exception as e:
  52. + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
  53. + failed = True
  54. + if not failed:
  55. + log.msg ("Wait for 5 seconds or until it dies (usually takes around 1 microsecond)")
  56. + waitresult = win32event.WaitForSingleObject (dupproc, 5)
  57. + log.msg ("Result of waiting: %d" % waitresult)
  58. + win32api.CloseHandle (th)
  59. + if waitresult == 0:
  60. + result = True
  61. + else:
  62. + result = True
  63. + except:
  64. + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
  65. + finally:
  66. + win32api.CloseHandle (dupproc)
  67. + return result
  68. + else:
  69. + return proc.kill ()
  70. +
  71. +class Dummy(object):
  72. + def SetHandle (self, h):
  73. + self._handle = h
  74. +
  75. +def safe_terminate_process_by_pid (proc, code):
  76. + if os.name == 'nt':
  77. + try:
  78. + log.msg("Opening process %d" % proc)
  79. + openproc = win32api.OpenProcess (2 | 1024 | 8 | 32 | 16 | 0x100000, 0, proc)
  80. + log.msg("Opened process %d" % proc)
  81. + try:
  82. + d = Dummy ()
  83. + d.SetHandle (openproc)
  84. + log.msg("Terminating it safely")
  85. + safe_terminate_process (d, code)
  86. + log.msg("Finished terminating")
  87. + finally:
  88. + log.msg("Closing process handle")
  89. + win32api.CloseHandle (openproc)
  90. + except:
  91. + log.msg("exception %s - %s" % (sys.exc_info()[0], sys.exc_info()[1]))
  92. + pass
  93. + else:
  94. + return os.kill (proc, code)
  95. +
  96. +
  97. def shell_quote(cmd_list):
  98. # attempt to quote cmd_list such that a shell will properly re-interpret
  99. # it. The pipes module is only available on UNIX, and Windows "shell"
  100. @@ -148,6 +232,7 @@
  101. self.pending_stdin = ""
  102. self.stdin_finished = False
  103. self.killed = False
  104. + self.scriptfile = ""
  105. def setStdin(self, data):
  106. assert not self.connected
  107. @@ -198,6 +283,11 @@
  108. rc = 1
  109. else:
  110. rc = -1
  111. + if self.scriptfile:
  112. + try:
  113. + os.remove (self.scriptfile)
  114. + except:
  115. + pass
  116. self.command.finished(sig, rc)
  117. @@ -408,9 +498,14 @@
  118. if type(self.command) in types.StringTypes:
  119. if runtime.platformType == 'win32':
  120. - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
  121. - if '/c' not in argv: argv += ['/c']
  122. - argv += [self.command]
  123. + if os.environ['BUILDSLAVE_SHELL']:
  124. + argv = os.environ['BUILDSLAVE_SHELL'].split() # allow %COMSPEC% to have args
  125. + argv += [self.command]
  126. + else:
  127. + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
  128. + if '/c' not in argv:
  129. + argv += ['/c']
  130. + argv += [self.command]
  131. else:
  132. # for posix, use /bin/sh. for other non-posix, well, doesn't
  133. # hurt to try
  134. @@ -424,9 +519,26 @@
  135. # handle path searching, etc.
  136. if runtime.platformType == 'win32' and not \
  137. (self.command[0].lower().endswith(".exe") and os.path.isabs(self.command[0])):
  138. - argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
  139. - if '/c' not in argv: argv += ['/c']
  140. - argv += list(self.command)
  141. + if os.environ['BUILDSLAVE_SHELL']:
  142. + argv = os.environ['BUILDSLAVE_SHELL'].split()
  143. + # Create a temporary script file that changes current directory
  144. + # and runs the command we want
  145. + # It will be deleted after command is finished running (see RunProcessPP)
  146. + tf, tf_name = tempfile.mkstemp ()
  147. + f = os.fdopen (tf, 'wb')
  148. + fcontents = '#!/bin/sh\ncd {}\n{}'.format (
  149. + re.sub(r'(?<!\\) ','\\ ', self.workdir.replace('\\','/')),
  150. + ' '.join (self.command))
  151. + f.write (fcontents)
  152. + log.msg("Script: {}".format (fcontents))
  153. + f.close ()
  154. + self.pp.scriptfile = tf_name
  155. + argv += [tf_name.replace('\\','/')]
  156. + else:
  157. + argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
  158. + if '/c' not in argv:
  159. + argv += ['/c']
  160. + argv += list(self.command)
  161. else:
  162. argv = self.command
  163. # Attempt to format this for use by a shell, although the process isn't perfect
  164. @@ -439,7 +551,7 @@
  165. self.environ['PWD'] = os.path.abspath(self.workdir)
  166. # self.stdin is handled in RunProcessPP.connectionMade
  167. -
  168. + log.msg("Running {}".format (argv))
  169. log.msg(" " + display)
  170. self._addToBuffers('header', display+"\n")
  171. @@ -770,9 +882,7 @@
  172. if self.interruptSignal == None:
  173. log.msg("self.interruptSignal==None, only pretending to kill child")
  174. else:
  175. - log.msg("using TASKKILL /F PID /T to kill pid %s" % self.process.pid)
  176. - subprocess.check_call("TASKKILL /F /PID %s /T" % self.process.pid)
  177. - log.msg("taskkill'd pid %s" % self.process.pid)
  178. + safe_terminate_process_by_pid (self.process.pid, 1)
  179. hit = 1
  180. # try signalling the process itself (works on Windows too, sorta)
  181. @@ -795,10 +905,11 @@
  182. if not hit:
  183. log.msg("signalProcess/os.kill failed both times")
  184. - if runtime.platformType == "posix":
  185. + if runtime.platformType == "posix" or runtime.platformType == "win32":
  186. # we only do this under posix because the win32eventreactor
  187. # blocks here until the process has terminated, while closing
  188. # stderr. This is weird.
  189. + # LRN: Turns out, things don't work without this on W32. At all.
  190. self.pp.transport.loseConnection()
  191. if self.deferred: