addon-sdk/source/python-lib/mozrunner/qijo.py

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong
michael@0 6 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER
michael@0 7
michael@0 8 LPVOID = c_void_p
michael@0 9 LPDWORD = POINTER(DWORD)
michael@0 10 SIZE_T = c_size_t
michael@0 11 ULONG_PTR = POINTER(c_ulong)
michael@0 12
michael@0 13 # A ULONGLONG is a 64-bit unsigned integer.
michael@0 14 # Thus there are 8 bytes in a ULONGLONG.
michael@0 15 # XXX why not import c_ulonglong ?
michael@0 16 ULONGLONG = BYTE * 8
michael@0 17
michael@0 18 class IO_COUNTERS(Structure):
michael@0 19 # The IO_COUNTERS struct is 6 ULONGLONGs.
michael@0 20 # TODO: Replace with non-dummy fields.
michael@0 21 _fields_ = [('dummy', ULONGLONG * 6)]
michael@0 22
michael@0 23 class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure):
michael@0 24 _fields_ = [('TotalUserTime', LARGE_INTEGER),
michael@0 25 ('TotalKernelTime', LARGE_INTEGER),
michael@0 26 ('ThisPeriodTotalUserTime', LARGE_INTEGER),
michael@0 27 ('ThisPeriodTotalKernelTime', LARGE_INTEGER),
michael@0 28 ('TotalPageFaultCount', DWORD),
michael@0 29 ('TotalProcesses', DWORD),
michael@0 30 ('ActiveProcesses', DWORD),
michael@0 31 ('TotalTerminatedProcesses', DWORD)]
michael@0 32
michael@0 33 class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure):
michael@0 34 _fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
michael@0 35 ('IoInfo', IO_COUNTERS)]
michael@0 36
michael@0 37 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx
michael@0 38 class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure):
michael@0 39 _fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER),
michael@0 40 ('PerJobUserTimeLimit', LARGE_INTEGER),
michael@0 41 ('LimitFlags', DWORD),
michael@0 42 ('MinimumWorkingSetSize', SIZE_T),
michael@0 43 ('MaximumWorkingSetSize', SIZE_T),
michael@0 44 ('ActiveProcessLimit', DWORD),
michael@0 45 ('Affinity', ULONG_PTR),
michael@0 46 ('PriorityClass', DWORD),
michael@0 47 ('SchedulingClass', DWORD)
michael@0 48 ]
michael@0 49
michael@0 50 # see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx
michael@0 51 class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure):
michael@0 52 _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION),
michael@0 53 ('IoInfo', IO_COUNTERS),
michael@0 54 ('ProcessMemoryLimit', SIZE_T),
michael@0 55 ('JobMemoryLimit', SIZE_T),
michael@0 56 ('PeakProcessMemoryUsed', SIZE_T),
michael@0 57 ('PeakJobMemoryUsed', SIZE_T)]
michael@0 58
michael@0 59 # XXX Magical numbers like 8 should be documented
michael@0 60 JobObjectBasicAndIoAccountingInformation = 8
michael@0 61
michael@0 62 # ...like magical number 9 comes from
michael@0 63 # http://community.flexerasoftware.com/archive/index.php?t-181670.html
michael@0 64 # I wish I had a more canonical source
michael@0 65 JobObjectExtendedLimitInformation = 9
michael@0 66
michael@0 67 class JobObjectInfo(object):
michael@0 68 mapping = { 'JobObjectBasicAndIoAccountingInformation': 8,
michael@0 69 'JobObjectExtendedLimitInformation': 9
michael@0 70 }
michael@0 71 structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION,
michael@0 72 9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION
michael@0 73 }
michael@0 74 def __init__(self, _class):
michael@0 75 if isinstance(_class, basestring):
michael@0 76 assert _class in self.mapping, 'Class should be one of %s; you gave %s' % (self.mapping, _class)
michael@0 77 _class = self.mapping[_class]
michael@0 78 assert _class in self.structures, 'Class should be one of %s; you gave %s' % (self.structures, _class)
michael@0 79 self.code = _class
michael@0 80 self.info = self.structures[_class]()
michael@0 81
michael@0 82
michael@0 83 QueryInformationJobObjectProto = WINFUNCTYPE(
michael@0 84 BOOL, # Return type
michael@0 85 HANDLE, # hJob
michael@0 86 DWORD, # JobObjectInfoClass
michael@0 87 LPVOID, # lpJobObjectInfo
michael@0 88 DWORD, # cbJobObjectInfoLength
michael@0 89 LPDWORD # lpReturnLength
michael@0 90 )
michael@0 91
michael@0 92 QueryInformationJobObjectFlags = (
michael@0 93 (1, 'hJob'),
michael@0 94 (1, 'JobObjectInfoClass'),
michael@0 95 (1, 'lpJobObjectInfo'),
michael@0 96 (1, 'cbJobObjectInfoLength'),
michael@0 97 (1, 'lpReturnLength', None)
michael@0 98 )
michael@0 99
michael@0 100 _QueryInformationJobObject = QueryInformationJobObjectProto(
michael@0 101 ('QueryInformationJobObject', windll.kernel32),
michael@0 102 QueryInformationJobObjectFlags
michael@0 103 )
michael@0 104
michael@0 105 class SubscriptableReadOnlyStruct(object):
michael@0 106 def __init__(self, struct):
michael@0 107 self._struct = struct
michael@0 108
michael@0 109 def _delegate(self, name):
michael@0 110 result = getattr(self._struct, name)
michael@0 111 if isinstance(result, Structure):
michael@0 112 return SubscriptableReadOnlyStruct(result)
michael@0 113 return result
michael@0 114
michael@0 115 def __getitem__(self, name):
michael@0 116 match = [fname for fname, ftype in self._struct._fields_
michael@0 117 if fname == name]
michael@0 118 if match:
michael@0 119 return self._delegate(name)
michael@0 120 raise KeyError(name)
michael@0 121
michael@0 122 def __getattr__(self, name):
michael@0 123 return self._delegate(name)
michael@0 124
michael@0 125 def QueryInformationJobObject(hJob, JobObjectInfoClass):
michael@0 126 jobinfo = JobObjectInfo(JobObjectInfoClass)
michael@0 127 result = _QueryInformationJobObject(
michael@0 128 hJob=hJob,
michael@0 129 JobObjectInfoClass=jobinfo.code,
michael@0 130 lpJobObjectInfo=addressof(jobinfo.info),
michael@0 131 cbJobObjectInfoLength=sizeof(jobinfo.info)
michael@0 132 )
michael@0 133 if not result:
michael@0 134 raise WinError()
michael@0 135 return SubscriptableReadOnlyStruct(jobinfo.info)
michael@0 136
michael@0 137 def test_qijo():
michael@0 138 from killableprocess import Popen
michael@0 139
michael@0 140 popen = Popen('c:\\windows\\notepad.exe')
michael@0 141
michael@0 142 try:
michael@0 143 result = QueryInformationJobObject(0, 8)
michael@0 144 raise AssertionError('throw should occur')
michael@0 145 except WindowsError, e:
michael@0 146 pass
michael@0 147
michael@0 148 try:
michael@0 149 result = QueryInformationJobObject(0, 1)
michael@0 150 raise AssertionError('throw should occur')
michael@0 151 except NotImplementedError, e:
michael@0 152 pass
michael@0 153
michael@0 154 result = QueryInformationJobObject(popen._job, 8)
michael@0 155 if result['BasicInfo']['ActiveProcesses'] != 1:
michael@0 156 raise AssertionError('expected ActiveProcesses to be 1')
michael@0 157 popen.kill()
michael@0 158
michael@0 159 result = QueryInformationJobObject(popen._job, 8)
michael@0 160 if result.BasicInfo.ActiveProcesses != 0:
michael@0 161 raise AssertionError('expected ActiveProcesses to be 0')
michael@0 162
michael@0 163 if __name__ == '__main__':
michael@0 164 print "testing."
michael@0 165 test_qijo()
michael@0 166 print "success!"

mercurial