1
# A module to expose various thread/process/job related structures and
2
# methods from kernel32
6
# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
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/>
13
# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
14
# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
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:
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
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
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, WORD, ULONG
39
from qijo import QueryInformationJobObject
42
LPBYTE = POINTER(BYTE)
43
LPDWORD = POINTER(DWORD)
44
LPBOOL = POINTER(BOOL)
45
LPULONG = POINTER(c_ulong)
47
def ErrCheckBool(result, func, args):
48
"""errcheck function for Windows functions that return a BOOL True
57
class AutoHANDLE(HANDLE):
58
"""Subclass of HANDLE which will call CloseHandle() on deletion."""
60
CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE)
61
CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32))
62
CloseHandle.errcheck = ErrCheckBool
65
if self.value and self.value != HANDLE(-1).value:
66
self.CloseHandle(self)
75
def ErrCheckHandle(result, func, args):
76
"""errcheck function for Windows functions that return a HANDLE."""
79
return AutoHANDLE(result)
81
# PROCESS_INFORMATION structure
83
class PROCESS_INFORMATION(Structure):
84
_fields_ = [("hProcess", HANDLE),
86
("dwProcessID", DWORD),
87
("dwThreadID", DWORD)]
90
Structure.__init__(self)
92
self.cb = sizeof(self)
94
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
96
# STARTUPINFO structure
98
class STARTUPINFO(Structure):
99
_fields_ = [("cb", DWORD),
100
("lpReserved", LPWSTR),
101
("lpDesktop", LPWSTR),
107
("dwXCountChars", DWORD),
108
("dwYCountChars", DWORD),
109
("dwFillAttribute", DWORD),
111
("wShowWindow", WORD),
112
("cbReserved2", WORD),
113
("lpReserved2", LPBYTE),
114
("hStdInput", HANDLE),
115
("hStdOutput", HANDLE),
116
("hStdError", HANDLE)
118
LPSTARTUPINFO = POINTER(STARTUPINFO)
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
134
class EnvironmentBlock:
135
"""An object which can be passed as the lpEnv parameter of CreateProcess.
136
It is initialized with a dictionary."""
138
def __init__(self, dict):
140
self._as_parameter_ = None
142
values = ["%s=%s" % (key, value)
143
for (key, value) in dict.iteritems()]
145
self._as_parameter_ = LPCWSTR("\0".join(values))
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
152
GetLastErrorProto = WINFUNCTYPE(DWORD # Return Type
154
GetLastErrorFlags = ()
155
GetLastError = GetLastErrorProto(("GetLastError", windll.kernel32), GetLastErrorFlags)
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
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"))
183
def ErrCheckCreateProcess(result, func, args):
184
ErrCheckBool(result, func, args)
185
# return a tuple (hProcess, hThread, dwProcessID, dwThreadID)
187
return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID
189
CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32),
191
CreateProcess.errcheck = ErrCheckCreateProcess
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
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
205
INVALID_HANDLE_VALUE = HANDLE(-1) # From winbase.h
207
# Self Defined Constants for IOPort <--> Job Object communication
208
COMPKEY_TERMINATE = c_ulong(0)
209
COMPKEY_JOBOBJECT = c_ulong(1)
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
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
229
DEBUG_ONLY_THIS_PROCESS = 0x00000002
230
DEBUG_PROCESS = 0x00000001
231
DETACHED_PROCESS = 0x00000008
233
# GetQueuedCompletionPortStatus - http://msdn.microsoft.com/en-us/library/aa364986%28v=vs.85%29.aspx
234
GetQueuedCompletionStatusProto = WINFUNCTYPE(BOOL, # Return Type
235
HANDLE, # Completion Port
237
LPULONG, # Completion Key
238
LPULONG, # PID Returned from the call (may be null)
239
DWORD) # milliseconds to wait
240
GetQueuedCompletionStatusFlags = ((1, "CompletionPort", INVALID_HANDLE_VALUE),
241
(1, "lpNumberOfBytes", None),
242
(1, "lpCompletionKey", None),
244
(1, "dwMilliseconds", 0))
245
GetQueuedCompletionStatus = GetQueuedCompletionStatusProto(("GetQueuedCompletionStatus",
247
GetQueuedCompletionStatusFlags)
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 Port
254
c_ulong, # Completion Key
255
DWORD # Number of Threads
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",
263
CreateIoCompletionPortFlags)
264
CreateIoCompletionPort.errcheck = ErrCheckHandle
266
# SetInformationJobObject
267
SetInformationJobObjectProto = WINFUNCTYPE(BOOL, # Return Type
269
DWORD, # Type of Class next param is
270
LPVOID, # Job Object Class
271
DWORD # Job Object Class Length
273
SetInformationJobObjectProtoFlags = ((1, "hJob", None),
274
(1, "JobObjectInfoClass", None),
275
(1, "lpJobObjectInfo", None),
276
(1, "cbJobObjectInfoLength", 0))
277
SetInformationJobObject = SetInformationJobObjectProto(("SetInformationJobObject",
279
SetInformationJobObjectProtoFlags)
280
SetInformationJobObject.errcheck = ErrCheckBool
283
CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type
284
LPVOID, # lpJobAttributes
288
CreateJobObjectFlags = ((1, "lpJobAttributes", None),
291
CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32),
292
CreateJobObjectFlags)
293
CreateJobObject.errcheck = ErrCheckHandle
295
# AssignProcessToJobObject()
297
AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type
301
AssignProcessToJobObjectFlags = ((1, "hJob"),
303
AssignProcessToJobObject = AssignProcessToJobObjectProto(
304
("AssignProcessToJobObject", windll.kernel32),
305
AssignProcessToJobObjectFlags)
306
AssignProcessToJobObject.errcheck = ErrCheckBool
308
# GetCurrentProcess()
309
# because os.getPid() is way too easy
310
GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type
312
GetCurrentProcessFlags = ()
313
GetCurrentProcess = GetCurrentProcessProto(
314
("GetCurrentProcess", windll.kernel32),
315
GetCurrentProcessFlags)
316
GetCurrentProcess.errcheck = ErrCheckHandle
320
IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type
321
HANDLE, # Process Handle
325
IsProcessInJobFlags = ((1, "ProcessHandle"),
326
(1, "JobHandle", HANDLE(0)),
328
IsProcessInJob = IsProcessInJobProto(
329
("IsProcessInJob", windll.kernel32),
331
IsProcessInJob.errcheck = ErrCheckBool
332
except AttributeError:
333
# windows 2k doesn't have this API
334
def IsProcessInJob(process):
340
def ErrCheckResumeThread(result, func, args):
346
ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type
349
ResumeThreadFlags = ((1, "hThread"),)
350
ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32),
352
ResumeThread.errcheck = ErrCheckResumeThread
356
TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type
360
TerminateProcessFlags = ((1, "hProcess"),
361
(1, "uExitCode", 127))
362
TerminateProcess = TerminateProcessProto(
363
("TerminateProcess", windll.kernel32),
364
TerminateProcessFlags)
365
TerminateProcess.errcheck = ErrCheckBool
367
# TerminateJobObject()
369
TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type
373
TerminateJobObjectFlags = ((1, "hJob"),
374
(1, "uExitCode", 127))
375
TerminateJobObject = TerminateJobObjectProto(
376
("TerminateJobObject", windll.kernel32),
377
TerminateJobObjectFlags)
378
TerminateJobObject.errcheck = ErrCheckBool
380
# WaitForSingleObject()
382
WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type
384
DWORD, # dwMilliseconds
386
WaitForSingleObjectFlags = ((1, "hHandle"),
387
(1, "dwMilliseconds", -1))
388
WaitForSingleObject = WaitForSingleObjectProto(
389
("WaitForSingleObject", windll.kernel32),
390
WaitForSingleObjectFlags)
392
# http://msdn.microsoft.com/en-us/library/ms681381%28v=vs.85%29.aspx
394
WAIT_TIMEOUT = 0x0102
396
WAIT_ABANDONED = 0x0080
398
# http://msdn.microsoft.com/en-us/library/ms683189%28VS.85%29.aspx
401
# Used when we terminate a process.
402
ERROR_CONTROL_C_EXIT = 0x23c
404
# GetExitCodeProcess()
406
GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type
408
LPDWORD, # lpExitCode
410
GetExitCodeProcessFlags = ((1, "hProcess"),
412
GetExitCodeProcess = GetExitCodeProcessProto(
413
("GetExitCodeProcess", windll.kernel32),
414
GetExitCodeProcessFlags)
415
GetExitCodeProcess.errcheck = ErrCheckBool
417
def CanCreateJobObject():
418
currentProc = GetCurrentProcess()
419
if IsProcessInJob(currentProc):
420
jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitInformation')
421
limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
422
return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitflags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
426
### testing functions
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"
434
assert CanCreateJobObject()
435
print 'File: %s' % __file__
436
command = [sys.executable, __file__, '-child']
437
print 'Running command: %s' % command
438
process = Popen(command)
440
code = process.returncode
441
print 'Child code: %s' % code
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')
453
jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation')
454
print 'Job info: %s' % jobinfo
455
limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
456
print 'LimitFlags: %s' % limitflags