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, Union, windll, WinError
, WINFUNCTYPE, c_ulong |
| 38 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WO
RD, ULONG |
| 39 from qijo import QueryInformationJobObject |
| 40 |
| 41 LPVOID = c_void_p |
| 42 LPBYTE = POINTER(BYTE) |
| 43 LPDWORD = POINTER(DWORD) |
| 44 LPBOOL = POINTER(BOOL) |
| 45 LPULONG = POINTER(c_ulong) |
| 46 |
| 47 def ErrCheckBool(result, func, args): |
| 48 """errcheck function for Windows functions that return a BOOL True |
| 49 on success""" |
| 50 if not result: |
| 51 raise WinError() |
| 52 return args |
| 53 |
| 54 |
| 55 # AutoHANDLE |
| 56 |
| 57 class AutoHANDLE(HANDLE): |
| 58 """Subclass of HANDLE which will call CloseHandle() on deletion.""" |
| 59 |
| 60 CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) |
| 61 CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) |
| 62 CloseHandle.errcheck = ErrCheckBool |
| 63 |
| 64 def Close(self): |
| 65 if self.value and self.value != HANDLE(-1).value: |
| 66 self.CloseHandle(self) |
| 67 self.value = 0 |
| 68 |
| 69 def __del__(self): |
| 70 self.Close() |
| 71 |
| 72 def __int__(self): |
| 73 return self.value |
| 74 |
| 75 def ErrCheckHandle(result, func, args): |
| 76 """errcheck function for Windows functions that return a HANDLE.""" |
| 77 if not result: |
| 78 raise WinError() |
| 79 return AutoHANDLE(result) |
| 80 |
| 81 # PROCESS_INFORMATION structure |
| 82 |
| 83 class PROCESS_INFORMATION(Structure): |
| 84 _fields_ = [("hProcess", HANDLE), |
| 85 ("hThread", HANDLE), |
| 86 ("dwProcessID", DWORD), |
| 87 ("dwThreadID", DWORD)] |
| 88 |
| 89 def __init__(self): |
| 90 Structure.__init__(self) |
| 91 |
| 92 self.cb = sizeof(self) |
| 93 |
| 94 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) |
| 95 |
| 96 # STARTUPINFO structure |
| 97 |
| 98 class STARTUPINFO(Structure): |
| 99 _fields_ = [("cb", DWORD), |
| 100 ("lpReserved", LPWSTR), |
| 101 ("lpDesktop", LPWSTR), |
| 102 ("lpTitle", LPWSTR), |
| 103 ("dwX", DWORD), |
| 104 ("dwY", DWORD), |
| 105 ("dwXSize", DWORD), |
| 106 ("dwYSize", DWORD), |
| 107 ("dwXCountChars", DWORD), |
| 108 ("dwYCountChars", DWORD), |
| 109 ("dwFillAttribute", DWORD), |
| 110 ("dwFlags", DWORD), |
| 111 ("wShowWindow", WORD), |
| 112 ("cbReserved2", WORD), |
| 113 ("lpReserved2", LPBYTE), |
| 114 ("hStdInput", HANDLE), |
| 115 ("hStdOutput", HANDLE), |
| 116 ("hStdError", HANDLE) |
| 117 ] |
| 118 LPSTARTUPINFO = POINTER(STARTUPINFO) |
| 119 |
| 120 SW_HIDE = 0 |
| 121 |
| 122 STARTF_USESHOWWINDOW = 0x01 |
| 123 STARTF_USESIZE = 0x02 |
| 124 STARTF_USEPOSITION = 0x04 |
| 125 STARTF_USECOUNTCHARS = 0x08 |
| 126 STARTF_USEFILLATTRIBUTE = 0x10 |
| 127 STARTF_RUNFULLSCREEN = 0x20 |
| 128 STARTF_FORCEONFEEDBACK = 0x40 |
| 129 STARTF_FORCEOFFFEEDBACK = 0x80 |
| 130 STARTF_USESTDHANDLES = 0x100 |
| 131 |
| 132 # EnvironmentBlock |
| 133 |
| 134 class EnvironmentBlock: |
| 135 """An object which can be passed as the lpEnv parameter of CreateProcess. |
| 136 It is initialized with a dictionary.""" |
| 137 |
| 138 def __init__(self, dict): |
| 139 if not dict: |
| 140 self._as_parameter_ = None |
| 141 else: |
| 142 values = ["%s=%s" % (key, value) |
| 143 for (key, value) in dict.iteritems()] |
| 144 values.append("") |
| 145 self._as_parameter_ = LPCWSTR("\0".join(values)) |
| 146 |
| 147 # Error Messages we need to watch for go here |
| 148 # See: http://msdn.microsoft.com/en-us/library/ms681388%28v=vs.85%29.aspx |
| 149 ERROR_ABANDONED_WAIT_0 = 735 |
| 150 |
| 151 # GetLastError() |
| 152 GetLastErrorProto = WINFUNCTYPE(DWORD # Return Type |
| 153 ) |
| 154 GetLastErrorFlags = () |
| 155 GetLastError = GetLastErrorProto(("GetLastError", windll.kernel32), GetLastError
Flags) |
| 156 |
| 157 # CreateProcess() |
| 158 |
| 159 CreateProcessProto = WINFUNCTYPE(BOOL, # Return type |
| 160 LPCWSTR, # lpApplicationName |
| 161 LPWSTR, # lpCommandLine |
| 162 LPVOID, # lpProcessAttributes |
| 163 LPVOID, # lpThreadAttributes |
| 164 BOOL, # bInheritHandles |
| 165 DWORD, # dwCreationFlags |
| 166 LPVOID, # lpEnvironment |
| 167 LPCWSTR, # lpCurrentDirectory |
| 168 LPSTARTUPINFO, # lpStartupInfo |
| 169 LPPROCESS_INFORMATION # lpProcessInformation |
| 170 ) |
| 171 |
| 172 CreateProcessFlags = ((1, "lpApplicationName", None), |
| 173 (1, "lpCommandLine"), |
| 174 (1, "lpProcessAttributes", None), |
| 175 (1, "lpThreadAttributes", None), |
| 176 (1, "bInheritHandles", True), |
| 177 (1, "dwCreationFlags", 0), |
| 178 (1, "lpEnvironment", None), |
| 179 (1, "lpCurrentDirectory", None), |
| 180 (1, "lpStartupInfo"), |
| 181 (2, "lpProcessInformation")) |
| 182 |
| 183 def ErrCheckCreateProcess(result, func, args): |
| 184 ErrCheckBool(result, func, args) |
| 185 # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) |
| 186 pi = args[9] |
| 187 return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.d
wThreadID |
| 188 |
| 189 CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), |
| 190 CreateProcessFlags) |
| 191 CreateProcess.errcheck = ErrCheckCreateProcess |
| 192 |
| 193 # flags for CreateProcess |
| 194 CREATE_BREAKAWAY_FROM_JOB = 0x01000000 |
| 195 CREATE_DEFAULT_ERROR_MODE = 0x04000000 |
| 196 CREATE_NEW_CONSOLE = 0x00000010 |
| 197 CREATE_NEW_PROCESS_GROUP = 0x00000200 |
| 198 CREATE_NO_WINDOW = 0x08000000 |
| 199 CREATE_SUSPENDED = 0x00000004 |
| 200 CREATE_UNICODE_ENVIRONMENT = 0x00000400 |
| 201 |
| 202 # Flags for IOCompletion ports (some of these would probably be defined if |
| 203 # we used the win32 extensions for python, but we don't want to do that if we |
| 204 # can help it. |
| 205 INVALID_HANDLE_VALUE = HANDLE(-1) # From winbase.h |
| 206 |
| 207 # Self Defined Constants for IOPort <--> Job Object communication |
| 208 COMPKEY_TERMINATE = c_ulong(0) |
| 209 COMPKEY_JOBOBJECT = c_ulong(1) |
| 210 |
| 211 # flags for job limit information |
| 212 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx |
| 213 JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 |
| 214 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 |
| 215 |
| 216 # Flags for Job Object Completion Port Message IDs from winnt.h |
| 217 # See also: http://msdn.microsoft.com/en-us/library/ms684141%28v=vs.85%29.aspx |
| 218 JOB_OBJECT_MSG_END_OF_JOB_TIME = 1 |
| 219 JOB_OBJECT_MSG_END_OF_PROCESS_TIME = 2 |
| 220 JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT = 3 |
| 221 JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = 4 |
| 222 JOB_OBJECT_MSG_NEW_PROCESS = 6 |
| 223 JOB_OBJECT_MSG_EXIT_PROCESS = 7 |
| 224 JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS = 8 |
| 225 JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT = 9 |
| 226 JOB_OBJECT_MSG_JOB_MEMORY_LIMIT = 10 |
| 227 |
| 228 # See winbase.h |
| 229 DEBUG_ONLY_THIS_PROCESS = 0x00000002 |
| 230 DEBUG_PROCESS = 0x00000001 |
| 231 DETACHED_PROCESS = 0x00000008 |
| 232 |
| 233 # GetQueuedCompletionPortStatus - http://msdn.microsoft.com/en-us/library/aa3649
86%28v=vs.85%29.aspx |
| 234 GetQueuedCompletionStatusProto = WINFUNCTYPE(BOOL, # Return Type |
| 235 HANDLE, # Completion Port |
| 236 LPDWORD, # Msg ID |
| 237 LPULONG, # Completion Key |
| 238 LPULONG, # PID Returned from t
he call (may be null) |
| 239 DWORD) # milliseconds to wai
t |
| 240 GetQueuedCompletionStatusFlags = ((1, "CompletionPort", INVALID_HANDLE_VALUE), |
| 241 (1, "lpNumberOfBytes", None), |
| 242 (1, "lpCompletionKey", None), |
| 243 (1, "lpPID", None), |
| 244 (1, "dwMilliseconds", 0)) |
| 245 GetQueuedCompletionStatus = GetQueuedCompletionStatusProto(("GetQueuedCompletion
Status", |
| 246 windll.kernel32), |
| 247 GetQueuedCompletionSt
atusFlags) |
| 248 |
| 249 # CreateIOCompletionPort |
| 250 # Note that the completion key is just a number, not a pointer. |
| 251 CreateIoCompletionPortProto = WINFUNCTYPE(HANDLE, # Return Type |
| 252 HANDLE, # File Handle |
| 253 HANDLE, # Existing Completion Por
t |
| 254 c_ulong, # Completion Key |
| 255 DWORD # Number of Threads |
| 256 ) |
| 257 CreateIoCompletionPortFlags = ((1, "FileHandle", INVALID_HANDLE_VALUE), |
| 258 (1, "ExistingCompletionPort", 0), |
| 259 (1, "CompletionKey", c_ulong(0)), |
| 260 (1, "NumberOfConcurrentThreads", 0)) |
| 261 CreateIoCompletionPort = CreateIoCompletionPortProto(("CreateIoCompletionPort", |
| 262 windll.kernel32), |
| 263 CreateIoCompletionPortFlag
s) |
| 264 CreateIoCompletionPort.errcheck = ErrCheckHandle |
| 265 |
| 266 # SetInformationJobObject |
| 267 SetInformationJobObjectProto = WINFUNCTYPE(BOOL, # Return Type |
| 268 HANDLE, # Job Handle |
| 269 DWORD, # Type of Class next param
is |
| 270 LPVOID, # Job Object Class |
| 271 DWORD # Job Object Class Length |
| 272 ) |
| 273 SetInformationJobObjectProtoFlags = ((1, "hJob", None), |
| 274 (1, "JobObjectInfoClass", None), |
| 275 (1, "lpJobObjectInfo", None), |
| 276 (1, "cbJobObjectInfoLength", 0)) |
| 277 SetInformationJobObject = SetInformationJobObjectProto(("SetInformationJobObject
", |
| 278 windll.kernel32), |
| 279 SetInformationJobObjectP
rotoFlags) |
| 280 SetInformationJobObject.errcheck = ErrCheckBool |
| 281 |
| 282 # CreateJobObject() |
| 283 CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type |
| 284 LPVOID, # lpJobAttributes |
| 285 LPCWSTR # lpName |
| 286 ) |
| 287 |
| 288 CreateJobObjectFlags = ((1, "lpJobAttributes", None), |
| 289 (1, "lpName", None)) |
| 290 |
| 291 CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), |
| 292 CreateJobObjectFlags) |
| 293 CreateJobObject.errcheck = ErrCheckHandle |
| 294 |
| 295 # AssignProcessToJobObject() |
| 296 |
| 297 AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
| 298 HANDLE, # hJob |
| 299 HANDLE # hProcess |
| 300 ) |
| 301 AssignProcessToJobObjectFlags = ((1, "hJob"), |
| 302 (1, "hProcess")) |
| 303 AssignProcessToJobObject = AssignProcessToJobObjectProto( |
| 304 ("AssignProcessToJobObject", windll.kernel32), |
| 305 AssignProcessToJobObjectFlags) |
| 306 AssignProcessToJobObject.errcheck = ErrCheckBool |
| 307 |
| 308 # GetCurrentProcess() |
| 309 # because os.getPid() is way too easy |
| 310 GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type |
| 311 ) |
| 312 GetCurrentProcessFlags = () |
| 313 GetCurrentProcess = GetCurrentProcessProto( |
| 314 ("GetCurrentProcess", windll.kernel32), |
| 315 GetCurrentProcessFlags) |
| 316 GetCurrentProcess.errcheck = ErrCheckHandle |
| 317 |
| 318 # IsProcessInJob() |
| 319 try: |
| 320 IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type |
| 321 HANDLE, # Process Handle |
| 322 HANDLE, # Job Handle |
| 323 LPBOOL # Result |
| 324 ) |
| 325 IsProcessInJobFlags = ((1, "ProcessHandle"), |
| 326 (1, "JobHandle", HANDLE(0)), |
| 327 (2, "Result")) |
| 328 IsProcessInJob = IsProcessInJobProto( |
| 329 ("IsProcessInJob", windll.kernel32), |
| 330 IsProcessInJobFlags) |
| 331 IsProcessInJob.errcheck = ErrCheckBool |
| 332 except AttributeError: |
| 333 # windows 2k doesn't have this API |
| 334 def IsProcessInJob(process): |
| 335 return False |
| 336 |
| 337 |
| 338 # ResumeThread() |
| 339 |
| 340 def ErrCheckResumeThread(result, func, args): |
| 341 if result == -1: |
| 342 raise WinError() |
| 343 |
| 344 return args |
| 345 |
| 346 ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type |
| 347 HANDLE # hThread |
| 348 ) |
| 349 ResumeThreadFlags = ((1, "hThread"),) |
| 350 ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), |
| 351 ResumeThreadFlags) |
| 352 ResumeThread.errcheck = ErrCheckResumeThread |
| 353 |
| 354 # TerminateProcess() |
| 355 |
| 356 TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type |
| 357 HANDLE, # hProcess |
| 358 UINT # uExitCode |
| 359 ) |
| 360 TerminateProcessFlags = ((1, "hProcess"), |
| 361 (1, "uExitCode", 127)) |
| 362 TerminateProcess = TerminateProcessProto( |
| 363 ("TerminateProcess", windll.kernel32), |
| 364 TerminateProcessFlags) |
| 365 TerminateProcess.errcheck = ErrCheckBool |
| 366 |
| 367 # TerminateJobObject() |
| 368 |
| 369 TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
| 370 HANDLE, # hJob |
| 371 UINT # uExitCode |
| 372 ) |
| 373 TerminateJobObjectFlags = ((1, "hJob"), |
| 374 (1, "uExitCode", 127)) |
| 375 TerminateJobObject = TerminateJobObjectProto( |
| 376 ("TerminateJobObject", windll.kernel32), |
| 377 TerminateJobObjectFlags) |
| 378 TerminateJobObject.errcheck = ErrCheckBool |
| 379 |
| 380 # WaitForSingleObject() |
| 381 |
| 382 WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type |
| 383 HANDLE, # hHandle |
| 384 DWORD, # dwMilliseconds |
| 385 ) |
| 386 WaitForSingleObjectFlags = ((1, "hHandle"), |
| 387 (1, "dwMilliseconds", -1)) |
| 388 WaitForSingleObject = WaitForSingleObjectProto( |
| 389 ("WaitForSingleObject", windll.kernel32), |
| 390 WaitForSingleObjectFlags) |
| 391 |
| 392 # http://msdn.microsoft.com/en-us/library/ms681381%28v=vs.85%29.aspx |
| 393 INFINITE = -1 |
| 394 WAIT_TIMEOUT = 0x0102 |
| 395 WAIT_OBJECT_0 = 0x0 |
| 396 WAIT_ABANDONED = 0x0080 |
| 397 |
| 398 # http://msdn.microsoft.com/en-us/library/ms683189%28VS.85%29.aspx |
| 399 STILL_ACTIVE = 259 |
| 400 |
| 401 # Used when we terminate a process. |
| 402 ERROR_CONTROL_C_EXIT = 0x23c |
| 403 |
| 404 # GetExitCodeProcess() |
| 405 |
| 406 GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type |
| 407 HANDLE, # hProcess |
| 408 LPDWORD, # lpExitCode |
| 409 ) |
| 410 GetExitCodeProcessFlags = ((1, "hProcess"), |
| 411 (2, "lpExitCode")) |
| 412 GetExitCodeProcess = GetExitCodeProcessProto( |
| 413 ("GetExitCodeProcess", windll.kernel32), |
| 414 GetExitCodeProcessFlags) |
| 415 GetExitCodeProcess.errcheck = ErrCheckBool |
| 416 |
| 417 def CanCreateJobObject(): |
| 418 currentProc = GetCurrentProcess() |
| 419 if IsProcessInJob(currentProc): |
| 420 jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitIn
formation') |
| 421 limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] |
| 422 return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitfla
gs & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) |
| 423 else: |
| 424 return True |
| 425 |
| 426 ### testing functions |
| 427 |
| 428 def parent(): |
| 429 print 'Starting parent' |
| 430 currentProc = GetCurrentProcess() |
| 431 if IsProcessInJob(currentProc): |
| 432 print >> sys.stderr, "You should not be in a job object to test" |
| 433 sys.exit(1) |
| 434 assert CanCreateJobObject() |
| 435 print 'File: %s' % __file__ |
| 436 command = [sys.executable, __file__, '-child'] |
| 437 print 'Running command: %s' % command |
| 438 process = Popen(command) |
| 439 process.kill() |
| 440 code = process.returncode |
| 441 print 'Child code: %s' % code |
| 442 assert code == 127 |
| 443 |
| 444 def child(): |
| 445 print 'Starting child' |
| 446 currentProc = GetCurrentProcess() |
| 447 injob = IsProcessInJob(currentProc) |
| 448 print "Is in a job?: %s" % injob |
| 449 can_create = CanCreateJobObject() |
| 450 print 'Can create job?: %s' % can_create |
| 451 process = Popen('c:\\windows\\notepad.exe') |
| 452 assert process._job |
| 453 jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInf
ormation') |
| 454 print 'Job info: %s' % jobinfo |
| 455 limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] |
| 456 print 'LimitFlags: %s' % limitflags |
| 457 process.kill() |
OLD | NEW |