Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 # killableprocess - subprocesses which can be reliably killed
2 #
3 # Parts of this module are copied from the subprocess.py file contained
4 # in the Python distribution.
5 #
6 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
7 #
8 # Additions and modifications written by Benjamin Smedberg
9 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
10 # <http://www.mozilla.org/>
11 #
12 # More Modifications
13 # Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
14 # Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
15 #
16 # By obtaining, using, and/or copying this software and/or its
17 # associated documentation, you agree that you have read, understood,
18 # and will comply with the following terms and conditions:
19 #
20 # Permission to use, copy, modify, and distribute this software and
21 # its associated documentation for any purpose and without fee is
22 # hereby granted, provided that the above copyright notice appears in
23 # all copies, and that both that copyright notice and this permission
24 # notice appear in supporting documentation, and that the name of the
25 # author not be used in advertising or publicity pertaining to
26 # distribution of the software without specific, written prior
27 # permission.
28 #
29 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
30 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
31 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
32 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
34 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
35 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
37 """killableprocess - Subprocesses which can be reliably killed
39 This module is a subclass of the builtin "subprocess" module. It allows
40 processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
42 It also adds a timeout argument to Wait() for a limited period of time before
43 forcefully killing the process.
45 Note: On Windows, this module requires Windows 2000 or higher (no support for
46 Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
47 Python 2.5+ or available from http://python.net/crew/theller/ctypes/
48 """
50 import subprocess
51 import sys
52 import os
53 import time
54 import datetime
55 import types
56 import exceptions
58 try:
59 from subprocess import CalledProcessError
60 except ImportError:
61 # Python 2.4 doesn't implement CalledProcessError
62 class CalledProcessError(Exception):
63 """This exception is raised when a process run by check_call() returns
64 a non-zero exit status. The exit status will be stored in the
65 returncode attribute."""
66 def __init__(self, returncode, cmd):
67 self.returncode = returncode
68 self.cmd = cmd
69 def __str__(self):
70 return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
72 mswindows = (sys.platform == "win32")
74 if mswindows:
75 import winprocess
76 else:
77 import signal
79 # This is normally defined in win32con, but we don't want
80 # to incur the huge tree of dependencies (pywin32 and friends)
81 # just to get one constant. So here's our hack
82 STILL_ACTIVE = 259
84 def call(*args, **kwargs):
85 waitargs = {}
86 if "timeout" in kwargs:
87 waitargs["timeout"] = kwargs.pop("timeout")
89 return Popen(*args, **kwargs).wait(**waitargs)
91 def check_call(*args, **kwargs):
92 """Call a program with an optional timeout. If the program has a non-zero
93 exit status, raises a CalledProcessError."""
95 retcode = call(*args, **kwargs)
96 if retcode:
97 cmd = kwargs.get("args")
98 if cmd is None:
99 cmd = args[0]
100 raise CalledProcessError(retcode, cmd)
102 if not mswindows:
103 def DoNothing(*args):
104 pass
106 class Popen(subprocess.Popen):
107 kill_called = False
108 if mswindows:
109 def _execute_child(self, *args_tuple):
110 # workaround for bug 958609
111 if sys.hexversion < 0x02070600: # prior to 2.7.6
112 (args, executable, preexec_fn, close_fds,
113 cwd, env, universal_newlines, startupinfo,
114 creationflags, shell,
115 p2cread, p2cwrite,
116 c2pread, c2pwrite,
117 errread, errwrite) = args_tuple
118 to_close = set()
119 else: # 2.7.6 and later
120 (args, executable, preexec_fn, close_fds,
121 cwd, env, universal_newlines, startupinfo,
122 creationflags, shell, to_close,
123 p2cread, p2cwrite,
124 c2pread, c2pwrite,
125 errread, errwrite) = args_tuple
127 if not isinstance(args, types.StringTypes):
128 args = subprocess.list2cmdline(args)
130 # Always or in the create new process group
131 creationflags |= winprocess.CREATE_NEW_PROCESS_GROUP
133 if startupinfo is None:
134 startupinfo = winprocess.STARTUPINFO()
136 if None not in (p2cread, c2pwrite, errwrite):
137 startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
139 startupinfo.hStdInput = int(p2cread)
140 startupinfo.hStdOutput = int(c2pwrite)
141 startupinfo.hStdError = int(errwrite)
142 if shell:
143 startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
144 startupinfo.wShowWindow = winprocess.SW_HIDE
145 comspec = os.environ.get("COMSPEC", "cmd.exe")
146 args = comspec + " /c " + args
148 # determine if we can create create a job
149 canCreateJob = winprocess.CanCreateJobObject()
151 # set process creation flags
152 creationflags |= winprocess.CREATE_SUSPENDED
153 creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
154 if canCreateJob:
155 # Uncomment this line below to discover very useful things about your environment
156 #print "++++ killableprocess: releng twistd patch not applied, we can create job objects"
157 creationflags |= winprocess.CREATE_BREAKAWAY_FROM_JOB
159 # create the process
160 hp, ht, pid, tid = winprocess.CreateProcess(
161 executable, args,
162 None, None, # No special security
163 1, # Must inherit handles!
164 creationflags,
165 winprocess.EnvironmentBlock(env),
166 cwd, startupinfo)
167 self._child_created = True
168 self._handle = hp
169 self._thread = ht
170 self.pid = pid
171 self.tid = tid
173 if canCreateJob:
174 # We create a new job for this process, so that we can kill
175 # the process and any sub-processes
176 self._job = winprocess.CreateJobObject()
177 winprocess.AssignProcessToJobObject(self._job, int(hp))
178 else:
179 self._job = None
181 winprocess.ResumeThread(int(ht))
182 ht.Close()
184 if p2cread is not None:
185 p2cread.Close()
186 if c2pwrite is not None:
187 c2pwrite.Close()
188 if errwrite is not None:
189 errwrite.Close()
190 time.sleep(.1)
192 def kill(self, group=True):
193 """Kill the process. If group=True, all sub-processes will also be killed."""
194 self.kill_called = True
196 if mswindows:
197 if group and self._job:
198 winprocess.TerminateJobObject(self._job, 127)
199 else:
200 winprocess.TerminateProcess(self._handle, 127)
201 self.returncode = 127
202 else:
203 if group:
204 try:
205 os.killpg(self.pid, signal.SIGKILL)
206 except: pass
207 else:
208 os.kill(self.pid, signal.SIGKILL)
209 self.returncode = -9
211 def wait(self, timeout=None, group=True):
212 """Wait for the process to terminate. Returns returncode attribute.
213 If timeout seconds are reached and the process has not terminated,
214 it will be forcefully killed. If timeout is -1, wait will not
215 time out."""
216 if timeout is not None:
217 # timeout is now in milliseconds
218 timeout = timeout * 1000
220 starttime = datetime.datetime.now()
222 if mswindows:
223 if timeout is None:
224 timeout = -1
225 rc = winprocess.WaitForSingleObject(self._handle, timeout)
227 if (rc == winprocess.WAIT_OBJECT_0 or
228 rc == winprocess.WAIT_ABANDONED or
229 rc == winprocess.WAIT_FAILED):
230 # Object has either signaled, or the API call has failed. In
231 # both cases we want to give the OS the benefit of the doubt
232 # and supply a little time before we start shooting processes
233 # with an M-16.
235 # Returns 1 if running, 0 if not, -1 if timed out
236 def check():
237 now = datetime.datetime.now()
238 diff = now - starttime
239 if (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000):
240 if self._job:
241 if (winprocess.QueryInformationJobObject(self._job, 8)['BasicInfo']['ActiveProcesses'] > 0):
242 # Job Object is still containing active processes
243 return 1
244 else:
245 # No job, we use GetExitCodeProcess, which will tell us if the process is still active
246 self.returncode = winprocess.GetExitCodeProcess(self._handle)
247 if (self.returncode == STILL_ACTIVE):
248 # Process still active, continue waiting
249 return 1
250 # Process not active, return 0
251 return 0
252 else:
253 # Timed out, return -1
254 return -1
256 notdone = check()
257 while notdone == 1:
258 time.sleep(.5)
259 notdone = check()
261 if notdone == -1:
262 # Then check timed out, we have a hung process, attempt
263 # last ditch kill with explosives
264 self.kill(group)
266 else:
267 # In this case waitforsingleobject timed out. We have to
268 # take the process behind the woodshed and shoot it.
269 self.kill(group)
271 else:
272 if sys.platform in ('linux2', 'sunos5', 'solaris') \
273 or sys.platform.startswith('freebsd'):
274 def group_wait(timeout):
275 try:
276 os.waitpid(self.pid, 0)
277 except OSError, e:
278 pass # If wait has already been called on this pid, bad things happen
279 return self.returncode
280 elif sys.platform == 'darwin':
281 def group_wait(timeout):
282 try:
283 count = 0
284 if timeout is None and self.kill_called:
285 timeout = 10 # Have to set some kind of timeout or else this could go on forever
286 if timeout is None:
287 while 1:
288 os.killpg(self.pid, signal.SIG_DFL)
289 while ((count * 2) <= timeout):
290 os.killpg(self.pid, signal.SIG_DFL)
291 # count is increased by 500ms for every 0.5s of sleep
292 time.sleep(.5); count += 500
293 except exceptions.OSError:
294 return self.returncode
296 if timeout is None:
297 if group is True:
298 return group_wait(timeout)
299 else:
300 subprocess.Popen.wait(self)
301 return self.returncode
303 returncode = False
305 now = datetime.datetime.now()
306 diff = now - starttime
307 while (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000) and ( returncode is False ):
308 if group is True:
309 return group_wait(timeout)
310 else:
311 if subprocess.poll() is not None:
312 returncode = self.returncode
313 time.sleep(.5)
314 now = datetime.datetime.now()
315 diff = now - starttime
316 return self.returncode
318 return self.returncode
319 # We get random maxint errors from subprocesses __del__
320 __del__ = lambda self: None
322 def setpgid_preexec_fn():
323 os.setpgid(0, 0)
325 def runCommand(cmd, **kwargs):
326 if sys.platform != "win32":
327 return Popen(cmd, preexec_fn=setpgid_preexec_fn, **kwargs)
328 else:
329 return Popen(cmd, **kwargs)