1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/python-lib/mozrunner/winprocess.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,379 @@ 1.4 +# A module to expose various thread/process/job related structures and 1.5 +# methods from kernel32 1.6 +# 1.7 +# The MIT License 1.8 +# 1.9 +# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se> 1.10 +# 1.11 +# Additions and modifications written by Benjamin Smedberg 1.12 +# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation 1.13 +# <http://www.mozilla.org/> 1.14 +# 1.15 +# More Modifications 1.16 +# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com> 1.17 +# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com> 1.18 +# 1.19 +# By obtaining, using, and/or copying this software and/or its 1.20 +# associated documentation, you agree that you have read, understood, 1.21 +# and will comply with the following terms and conditions: 1.22 +# 1.23 +# Permission to use, copy, modify, and distribute this software and 1.24 +# its associated documentation for any purpose and without fee is 1.25 +# hereby granted, provided that the above copyright notice appears in 1.26 +# all copies, and that both that copyright notice and this permission 1.27 +# notice appear in supporting documentation, and that the name of the 1.28 +# author not be used in advertising or publicity pertaining to 1.29 +# distribution of the software without specific, written prior 1.30 +# permission. 1.31 +# 1.32 +# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1.33 +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 1.34 +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR 1.35 +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 1.36 +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 1.37 +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 1.38 +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1.39 + 1.40 +from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE 1.41 +from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD, \ 1.42 + c_buffer, c_ulong, byref 1.43 +from qijo import QueryInformationJobObject 1.44 + 1.45 +LPVOID = c_void_p 1.46 +LPBYTE = POINTER(BYTE) 1.47 +LPDWORD = POINTER(DWORD) 1.48 +LPBOOL = POINTER(BOOL) 1.49 + 1.50 +def ErrCheckBool(result, func, args): 1.51 + """errcheck function for Windows functions that return a BOOL True 1.52 + on success""" 1.53 + if not result: 1.54 + raise WinError() 1.55 + return args 1.56 + 1.57 + 1.58 +# AutoHANDLE 1.59 + 1.60 +class AutoHANDLE(HANDLE): 1.61 + """Subclass of HANDLE which will call CloseHandle() on deletion.""" 1.62 + 1.63 + CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) 1.64 + CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) 1.65 + CloseHandle.errcheck = ErrCheckBool 1.66 + 1.67 + def Close(self): 1.68 + if self.value and self.value != HANDLE(-1).value: 1.69 + self.CloseHandle(self) 1.70 + self.value = 0 1.71 + 1.72 + def __del__(self): 1.73 + self.Close() 1.74 + 1.75 + def __int__(self): 1.76 + return self.value 1.77 + 1.78 +def ErrCheckHandle(result, func, args): 1.79 + """errcheck function for Windows functions that return a HANDLE.""" 1.80 + if not result: 1.81 + raise WinError() 1.82 + return AutoHANDLE(result) 1.83 + 1.84 +# PROCESS_INFORMATION structure 1.85 + 1.86 +class PROCESS_INFORMATION(Structure): 1.87 + _fields_ = [("hProcess", HANDLE), 1.88 + ("hThread", HANDLE), 1.89 + ("dwProcessID", DWORD), 1.90 + ("dwThreadID", DWORD)] 1.91 + 1.92 + def __init__(self): 1.93 + Structure.__init__(self) 1.94 + 1.95 + self.cb = sizeof(self) 1.96 + 1.97 +LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) 1.98 + 1.99 +# STARTUPINFO structure 1.100 + 1.101 +class STARTUPINFO(Structure): 1.102 + _fields_ = [("cb", DWORD), 1.103 + ("lpReserved", LPWSTR), 1.104 + ("lpDesktop", LPWSTR), 1.105 + ("lpTitle", LPWSTR), 1.106 + ("dwX", DWORD), 1.107 + ("dwY", DWORD), 1.108 + ("dwXSize", DWORD), 1.109 + ("dwYSize", DWORD), 1.110 + ("dwXCountChars", DWORD), 1.111 + ("dwYCountChars", DWORD), 1.112 + ("dwFillAttribute", DWORD), 1.113 + ("dwFlags", DWORD), 1.114 + ("wShowWindow", WORD), 1.115 + ("cbReserved2", WORD), 1.116 + ("lpReserved2", LPBYTE), 1.117 + ("hStdInput", HANDLE), 1.118 + ("hStdOutput", HANDLE), 1.119 + ("hStdError", HANDLE) 1.120 + ] 1.121 +LPSTARTUPINFO = POINTER(STARTUPINFO) 1.122 + 1.123 +SW_HIDE = 0 1.124 + 1.125 +STARTF_USESHOWWINDOW = 0x01 1.126 +STARTF_USESIZE = 0x02 1.127 +STARTF_USEPOSITION = 0x04 1.128 +STARTF_USECOUNTCHARS = 0x08 1.129 +STARTF_USEFILLATTRIBUTE = 0x10 1.130 +STARTF_RUNFULLSCREEN = 0x20 1.131 +STARTF_FORCEONFEEDBACK = 0x40 1.132 +STARTF_FORCEOFFFEEDBACK = 0x80 1.133 +STARTF_USESTDHANDLES = 0x100 1.134 + 1.135 +# EnvironmentBlock 1.136 + 1.137 +class EnvironmentBlock: 1.138 + """An object which can be passed as the lpEnv parameter of CreateProcess. 1.139 + It is initialized with a dictionary.""" 1.140 + 1.141 + def __init__(self, dict): 1.142 + if not dict: 1.143 + self._as_parameter_ = None 1.144 + else: 1.145 + values = ["%s=%s" % (key, value) 1.146 + for (key, value) in dict.iteritems()] 1.147 + values.append("") 1.148 + self._as_parameter_ = LPCWSTR("\0".join(values)) 1.149 + 1.150 +# CreateProcess() 1.151 + 1.152 +CreateProcessProto = WINFUNCTYPE(BOOL, # Return type 1.153 + LPCWSTR, # lpApplicationName 1.154 + LPWSTR, # lpCommandLine 1.155 + LPVOID, # lpProcessAttributes 1.156 + LPVOID, # lpThreadAttributes 1.157 + BOOL, # bInheritHandles 1.158 + DWORD, # dwCreationFlags 1.159 + LPVOID, # lpEnvironment 1.160 + LPCWSTR, # lpCurrentDirectory 1.161 + LPSTARTUPINFO, # lpStartupInfo 1.162 + LPPROCESS_INFORMATION # lpProcessInformation 1.163 + ) 1.164 + 1.165 +CreateProcessFlags = ((1, "lpApplicationName", None), 1.166 + (1, "lpCommandLine"), 1.167 + (1, "lpProcessAttributes", None), 1.168 + (1, "lpThreadAttributes", None), 1.169 + (1, "bInheritHandles", True), 1.170 + (1, "dwCreationFlags", 0), 1.171 + (1, "lpEnvironment", None), 1.172 + (1, "lpCurrentDirectory", None), 1.173 + (1, "lpStartupInfo"), 1.174 + (2, "lpProcessInformation")) 1.175 + 1.176 +def ErrCheckCreateProcess(result, func, args): 1.177 + ErrCheckBool(result, func, args) 1.178 + # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) 1.179 + pi = args[9] 1.180 + return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID 1.181 + 1.182 +CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), 1.183 + CreateProcessFlags) 1.184 +CreateProcess.errcheck = ErrCheckCreateProcess 1.185 + 1.186 +# flags for CreateProcess 1.187 +CREATE_BREAKAWAY_FROM_JOB = 0x01000000 1.188 +CREATE_DEFAULT_ERROR_MODE = 0x04000000 1.189 +CREATE_NEW_CONSOLE = 0x00000010 1.190 +CREATE_NEW_PROCESS_GROUP = 0x00000200 1.191 +CREATE_NO_WINDOW = 0x08000000 1.192 +CREATE_SUSPENDED = 0x00000004 1.193 +CREATE_UNICODE_ENVIRONMENT = 0x00000400 1.194 + 1.195 +# flags for job limit information 1.196 +# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx 1.197 +JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 1.198 +JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 1.199 + 1.200 +# XXX these flags should be documented 1.201 +DEBUG_ONLY_THIS_PROCESS = 0x00000002 1.202 +DEBUG_PROCESS = 0x00000001 1.203 +DETACHED_PROCESS = 0x00000008 1.204 + 1.205 +# CreateJobObject() 1.206 + 1.207 +CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type 1.208 + LPVOID, # lpJobAttributes 1.209 + LPCWSTR # lpName 1.210 + ) 1.211 + 1.212 +CreateJobObjectFlags = ((1, "lpJobAttributes", None), 1.213 + (1, "lpName", None)) 1.214 + 1.215 +CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), 1.216 + CreateJobObjectFlags) 1.217 +CreateJobObject.errcheck = ErrCheckHandle 1.218 + 1.219 +# AssignProcessToJobObject() 1.220 + 1.221 +AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type 1.222 + HANDLE, # hJob 1.223 + HANDLE # hProcess 1.224 + ) 1.225 +AssignProcessToJobObjectFlags = ((1, "hJob"), 1.226 + (1, "hProcess")) 1.227 +AssignProcessToJobObject = AssignProcessToJobObjectProto( 1.228 + ("AssignProcessToJobObject", windll.kernel32), 1.229 + AssignProcessToJobObjectFlags) 1.230 +AssignProcessToJobObject.errcheck = ErrCheckBool 1.231 + 1.232 +# GetCurrentProcess() 1.233 +# because os.getPid() is way too easy 1.234 +GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type 1.235 + ) 1.236 +GetCurrentProcessFlags = () 1.237 +GetCurrentProcess = GetCurrentProcessProto( 1.238 + ("GetCurrentProcess", windll.kernel32), 1.239 + GetCurrentProcessFlags) 1.240 +GetCurrentProcess.errcheck = ErrCheckHandle 1.241 + 1.242 +# IsProcessInJob() 1.243 +try: 1.244 + IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type 1.245 + HANDLE, # Process Handle 1.246 + HANDLE, # Job Handle 1.247 + LPBOOL # Result 1.248 + ) 1.249 + IsProcessInJobFlags = ((1, "ProcessHandle"), 1.250 + (1, "JobHandle", HANDLE(0)), 1.251 + (2, "Result")) 1.252 + IsProcessInJob = IsProcessInJobProto( 1.253 + ("IsProcessInJob", windll.kernel32), 1.254 + IsProcessInJobFlags) 1.255 + IsProcessInJob.errcheck = ErrCheckBool 1.256 +except AttributeError: 1.257 + # windows 2k doesn't have this API 1.258 + def IsProcessInJob(process): 1.259 + return False 1.260 + 1.261 + 1.262 +# ResumeThread() 1.263 + 1.264 +def ErrCheckResumeThread(result, func, args): 1.265 + if result == -1: 1.266 + raise WinError() 1.267 + 1.268 + return args 1.269 + 1.270 +ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type 1.271 + HANDLE # hThread 1.272 + ) 1.273 +ResumeThreadFlags = ((1, "hThread"),) 1.274 +ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), 1.275 + ResumeThreadFlags) 1.276 +ResumeThread.errcheck = ErrCheckResumeThread 1.277 + 1.278 +# TerminateProcess() 1.279 + 1.280 +TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type 1.281 + HANDLE, # hProcess 1.282 + UINT # uExitCode 1.283 + ) 1.284 +TerminateProcessFlags = ((1, "hProcess"), 1.285 + (1, "uExitCode", 127)) 1.286 +TerminateProcess = TerminateProcessProto( 1.287 + ("TerminateProcess", windll.kernel32), 1.288 + TerminateProcessFlags) 1.289 +TerminateProcess.errcheck = ErrCheckBool 1.290 + 1.291 +# TerminateJobObject() 1.292 + 1.293 +TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type 1.294 + HANDLE, # hJob 1.295 + UINT # uExitCode 1.296 + ) 1.297 +TerminateJobObjectFlags = ((1, "hJob"), 1.298 + (1, "uExitCode", 127)) 1.299 +TerminateJobObject = TerminateJobObjectProto( 1.300 + ("TerminateJobObject", windll.kernel32), 1.301 + TerminateJobObjectFlags) 1.302 +TerminateJobObject.errcheck = ErrCheckBool 1.303 + 1.304 +# WaitForSingleObject() 1.305 + 1.306 +WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type 1.307 + HANDLE, # hHandle 1.308 + DWORD, # dwMilliseconds 1.309 + ) 1.310 +WaitForSingleObjectFlags = ((1, "hHandle"), 1.311 + (1, "dwMilliseconds", -1)) 1.312 +WaitForSingleObject = WaitForSingleObjectProto( 1.313 + ("WaitForSingleObject", windll.kernel32), 1.314 + WaitForSingleObjectFlags) 1.315 + 1.316 +INFINITE = -1 1.317 +WAIT_TIMEOUT = 0x0102 1.318 +WAIT_OBJECT_0 = 0x0 1.319 +WAIT_ABANDONED = 0x0080 1.320 +WAIT_FAILED = 0xFFFFFFFF 1.321 + 1.322 +# GetExitCodeProcess() 1.323 + 1.324 +GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type 1.325 + HANDLE, # hProcess 1.326 + LPDWORD, # lpExitCode 1.327 + ) 1.328 +GetExitCodeProcessFlags = ((1, "hProcess"), 1.329 + (2, "lpExitCode")) 1.330 +GetExitCodeProcess = GetExitCodeProcessProto( 1.331 + ("GetExitCodeProcess", windll.kernel32), 1.332 + GetExitCodeProcessFlags) 1.333 +GetExitCodeProcess.errcheck = ErrCheckBool 1.334 + 1.335 +def CanCreateJobObject(): 1.336 + # Running firefox in a job (from cfx) hangs on sites using flash plugin 1.337 + # so job creation is turned off for now. (see Bug 768651). 1.338 + return False 1.339 + 1.340 +### testing functions 1.341 + 1.342 +def parent(): 1.343 + print 'Starting parent' 1.344 + currentProc = GetCurrentProcess() 1.345 + if IsProcessInJob(currentProc): 1.346 + print >> sys.stderr, "You should not be in a job object to test" 1.347 + sys.exit(1) 1.348 + assert CanCreateJobObject() 1.349 + print 'File: %s' % __file__ 1.350 + command = [sys.executable, __file__, '-child'] 1.351 + print 'Running command: %s' % command 1.352 + process = Popen(command) 1.353 + process.kill() 1.354 + code = process.returncode 1.355 + print 'Child code: %s' % code 1.356 + assert code == 127 1.357 + 1.358 +def child(): 1.359 + print 'Starting child' 1.360 + currentProc = GetCurrentProcess() 1.361 + injob = IsProcessInJob(currentProc) 1.362 + print "Is in a job?: %s" % injob 1.363 + can_create = CanCreateJobObject() 1.364 + print 'Can create job?: %s' % can_create 1.365 + process = Popen('c:\\windows\\notepad.exe') 1.366 + assert process._job 1.367 + jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation') 1.368 + print 'Job info: %s' % jobinfo 1.369 + limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] 1.370 + print 'LimitFlags: %s' % limitflags 1.371 + process.kill() 1.372 + 1.373 +if __name__ == '__main__': 1.374 + import sys 1.375 + from killableprocess import Popen 1.376 + nargs = len(sys.argv[1:]) 1.377 + if nargs: 1.378 + if nargs != 1 or sys.argv[1] != '-child': 1.379 + raise AssertionError('Wrong flags; run like `python /path/to/winprocess.py`') 1.380 + child() 1.381 + else: 1.382 + parent()