| /* |
| * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| */ |
| #include "process_handles.h" |
| |
| 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) |
| { |
| return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); |
| } |
| |
| PyObject * |
| psutil_get_open_files(long dwPid, HANDLE hProcess) |
| { |
| OSVERSIONINFO osvi; |
| |
| ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); |
| osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
| GetVersionEx(&osvi); |
| |
| // Threaded version only works for Vista+ |
| if (osvi.dwMajorVersion >= 6) |
| return psutil_get_open_files_ntqueryobject(dwPid, hProcess); |
| else |
| return psutil_get_open_files_getmappedfilename(dwPid, hProcess); |
| } |
| |
| VOID |
| psutil_get_open_files_init(BOOL threaded) |
| { |
| 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 |
| if (threaded == TRUE) { |
| 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_ntqueryobject(long dwPid, HANDLE hProcess) |
| { |
| NTSTATUS status; |
| PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; |
| DWORD dwInfoSize = 0x10000; |
| DWORD dwRet = 0; |
| 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(TRUE); |
| |
| // Due to the use of global variables, ensure only 1 call |
| // to psutil_get_open_files() is running |
| EnterCriticalSection(&g_cs); |
| |
| if (__NtQuerySystemInformation == NULL || |
| __NtQueryObject == NULL || |
| g_hEvtStart == NULL || |
| g_hEvtFinish == NULL) |
| |
| { |
| PyErr_SetFromWindowsErr(0); |
| error = TRUE; |
| goto cleanup; |
| } |
| |
| // Py_BuildValue raises an exception if NULL is returned |
| pyListFiles = PyList_New(0); |
| if (pyListFiles == NULL) { |
| error = TRUE; |
| goto cleanup; |
| } |
| |
| do { |
| if (pHandleInfo != NULL) { |
| HeapFree(GetProcessHeap(), 0, pHandleInfo); |
| pHandleInfo = NULL; |
| } |
| |
| // NtQuerySystemInformation won't give us the correct buffer size, |
| // so we guess by doubling the buffer size. |
| dwInfoSize *= 2; |
| pHandleInfo = HeapAlloc(GetProcessHeap(), |
| HEAP_ZERO_MEMORY, |
| dwInfoSize); |
| |
| if (pHandleInfo == NULL) { |
| PyErr_NoMemory(); |
| error = TRUE; |
| goto cleanup; |
| } |
| } while ((status = __NtQuerySystemInformation( |
| SystemExtendedHandleInformation, |
| pHandleInfo, |
| dwInfoSize, |
| &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); |
| |
| // 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(), |
| &g_hFile, |
| 0, |
| TRUE, |
| DUPLICATE_SAME_ACCESS)) |
| { |
| /* |
| printf("[%d] DuplicateHandle (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| goto loop_cleanup; |
| } |
| |
| // Guess buffer size is MAX_PATH + 1 |
| g_dwLength = (MAX_PATH+1) * sizeof(WCHAR); |
| |
| do { |
| // 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 |
| // WinXP edge case puts g_dwLength == 0, just skip this handle |
| if (g_dwLength == 0) |
| goto loop_cleanup; |
| |
| g_dwSize = g_dwLength; |
| if (g_dwSize > 0) { |
| g_pNameBuffer = HeapAlloc(GetProcessHeap(), |
| HEAP_ZERO_MEMORY, |
| g_dwSize); |
| |
| if (g_pNameBuffer == NULL) |
| goto loop_cleanup; |
| } |
| |
| 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); |
| |
| // 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) { |
| /* |
| printf("[%d] Filename (%#x) %#d bytes: %S\n", |
| dwPid, |
| hHandle->HandleValue, |
| g_pNameBuffer->Length, |
| g_pNameBuffer->Buffer); |
| */ |
| |
| pyFilePath = PyUnicode_FromWideChar(g_pNameBuffer->Buffer, |
| g_pNameBuffer->Length/2); |
| if (pyFilePath == NULL) { |
| /* |
| printf("[%d] PyUnicode_FromWideChar (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| error = TRUE; |
| goto loop_cleanup; |
| } |
| |
| if (PyList_Append(pyListFiles, pyFilePath)) { |
| /* |
| printf("[%d] PyList_Append (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| error = TRUE; |
| goto loop_cleanup; |
| } |
| } |
| |
| loop_cleanup: |
| Py_XDECREF(pyFilePath); |
| pyFilePath = NULL; |
| |
| if (g_pNameBuffer != NULL) |
| HeapFree(GetProcessHeap(), 0, g_pNameBuffer); |
| g_pNameBuffer = NULL; |
| g_dwSize = 0; |
| g_dwLength = 0; |
| |
| if (g_hFile != NULL) |
| CloseHandle(g_hFile); |
| g_hFile = NULL; |
| } |
| |
| cleanup: |
| if (g_pNameBuffer != NULL) |
| HeapFree(GetProcessHeap(), 0, g_pNameBuffer); |
| g_pNameBuffer = NULL; |
| g_dwSize = 0; |
| g_dwLength = 0; |
| |
| if (g_hFile != NULL) |
| CloseHandle(g_hFile); |
| g_hFile = NULL; |
| |
| if (pHandleInfo != NULL) |
| HeapFree(GetProcessHeap(), 0, pHandleInfo); |
| pHandleInfo = NULL; |
| |
| 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); |
| WaitForSingleObject(g_hThread, INFINITE); |
| CloseHandle(g_hThread); |
| |
| // Cleanup Fiber |
| if (g_fiber != NULL) |
| DeleteFiber(g_fiber); |
| g_fiber = NULL; |
| |
| 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); |
| } |
| } |
| |
| PyObject * |
| psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) |
| { |
| NTSTATUS status; |
| PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; |
| DWORD dwInfoSize = 0x10000; |
| DWORD dwRet = 0; |
| PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; |
| HANDLE hFile = NULL; |
| HANDLE hMap = NULL; |
| DWORD i = 0; |
| BOOLEAN error = FALSE; |
| PyObject* pyListFiles = NULL; |
| PyObject* pyFilePath = NULL; |
| ULONG dwSize = 0; |
| LPVOID pMem = NULL; |
| TCHAR pszFilename[MAX_PATH+1]; |
| |
| if (g_initialized == FALSE) |
| psutil_get_open_files_init(FALSE); |
| |
| if (__NtQuerySystemInformation == NULL || __NtQueryObject == NULL) { |
| PyErr_SetFromWindowsErr(0); |
| error = TRUE; |
| goto cleanup; |
| } |
| |
| // Py_BuildValue raises an exception if NULL is returned |
| pyListFiles = PyList_New(0); |
| if (pyListFiles == NULL) { |
| error = TRUE; |
| goto cleanup; |
| } |
| |
| do { |
| if (pHandleInfo != NULL) { |
| HeapFree(GetProcessHeap(), 0, pHandleInfo); |
| pHandleInfo = NULL; |
| } |
| |
| // NtQuerySystemInformation won't give us the correct buffer size, |
| // so we guess by doubling the buffer size. |
| dwInfoSize *= 2; |
| pHandleInfo = HeapAlloc(GetProcessHeap(), |
| HEAP_ZERO_MEMORY, |
| dwInfoSize); |
| |
| if (pHandleInfo == NULL) { |
| PyErr_NoMemory(); |
| error = TRUE; |
| goto cleanup; |
| } |
| } while ((status = __NtQuerySystemInformation( |
| SystemExtendedHandleInformation, |
| pHandleInfo, |
| dwInfoSize, |
| &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); |
| |
| // 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(), |
| &hFile, |
| 0, |
| TRUE, |
| DUPLICATE_SAME_ACCESS)) |
| { |
| /* |
| printf("[%d] DuplicateHandle (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| goto loop_cleanup; |
| } |
| |
| hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); |
| if (hMap == NULL) { |
| /* |
| printf("[%d] CreateFileMapping (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| goto loop_cleanup; |
| } |
| |
| pMem = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 1); |
| |
| if (pMem == NULL) { |
| /* |
| printf("[%d] MapViewOfFile (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| goto loop_cleanup; |
| } |
| |
| dwSize = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH); |
| if (dwSize == 0) { |
| /* |
| printf("[%d] GetMappedFileName (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| goto loop_cleanup; |
| } |
| |
| pszFilename[dwSize] = '\0'; |
| /* |
| printf("[%d] Filename (%#x) %#d bytes: %S\n", |
| dwPid, |
| hHandle->HandleValue, |
| dwSize, |
| pszFilename); |
| */ |
| |
| pyFilePath = PyUnicode_FromWideChar(pszFilename, dwSize); |
| if (pyFilePath == NULL) { |
| /* |
| printf("[%d] PyUnicode_FromStringAndSize (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| error = TRUE; |
| goto loop_cleanup; |
| } |
| |
| if (PyList_Append(pyListFiles, pyFilePath)) { |
| /* |
| printf("[%d] PyList_Append (%#x): %#x \n", |
| dwPid, |
| hHandle->HandleValue, |
| GetLastError()); |
| */ |
| error = TRUE; |
| goto loop_cleanup; |
| } |
| |
| loop_cleanup: |
| Py_XDECREF(pyFilePath); |
| pyFilePath = NULL; |
| |
| if (pMem != NULL) |
| UnmapViewOfFile(pMem); |
| pMem = NULL; |
| |
| if (hMap != NULL) |
| CloseHandle(hMap); |
| hMap = NULL; |
| |
| if (hFile != NULL) |
| CloseHandle(hFile); |
| hFile = NULL; |
| |
| dwSize = 0; |
| } |
| |
| cleanup: |
| if (pMem != NULL) |
| UnmapViewOfFile(pMem); |
| pMem = NULL; |
| |
| if (hMap != NULL) |
| CloseHandle(hMap); |
| hMap = NULL; |
| |
| if (hFile != NULL) |
| CloseHandle(hFile); |
| hFile = NULL; |
| |
| if (pHandleInfo != NULL) |
| HeapFree(GetProcessHeap(), 0, pHandleInfo); |
| pHandleInfo = NULL; |
| |
| if (error) { |
| Py_XDECREF(pyListFiles); |
| pyListFiles = NULL; |
| } |
| |
| return pyListFiles; |
| } |