| OLD | NEW |
| (Empty) |
| 1 # A module to expose various thread/process/job related structures and | |
| 2 # methods from kernel32 | |
| 3 # | |
| 4 # The MIT License | |
| 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. | |
| 36 | |
| 37 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFU
NCTYPE | |
| 38 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WO
RD | |
| 39 from qijo import QueryInformationJobObject | |
| 40 | |
| 41 LPVOID = c_void_p | |
| 42 LPBYTE = POINTER(BYTE) | |
| 43 LPDWORD = POINTER(DWORD) | |
| 44 LPBOOL = POINTER(BOOL) | |
| 45 | |
| 46 def ErrCheckBool(result, func, args): | |
| 47 """errcheck function for Windows functions that return a BOOL True | |
| 48 on success""" | |
| 49 if not result: | |
| 50 raise WinError() | |
| 51 return args | |
| 52 | |
| 53 | |
| 54 # AutoHANDLE | |
| 55 | |
| 56 class AutoHANDLE(HANDLE): | |
| 57 """Subclass of HANDLE which will call CloseHandle() on deletion.""" | |
| 58 | |
| 59 CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) | |
| 60 CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) | |
| 61 CloseHandle.errcheck = ErrCheckBool | |
| 62 | |
| 63 def Close(self): | |
| 64 if self.value and self.value != HANDLE(-1).value: | |
| 65 self.CloseHandle(self) | |
| 66 self.value = 0 | |
| 67 | |
| 68 def __del__(self): | |
| 69 self.Close() | |
| 70 | |
| 71 def __int__(self): | |
| 72 return self.value | |
| 73 | |
| 74 def ErrCheckHandle(result, func, args): | |
| 75 """errcheck function for Windows functions that return a HANDLE.""" | |
| 76 if not result: | |
| 77 raise WinError() | |
| 78 return AutoHANDLE(result) | |
| 79 | |
| 80 # PROCESS_INFORMATION structure | |
| 81 | |
| 82 class PROCESS_INFORMATION(Structure): | |
| 83 _fields_ = [("hProcess", HANDLE), | |
| 84 ("hThread", HANDLE), | |
| 85 ("dwProcessID", DWORD), | |
| 86 ("dwThreadID", DWORD)] | |
| 87 | |
| 88 def __init__(self): | |
| 89 Structure.__init__(self) | |
| 90 | |
| 91 self.cb = sizeof(self) | |
| 92 | |
| 93 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) | |
| 94 | |
| 95 # STARTUPINFO structure | |
| 96 | |
| 97 class STARTUPINFO(Structure): | |
| 98 _fields_ = [("cb", DWORD), | |
| 99 ("lpReserved", LPWSTR), | |
| 100 ("lpDesktop", LPWSTR), | |
| 101 ("lpTitle", LPWSTR), | |
| 102 ("dwX", DWORD), | |
| 103 ("dwY", DWORD), | |
| 104 ("dwXSize", DWORD), | |
| 105 ("dwYSize", DWORD), | |
| 106 ("dwXCountChars", DWORD), | |
| 107 ("dwYCountChars", DWORD), | |
| 108 ("dwFillAttribute", DWORD), | |
| 109 ("dwFlags", DWORD), | |
| 110 ("wShowWindow", WORD), | |
| 111 ("cbReserved2", WORD), | |
| 112 ("lpReserved2", LPBYTE), | |
| 113 ("hStdInput", HANDLE), | |
| 114 ("hStdOutput", HANDLE), | |
| 115 ("hStdError", HANDLE) | |
| 116 ] | |
| 117 LPSTARTUPINFO = POINTER(STARTUPINFO) | |
| 118 | |
| 119 SW_HIDE = 0 | |
| 120 | |
| 121 STARTF_USESHOWWINDOW = 0x01 | |
| 122 STARTF_USESIZE = 0x02 | |
| 123 STARTF_USEPOSITION = 0x04 | |
| 124 STARTF_USECOUNTCHARS = 0x08 | |
| 125 STARTF_USEFILLATTRIBUTE = 0x10 | |
| 126 STARTF_RUNFULLSCREEN = 0x20 | |
| 127 STARTF_FORCEONFEEDBACK = 0x40 | |
| 128 STARTF_FORCEOFFFEEDBACK = 0x80 | |
| 129 STARTF_USESTDHANDLES = 0x100 | |
| 130 | |
| 131 # EnvironmentBlock | |
| 132 | |
| 133 class EnvironmentBlock: | |
| 134 """An object which can be passed as the lpEnv parameter of CreateProcess. | |
| 135 It is initialized with a dictionary.""" | |
| 136 | |
| 137 def __init__(self, dict): | |
| 138 if not dict: | |
| 139 self._as_parameter_ = None | |
| 140 else: | |
| 141 values = ["%s=%s" % (key, value) | |
| 142 for (key, value) in dict.iteritems()] | |
| 143 values.append("") | |
| 144 self._as_parameter_ = LPCWSTR("\0".join(values)) | |
| 145 | |
| 146 # CreateProcess() | |
| 147 | |
| 148 CreateProcessProto = WINFUNCTYPE(BOOL, # Return type | |
| 149 LPCWSTR, # lpApplicationName | |
| 150 LPWSTR, # lpCommandLine | |
| 151 LPVOID, # lpProcessAttributes | |
| 152 LPVOID, # lpThreadAttributes | |
| 153 BOOL, # bInheritHandles | |
| 154 DWORD, # dwCreationFlags | |
| 155 LPVOID, # lpEnvironment | |
| 156 LPCWSTR, # lpCurrentDirectory | |
| 157 LPSTARTUPINFO, # lpStartupInfo | |
| 158 LPPROCESS_INFORMATION # lpProcessInformation | |
| 159 ) | |
| 160 | |
| 161 CreateProcessFlags = ((1, "lpApplicationName", None), | |
| 162 (1, "lpCommandLine"), | |
| 163 (1, "lpProcessAttributes", None), | |
| 164 (1, "lpThreadAttributes", None), | |
| 165 (1, "bInheritHandles", True), | |
| 166 (1, "dwCreationFlags", 0), | |
| 167 (1, "lpEnvironment", None), | |
| 168 (1, "lpCurrentDirectory", None), | |
| 169 (1, "lpStartupInfo"), | |
| 170 (2, "lpProcessInformation")) | |
| 171 | |
| 172 def ErrCheckCreateProcess(result, func, args): | |
| 173 ErrCheckBool(result, func, args) | |
| 174 # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) | |
| 175 pi = args[9] | |
| 176 return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.d
wThreadID | |
| 177 | |
| 178 CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), | |
| 179 CreateProcessFlags) | |
| 180 CreateProcess.errcheck = ErrCheckCreateProcess | |
| 181 | |
| 182 # flags for CreateProcess | |
| 183 CREATE_BREAKAWAY_FROM_JOB = 0x01000000 | |
| 184 CREATE_DEFAULT_ERROR_MODE = 0x04000000 | |
| 185 CREATE_NEW_CONSOLE = 0x00000010 | |
| 186 CREATE_NEW_PROCESS_GROUP = 0x00000200 | |
| 187 CREATE_NO_WINDOW = 0x08000000 | |
| 188 CREATE_SUSPENDED = 0x00000004 | |
| 189 CREATE_UNICODE_ENVIRONMENT = 0x00000400 | |
| 190 | |
| 191 # flags for job limit information | |
| 192 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx | |
| 193 JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 | |
| 194 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 | |
| 195 | |
| 196 # XXX these flags should be documented | |
| 197 DEBUG_ONLY_THIS_PROCESS = 0x00000002 | |
| 198 DEBUG_PROCESS = 0x00000001 | |
| 199 DETACHED_PROCESS = 0x00000008 | |
| 200 | |
| 201 # CreateJobObject() | |
| 202 | |
| 203 CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type | |
| 204 LPVOID, # lpJobAttributes | |
| 205 LPCWSTR # lpName | |
| 206 ) | |
| 207 | |
| 208 CreateJobObjectFlags = ((1, "lpJobAttributes", None), | |
| 209 (1, "lpName", None)) | |
| 210 | |
| 211 CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), | |
| 212 CreateJobObjectFlags) | |
| 213 CreateJobObject.errcheck = ErrCheckHandle | |
| 214 | |
| 215 # AssignProcessToJobObject() | |
| 216 | |
| 217 AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type | |
| 218 HANDLE, # hJob | |
| 219 HANDLE # hProcess | |
| 220 ) | |
| 221 AssignProcessToJobObjectFlags = ((1, "hJob"), | |
| 222 (1, "hProcess")) | |
| 223 AssignProcessToJobObject = AssignProcessToJobObjectProto( | |
| 224 ("AssignProcessToJobObject", windll.kernel32), | |
| 225 AssignProcessToJobObjectFlags) | |
| 226 AssignProcessToJobObject.errcheck = ErrCheckBool | |
| 227 | |
| 228 # GetCurrentProcess() | |
| 229 # because os.getPid() is way too easy | |
| 230 GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type | |
| 231 ) | |
| 232 GetCurrentProcessFlags = () | |
| 233 GetCurrentProcess = GetCurrentProcessProto( | |
| 234 ("GetCurrentProcess", windll.kernel32), | |
| 235 GetCurrentProcessFlags) | |
| 236 GetCurrentProcess.errcheck = ErrCheckHandle | |
| 237 | |
| 238 # IsProcessInJob() | |
| 239 try: | |
| 240 IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type | |
| 241 HANDLE, # Process Handle | |
| 242 HANDLE, # Job Handle | |
| 243 LPBOOL # Result | |
| 244 ) | |
| 245 IsProcessInJobFlags = ((1, "ProcessHandle"), | |
| 246 (1, "JobHandle", HANDLE(0)), | |
| 247 (2, "Result")) | |
| 248 IsProcessInJob = IsProcessInJobProto( | |
| 249 ("IsProcessInJob", windll.kernel32), | |
| 250 IsProcessInJobFlags) | |
| 251 IsProcessInJob.errcheck = ErrCheckBool | |
| 252 except AttributeError: | |
| 253 # windows 2k doesn't have this API | |
| 254 def IsProcessInJob(process): | |
| 255 return False | |
| 256 | |
| 257 | |
| 258 # ResumeThread() | |
| 259 | |
| 260 def ErrCheckResumeThread(result, func, args): | |
| 261 if result == -1: | |
| 262 raise WinError() | |
| 263 | |
| 264 return args | |
| 265 | |
| 266 ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type | |
| 267 HANDLE # hThread | |
| 268 ) | |
| 269 ResumeThreadFlags = ((1, "hThread"),) | |
| 270 ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), | |
| 271 ResumeThreadFlags) | |
| 272 ResumeThread.errcheck = ErrCheckResumeThread | |
| 273 | |
| 274 # TerminateProcess() | |
| 275 | |
| 276 TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type | |
| 277 HANDLE, # hProcess | |
| 278 UINT # uExitCode | |
| 279 ) | |
| 280 TerminateProcessFlags = ((1, "hProcess"), | |
| 281 (1, "uExitCode", 127)) | |
| 282 TerminateProcess = TerminateProcessProto( | |
| 283 ("TerminateProcess", windll.kernel32), | |
| 284 TerminateProcessFlags) | |
| 285 TerminateProcess.errcheck = ErrCheckBool | |
| 286 | |
| 287 # TerminateJobObject() | |
| 288 | |
| 289 TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type | |
| 290 HANDLE, # hJob | |
| 291 UINT # uExitCode | |
| 292 ) | |
| 293 TerminateJobObjectFlags = ((1, "hJob"), | |
| 294 (1, "uExitCode", 127)) | |
| 295 TerminateJobObject = TerminateJobObjectProto( | |
| 296 ("TerminateJobObject", windll.kernel32), | |
| 297 TerminateJobObjectFlags) | |
| 298 TerminateJobObject.errcheck = ErrCheckBool | |
| 299 | |
| 300 # WaitForSingleObject() | |
| 301 | |
| 302 WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type | |
| 303 HANDLE, # hHandle | |
| 304 DWORD, # dwMilliseconds | |
| 305 ) | |
| 306 WaitForSingleObjectFlags = ((1, "hHandle"), | |
| 307 (1, "dwMilliseconds", -1)) | |
| 308 WaitForSingleObject = WaitForSingleObjectProto( | |
| 309 ("WaitForSingleObject", windll.kernel32), | |
| 310 WaitForSingleObjectFlags) | |
| 311 | |
| 312 INFINITE = -1 | |
| 313 WAIT_TIMEOUT = 0x0102 | |
| 314 WAIT_OBJECT_0 = 0x0 | |
| 315 WAIT_ABANDONED = 0x0080 | |
| 316 | |
| 317 # GetExitCodeProcess() | |
| 318 | |
| 319 GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type | |
| 320 HANDLE, # hProcess | |
| 321 LPDWORD, # lpExitCode | |
| 322 ) | |
| 323 GetExitCodeProcessFlags = ((1, "hProcess"), | |
| 324 (2, "lpExitCode")) | |
| 325 GetExitCodeProcess = GetExitCodeProcessProto( | |
| 326 ("GetExitCodeProcess", windll.kernel32), | |
| 327 GetExitCodeProcessFlags) | |
| 328 GetExitCodeProcess.errcheck = ErrCheckBool | |
| 329 | |
| 330 def CanCreateJobObject(): | |
| 331 currentProc = GetCurrentProcess() | |
| 332 if IsProcessInJob(currentProc): | |
| 333 jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitIn
formation') | |
| 334 limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] | |
| 335 return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitfla
gs & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) | |
| 336 else: | |
| 337 return True | |
| 338 | |
| 339 ### testing functions | |
| 340 | |
| 341 def parent(): | |
| 342 print 'Starting parent' | |
| 343 currentProc = GetCurrentProcess() | |
| 344 if IsProcessInJob(currentProc): | |
| 345 print >> sys.stderr, "You should not be in a job object to test" | |
| 346 sys.exit(1) | |
| 347 assert CanCreateJobObject() | |
| 348 print 'File: %s' % __file__ | |
| 349 command = [sys.executable, __file__, '-child'] | |
| 350 print 'Running command: %s' % command | |
| 351 process = Popen(command) | |
| 352 process.kill() | |
| 353 code = process.returncode | |
| 354 print 'Child code: %s' % code | |
| 355 assert code == 127 | |
| 356 | |
| 357 def child(): | |
| 358 print 'Starting child' | |
| 359 currentProc = GetCurrentProcess() | |
| 360 injob = IsProcessInJob(currentProc) | |
| 361 print "Is in a job?: %s" % injob | |
| 362 can_create = CanCreateJobObject() | |
| 363 print 'Can create job?: %s' % can_create | |
| 364 process = Popen('c:\\windows\\notepad.exe') | |
| 365 assert process._job | |
| 366 jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInf
ormation') | |
| 367 print 'Job info: %s' % jobinfo | |
| 368 limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] | |
| 369 print 'LimitFlags: %s' % limitflags | |
| 370 process.kill() | |
| 371 | |
| 372 if __name__ == '__main__': | |
| 373 import sys | |
| 374 from killableprocess import Popen | |
| 375 nargs = len(sys.argv[1:]) | |
| 376 if nargs: | |
| 377 if nargs != 1 or sys.argv[1] != '-child': | |
| 378 raise AssertionError('Wrong flags; run like `python /path/to/winproc
ess.py`') | |
| 379 child() | |
| 380 else: | |
| 381 parent() | |
| OLD | NEW |