winprocess.py 18 KB

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