diff options
| author | Jeff Tang <mrjefftang@gmail.com> | 2015-02-23 11:30:54 -0500 |
|---|---|---|
| committer | Jeff Tang <mrjefftang@gmail.com> | 2015-03-11 15:40:27 -0400 |
| commit | c813e6d62a0e7ff609809e592aba8ae105fbdc06 (patch) | |
| tree | 4494feea09e18ba417324608e7c2679265dfbaa2 /psutil | |
| parent | c2ed8b081681d316acc22fd3148e9f38acf57536 (diff) | |
| download | psutil-c813e6d62a0e7ff609809e592aba8ae105fbdc06.tar.gz | |
Rewrite get_open_files to use a worker thread
get_open_files spawns a worker thread to process calls to
NtQueryObject().
If the call timesout (100ms), the thread is terminated and the file
handle is skipped. The ConvertThreadToFiber call prevents the initial
stack from leaking when the thread is terminated on Windows XP/2003.
The code has been cleaned up and struct redefinitions in ntextapi have
been removed and with conflicts using #define's.
Diffstat (limited to 'psutil')
| -rw-r--r-- | psutil/_psutil_windows.c | 8 | ||||
| -rw-r--r-- | psutil/_pswindows.py | 8 | ||||
| -rw-r--r-- | psutil/arch/windows/ntextapi.h | 90 | ||||
| -rw-r--r-- | psutil/arch/windows/process_handles.c | 579 | ||||
| -rw-r--r-- | psutil/arch/windows/process_handles.h | 99 |
5 files changed, 358 insertions, 426 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 4a1af0f9..9d14156e 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -2625,14 +2625,6 @@ error: return NULL; } - -#ifdef UNICODE -#define WTSOpenServer WTSOpenServerW -#else -#define WTSOpenServer WTSOpenServerA -#endif - - /* * Return a Python dict of tuples for disk I/O information */ diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 1fa1181a..ac6100bf 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -430,10 +430,10 @@ class Process(object): # Convert the first part in the corresponding drive letter # (e.g. "C:\") by using Windows's QueryDosDevice() raw_file_names = cext.proc_open_files(self.pid) - for file in raw_file_names: - file = _convert_raw_path(file) - if isfile_strict(file) and file not in retlist: - ntuple = _common.popenfile(file, -1) + for _file in raw_file_names: + _file = _convert_raw_path(_file) + if isfile_strict(_file) and _file not in retlist: + ntuple = _common.popenfile(_file, -1) retlist.append(ntuple) return retlist diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index d1aa62df..d10432a3 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -3,9 +3,9 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - #if !defined(__NTEXTAPI_H__) #define __NTEXTAPI_H__ +#include <winternl.h> typedef enum _KTHREAD_STATE { Initialized, @@ -66,23 +66,6 @@ typedef struct _CLIENT_ID { HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { - LARGE_INTEGER BootTime; - LARGE_INTEGER CurrentTime; - LARGE_INTEGER TimeZoneBias; - ULONG TimeZoneId; - ULONG Reserved; - ULONGLONG BootTimeBias; - ULONGLONG SleepTimeBias; -} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; - typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; @@ -111,7 +94,7 @@ typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { ULONG_PTR Reserved4; } SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; -typedef struct _SYSTEM_PROCESS_INFORMATION { +typedef struct _SYSTEM_PROCESS_INFORMATION2 { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; @@ -146,31 +129,10 @@ typedef struct _SYSTEM_PROCESS_INFORMATION { LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SYSTEM_THREAD_INFORMATION Threads[1]; -} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; - - -// structures and enums from winternl.h (not available under mingw) -typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER Reserved1[2]; - ULONG Reserved2; -} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, - *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; +} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2; - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemBasicInformation = 0, - SystemPerformanceInformation = 2, - SystemTimeOfDayInformation = 3, - SystemProcessInformation = 5, - SystemProcessorPerformanceInformation = 8, - SystemInterruptInformation = 23, - SystemExceptionInformation = 33, - SystemRegistryQuotaInformation = 37, - SystemLookasideInformation = 45 -} SYSTEM_INFORMATION_CLASS; +#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2 +#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2 // ================================================ @@ -189,32 +151,9 @@ typedef struct _WINSTATION_INFO { FILETIME CurrentTime; } WINSTATION_INFO, *PWINSTATION_INFO; -typedef enum _WINSTATIONINFOCLASS { - WinStationInformation = 8 -} WINSTATIONINFOCLASS; - typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW) (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG); -typedef struct _WINSTATIONINFORMATIONW { - BYTE Reserved2[70]; - ULONG LogonId; - BYTE Reserved3[1140]; -} WINSTATIONINFORMATIONW, *PWINSTATIONINFORMATIONW; - -// mingw support: -// http://www.koders.com/c/fid7C02CAE627C526914CDEB427405B51DF393A5EFA.aspx -#ifndef _INC_WTSAPI -typedef struct _WTS_CLIENT_ADDRESS { - DWORD AddressFamily; // AF_INET, AF_IPX, AF_NETBIOS, AF_UNSPEC - BYTE Address[20]; // client network address -} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS; - -HANDLE WINAPI WTSOpenServerA(IN LPSTR pServerName); - -VOID WINAPI WTSCloseServer(IN HANDLE hServer); -#endif - /* * NtQueryInformationProcess code taken from @@ -238,16 +177,9 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( DWORD ProcessInformationLength ); -typedef struct _PROCESS_BASIC_INFORMATION { - PVOID Reserved1; - PVOID PebBaseAddress; - PVOID Reserved2[2]; - ULONG_PTR UniqueProcessId; - PVOID Reserved3; -} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; -typedef enum _PROCESSINFOCLASS { - ProcessBasicInformation, +typedef enum _PROCESSINFOCLASS2 { + _ProcessBasicInformation, ProcessQuotaLimits, ProcessIoCounters, ProcessVmCounters, @@ -273,7 +205,7 @@ typedef enum _PROCESSINFOCLASS { ProcessDeviceMap, ProcessSessionInformation, ProcessForegroundInformation, - ProcessWow64Information, + _ProcessWow64Information, /* added after XP+ */ ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, @@ -287,6 +219,10 @@ typedef enum _PROCESSINFOCLASS { ProcessCookie, ProcessImageInformation, MaxProcessInfoClass -} PROCESSINFOCLASS; +} PROCESSINFOCLASS2; + +#define PROCESSINFOCLASS PROCESSINFOCLASS2 +#define ProcessBasicInformation _ProcessBasicInformation +#define ProcessWow64Information _ProcessWow64Information #endif // __NTEXTAPI_H__ diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c index 6b0a4194..2312e7d9 100644 --- a/psutil/arch/windows/process_handles.c +++ b/psutil/arch/windows/process_handles.c @@ -4,117 +4,22 @@ * found in the LICENSE file. * */ - -#ifndef UNICODE -#define UNICODE -#endif - -#include <Python.h> -#include <stdio.h> -#include <windows.h> -#include <strsafe.h> #include "process_handles.h" -#ifndef NT_SUCCESS -#define NT_SUCCESS(x) ((x) >= 0) -#endif - -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 - -#include <winternl.h> -#define ObjectBasicInformation 0 -#define ObjectNameInformation 1 -#define ObjectTypeInformation 2 - -#define HANDLE_TYPE_FILE 28 - - -typedef LONG NTSTATUS; - -typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( - ULONG SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength -); - -typedef NTSTATUS (NTAPI *_NtDuplicateObject)( - HANDLE SourceProcessHandle, - HANDLE SourceHandle, - HANDLE TargetProcessHandle, - PHANDLE TargetHandle, - ACCESS_MASK DesiredAccess, - ULONG Attributes, - ULONG Options -); - -typedef NTSTATUS (NTAPI *_NtQueryObject)( - HANDLE ObjectHandle, - ULONG ObjectInformationClass, - PVOID ObjectInformation, - ULONG ObjectInformationLength, - PULONG ReturnLength -); - - -// Undocumented FILE_INFORMATION_CLASS: FileNameInformation -const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; - -typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX -{ - PVOID Object; - HANDLE UniqueProcessId; - HANDLE HandleValue; - ULONG GrantedAccess; - USHORT CreatorBackTraceIndex; - USHORT ObjectTypeIndex; - ULONG HandleAttributes; - ULONG Reserved; -} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; - -typedef struct _SYSTEM_HANDLE_INFORMATION_EX -{ - ULONG_PTR NumberOfHandles; - ULONG_PTR Reserved; - SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; -} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; - - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - DontUseThisType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE, *PPOOL_TYPE; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; - +static _NtQuerySystemInformation __NtQuerySystemInformation = NULL; +static _NtQueryObject __NtQueryObject = NULL; + +CRITICAL_SECTION g_cs; +BOOL g_initialized = FALSE; +NTSTATUS g_status; +HANDLE g_hFile = NULL; +HANDLE g_hEvtStart = NULL; +HANDLE g_hEvtFinish = NULL; +HANDLE g_hThread = NULL; +PUNICODE_STRING g_pNameBuffer = NULL; +ULONG g_dwSize = 0; +ULONG g_dwLength = 0; +PVOID g_fiber = NULL; PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) @@ -122,262 +27,262 @@ GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); } -void PrintError(LPTSTR lpszFunction) +VOID +psutil_get_open_files_init(void) { - // Retrieve the system error message for the last-error code - - LPVOID lpMsgBuf; - LPVOID lpDisplayBuf; - DWORD dw = GetLastError(); - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL ); - - // Display the error message - lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, - (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); - StringCchPrintf((LPTSTR)lpDisplayBuf, - LocalSize(lpDisplayBuf) / sizeof(TCHAR), - TEXT("%s failed with error %d: %s"), - lpszFunction, dw, lpMsgBuf); - - wprintf(lpDisplayBuf); - LocalFree(lpMsgBuf); - LocalFree(GetLastError); + if (g_initialized == TRUE) + return; + + // Resolve the Windows API calls + __NtQuerySystemInformation = + GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + __NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + // Create events for signalling work between threads + g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL); + g_hEvtFinish = CreateEvent(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&g_cs); + + g_initialized = TRUE; } PyObject * -psutil_get_open_files(long pid, HANDLE processHandle) +psutil_get_open_files(long dwPid, HANDLE hProcess) { - _NtQuerySystemInformation NtQuerySystemInformation = - GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); - - _NtQueryObject NtQueryObject = - GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); - - NTSTATUS status; - PSYSTEM_HANDLE_INFORMATION_EX handleInfo; - ULONG handleInfoSize = 0x10000; - ULONG i; - ULONG fileNameLength; - PyObject *filesList = Py_BuildValue("[]"); - PyObject *arg = NULL; - PyObject *fileFromWchar = NULL; - DWORD nReturn = 0; - - if (filesList == NULL) - return NULL; - - handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)HeapAlloc(GetProcessHeap(), 0, - handleInfoSize); - - if (handleInfo == NULL) { - Py_DECREF(filesList); - PyErr_NoMemory(); - return NULL; - } - - // NtQuerySystemInformation won't give us the correct buffer size, - // so we guess by doubling the buffer size. - while ((status = NtQuerySystemInformation( - SystemExtendedHandleInformation, - handleInfo, - handleInfoSize, - &nReturn - )) == STATUS_INFO_LENGTH_MISMATCH) + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; + DWORD dwInfoSize = 0x60000; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; + DWORD i = 0; + BOOLEAN error = FALSE; + PyObject* pyListFiles = NULL; + PyObject* pyFilePath = NULL; + DWORD dwWait = 0; + + if (g_initialized == FALSE) + psutil_get_open_files_init(); + + if (__NtQuerySystemInformation == NULL || + __NtQueryObject == NULL || + g_hEvtStart == NULL || + g_hEvtFinish == NULL) { - handleInfoSize *=2; - HeapFree(GetProcessHeap(), 0, handleInfo); - handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)HeapAlloc( - GetProcessHeap(), 0, handleInfoSize); + PyErr_SetFromWindowsErr(0); + error = TRUE; + goto cleanup; } - // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH - if (!NT_SUCCESS(status)) { - Py_DECREF(filesList); - HeapFree(GetProcessHeap(), 0, handleInfo); - return NULL; + // Due to the use of global variables, ensure only 1 call + // to psutil_get_open_files() is running + EnterCriticalSection(&g_cs); + + // Py_BuildValue raises an exception if NULL is returned + pyListFiles = PyList_New(0); + if (pyListFiles == NULL) + { + error = TRUE; + goto cleanup; } - for (i = 0; i < handleInfo->NumberOfHandles; i++) { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handleInfo->Handles[i]; - HANDLE dupHandle = NULL; - HANDLE mapHandle = NULL; - POBJECT_TYPE_INFORMATION objectTypeInfo = NULL; - PVOID objectNameInfo; - UNICODE_STRING objectName; - ULONG returnLength; - DWORD error = 0; - fileFromWchar = NULL; - arg = NULL; - - // Check if this handle belongs to the PID the user specified. - if (handle->UniqueProcessId != (HANDLE)pid || - handle->ObjectTypeIndex != HANDLE_TYPE_FILE) + do + { + if (pHandleInfo != NULL) { - continue; + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; } - // Skip handles with the following access codes as the next call - // to NtDuplicateObject() or NtQueryObject() might hang forever. - if ((handle->GrantedAccess == 0x0012019f) - || (handle->GrantedAccess == 0x001a019f) - || (handle->GrantedAccess == 0x00120189) - || (handle->GrantedAccess == 0x00100000)) { - continue; + pHandleInfo = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwInfoSize); + + if (pHandleInfo == NULL) + { + PyErr_NoMemory(); + error = TRUE; + goto cleanup; } + } while ((status = __NtQuerySystemInformation( + SystemExtendedHandleInformation, + pHandleInfo, + dwInfoSize, + &dwInfoSize)) == STATUS_INFO_LENGTH_MISMATCH); - if (!DuplicateHandle(processHandle, - handle->HandleValue, + // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(status)) + { + PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status)); + error = TRUE; + goto cleanup; + } + + for (i = 0; i < pHandleInfo->NumberOfHandles; i++) + { + hHandle = &pHandleInfo->Handles[i]; + + // Check if this hHandle belongs to the PID the user specified. + if (hHandle->UniqueProcessId != (HANDLE)dwPid || + hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE) + goto loop_cleanup; + + if (!DuplicateHandle(hProcess, + hHandle->HandleValue, GetCurrentProcess(), - &dupHandle, + &g_hFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - //printf("[%#x] Error: %d \n", handle->HandleValue, GetLastError()); - continue; - } - - mapHandle = CreateFileMapping(dupHandle, - NULL, - PAGE_READONLY, - 0, - 0, - NULL); - if (mapHandle == NULL) { - error = GetLastError(); - if (error == ERROR_INVALID_HANDLE || error == ERROR_BAD_EXE_FORMAT) { - CloseHandle(dupHandle); - //printf("CreateFileMapping Error: %d\n", error); - continue; - } - } - CloseHandle(mapHandle); - - // Query the object type. - objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectTypeInformation, - objectTypeInfo, - 0x1000, - NULL - ))) - { - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; - } + goto loop_cleanup; - objectNameInfo = malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - 0x1000, - &returnLength - ))) + do { - // Reallocate the buffer and try again. - objectNameInfo = realloc(objectNameInfo, returnLength); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - returnLength, - NULL - ))) + // Release any previously allocated buffer + if (g_pNameBuffer != NULL) + { + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + } + + // NtQueryObject puts the required buffer size in g_dwLength + g_dwSize = g_dwLength; + if (g_dwSize > 0) { - // We have the type name, so just display that. - /* - printf( - "[%#x] %.*S: (could not get name)\n", - handle->HandleValue, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); - */ - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); - continue; + g_pNameBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + g_dwSize); + if (g_pNameBuffer == NULL) + goto loop_cleanup; } - } - // Cast our buffer into an UNICODE_STRING. - objectName = *(PUNICODE_STRING)objectNameInfo; + dwWait = psutil_NtQueryObject(); + + // If the call does not return, skip this handle + if (dwWait != WAIT_OBJECT_0) + goto loop_cleanup; + + } while (g_status == STATUS_INFO_LENGTH_MISMATCH); - // Print the information! - if (objectName.Length) + // NtQueryObject stopped returning STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(g_status)) + goto loop_cleanup; + + // Convert to PyUnicode and append it to the return list + if (g_pNameBuffer->Length > 0) { - // The object has a name. Make sure it is a file otherwise - // ignore it - fileNameLength = objectName.Length / 2; - if (wcscmp(objectTypeInfo->Name.Buffer, L"File") == 0) { - // printf("%.*S\n", objectName.Length / 2, objectName.Buffer); - fileFromWchar = PyUnicode_FromWideChar(objectName.Buffer, - fileNameLength); - if (fileFromWchar == NULL) - goto error_py_fun; -#if PY_MAJOR_VERSION >= 3 - arg = Py_BuildValue("N", - PyUnicode_AsUTF8String(fileFromWchar)); -#else - arg = Py_BuildValue("N", - PyUnicode_FromObject(fileFromWchar)); -#endif - if (!arg) - goto error_py_fun; - Py_XDECREF(fileFromWchar); - fileFromWchar = NULL; - if (PyList_Append(filesList, arg)) - goto error_py_fun; - Py_XDECREF(arg); + pyFilePath = PyUnicode_FromWideChar(g_pNameBuffer->Buffer, + g_pNameBuffer->Length/2); + if (pyFilePath == NULL) + { + error = TRUE; + goto loop_cleanup; + } + + if (PyList_Append(pyListFiles, pyFilePath)) + { + error = TRUE; + goto loop_cleanup; } - /* - printf( - "[%#x] %.*S: %.*S\n", - handle->Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer, - objectName.Length / 2, - objectName.Buffer - ); - */ - } - else - { - // Print something else. - /* - printf( - "[%#x] %.*S: (unnamed)\n", - handle->Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); - */ - ;; } - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); + +loop_cleanup: + Py_XDECREF(pyFilePath); + pyFilePath = NULL; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + } + +cleanup: + if (pHandleInfo != NULL) + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + + if (error) + { + Py_XDECREF(pyListFiles); + pyListFiles = NULL; + } + + LeaveCriticalSection(&g_cs); + return pyListFiles; +} + +DWORD +psutil_NtQueryObject() +{ + DWORD dwWait = 0; + + if (g_hThread == NULL) + g_hThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)psutil_NtQueryObjectThread, + NULL, + 0, + NULL); + if (g_hThread == NULL) + return GetLastError(); + + // Signal the worker thread to start + SetEvent(g_hEvtStart); + + // Wait for the worker thread to finish + dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); + + // If the thread hangs, kill it and cleanup + if (dwWait == WAIT_TIMEOUT) + { + SuspendThread(g_hThread); + TerminateThread(g_hThread, 1); + // TerminateThread is async + WaitForSingleObject(g_hThread, INFINITE); + + // Cleanup Fiber + if (g_fiber != NULL) + DeleteFiber(g_fiber); + g_fiber = NULL; + + CloseHandle(g_hThread); + g_hThread = NULL; + } + + return dwWait; +} + +void +psutil_NtQueryObjectThread() +{ + // Prevent the thread stack from leaking when this + // thread gets terminated due to NTQueryObject hanging + g_fiber = ConvertThreadToFiber(NULL); + + // Loop infinitely waiting for work + while (TRUE) + { + WaitForSingleObject(g_hEvtStart, INFINITE); + g_status = __NtQueryObject(g_hFile, + ObjectNameInformation, + g_pNameBuffer, + g_dwSize, + &g_dwLength); + SetEvent(g_hEvtFinish); } - HeapFree(GetProcessHeap(), 0, handleInfo); - CloseHandle(processHandle); - return filesList; - -error_py_fun: - Py_XDECREF(arg); - Py_XDECREF(fileFromWchar); - Py_DECREF(filesList); - return NULL; } diff --git a/psutil/arch/windows/process_handles.h b/psutil/arch/windows/process_handles.h index 342ce8fd..7312de0a 100644 --- a/psutil/arch/windows/process_handles.h +++ b/psutil/arch/windows/process_handles.h @@ -4,7 +4,106 @@ * found in the LICENSE file. */ +#ifndef __PROCESS_HANDLES_H__ +#define __PROCESS_HANDLES_H__ + +#ifndef UNICODE +#define UNICODE +#endif + #include <Python.h> +#include <stdio.h> #include <windows.h> +#include <strsafe.h> +#include <winternl.h> + +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) ((x) >= 0) +#endif + +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +#define ObjectBasicInformation 0 +#define ObjectNameInformation 1 +#define ObjectTypeInformation 2 +#define HANDLE_TYPE_FILE 28 +#define NTQO_TIMEOUT 100 + +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + ULONG ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength +); +// Undocumented FILE_INFORMATION_CLASS: FileNameInformation +static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX +{ + PVOID Object; + HANDLE UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX +{ + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName); +VOID psutil_get_open_files_init(void); PyObject* psutil_get_open_files(long pid, HANDLE processHandle); +DWORD psutil_NtQueryObject(void); +void psutil_NtQueryObjectThread(void); + +#endif // __PROCESS_HANDLES_H__ |
