summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--psutil/_psutil_windows.c8
-rw-r--r--psutil/_pswindows.py8
-rw-r--r--psutil/arch/windows/ntextapi.h90
-rw-r--r--psutil/arch/windows/process_handles.c819
-rw-r--r--psutil/arch/windows/process_handles.h103
5 files changed, 619 insertions, 409 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index a45124e7..b05db820 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -2500,14 +2500,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 2ca96106..af77e247 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -444,10 +444,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..2840b613 100644
--- a/psutil/arch/windows/process_handles.c
+++ b/psutil/arch/windows/process_handles.c
@@ -4,380 +4,559 @@
* found in the LICENSE file.
*
*/
+#include "process_handles.h"
-#ifndef UNICODE
-#define UNICODE
-#endif
+static _NtQuerySystemInformation __NtQuerySystemInformation = NULL;
+static _NtQueryObject __NtQueryObject = NULL;
-#include <Python.h>
-#include <stdio.h>
-#include <windows.h>
-#include <strsafe.h>
-#include "process_handles.h"
+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;
-#ifndef NT_SUCCESS
-#define NT_SUCCESS(x) ((x) >= 0)
-#endif
-#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
+PVOID
+GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
+{
+ return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
+}
-#include <winternl.h>
-#define ObjectBasicInformation 0
-#define ObjectNameInformation 1
-#define ObjectTypeInformation 2
+PyObject *
+psutil_get_open_files(long dwPid, HANDLE hProcess)
+{
+ OSVERSIONINFO osvi;
-#define HANDLE_TYPE_FILE 28
+ 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);
+}
-typedef LONG NTSTATUS;
+VOID
+psutil_get_open_files_init(BOOL threaded)
+{
+ if (g_initialized == TRUE)
+ return;
-typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
- ULONG SystemInformationClass,
- PVOID SystemInformation,
- ULONG SystemInformationLength,
- PULONG ReturnLength
-);
+ // 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;
+}
-typedef NTSTATUS (NTAPI *_NtDuplicateObject)(
- HANDLE SourceProcessHandle,
- HANDLE SourceHandle,
- HANDLE TargetProcessHandle,
- PHANDLE TargetHandle,
- ACCESS_MASK DesiredAccess,
- ULONG Attributes,
- ULONG Options
-);
+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;
+ }
-typedef NTSTATUS (NTAPI *_NtQueryObject)(
- HANDLE ObjectHandle,
- ULONG ObjectInformationClass,
- PVOID ObjectInformation,
- ULONG ObjectInformationLength,
- PULONG ReturnLength
-);
+ // 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;
+ }
-// Undocumented FILE_INFORMATION_CLASS: FileNameInformation
-const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64;
+ // 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);
-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;
+ 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;
+ }
-PVOID
-GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName)
-{
- return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
+ 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;
}
-void PrintError(LPTSTR lpszFunction)
+DWORD
+psutil_NtQueryObject()
{
- // 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);
+ 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;
}
-PyObject *
-psutil_get_open_files(long pid, HANDLE processHandle)
+void
+psutil_NtQueryObjectThread()
{
- _NtQuerySystemInformation NtQuerySystemInformation =
- GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation");
+ // Prevent the thread stack from leaking when this
+ // thread gets terminated due to NTQueryObject hanging
+ g_fiber = ConvertThreadToFiber(NULL);
- _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;
+ // 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);
}
+}
- // 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)
+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)
{
- 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;
+ // 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;
+ // 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(processHandle,
- handle->HandleValue,
+ if (!DuplicateHandle(hProcess,
+ hHandle->HandleValue,
GetCurrentProcess(),
- &dupHandle,
+ &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;
+ /*
+ printf("[%d] DuplicateHandle (%#x): %#x \n",
+ dwPid,
+ hHandle->HandleValue,
+ GetLastError());
+ */
+ goto loop_cleanup;
}
- objectNameInfo = malloc(0x1000);
- if (!NT_SUCCESS(NtQueryObject(
- dupHandle,
- ObjectNameInformation,
- objectNameInfo,
- 0x1000,
- &returnLength
- )))
+ hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hMap == NULL)
{
- // Reallocate the buffer and try again.
- objectNameInfo = realloc(objectNameInfo, returnLength);
- if (!NT_SUCCESS(NtQueryObject(
- dupHandle,
- ObjectNameInformation,
- objectNameInfo,
- returnLength,
- NULL
- )))
- {
- // 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;
+ /*
+ 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;
}
- // Cast our buffer into an UNICODE_STRING.
- objectName = *(PUNICODE_STRING)objectNameInfo;
+ dwSize = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH);
+ if (dwSize == 0)
+ {
+ /*
+ printf("[%d] GetMappedFileName (%#x): %#x \n",
+ dwPid,
+ hHandle->HandleValue,
+ GetLastError());
+ */
+ goto loop_cleanup;
+ }
- // Print the information!
- if (objectName.Length)
+ pszFilename[dwSize] = '\0';
+ /*
+ printf("[%d] Filename (%#x) %#d bytes: %S\n",
+ dwPid,
+ hHandle->HandleValue,
+ dwSize,
+ pszFilename);
+ */
+
+ pyFilePath = PyUnicode_FromWideChar(pszFilename, dwSize);
+ if (pyFilePath == NULL)
{
- // 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);
- }
/*
- printf(
- "[%#x] %.*S: %.*S\n",
- handle->Handle,
- objectTypeInfo->Name.Length / 2,
- objectTypeInfo->Name.Buffer,
- objectName.Length / 2,
- objectName.Buffer
- );
+ printf("[%d] PyUnicode_FromStringAndSize (%#x): %#x \n",
+ dwPid,
+ hHandle->HandleValue,
+ GetLastError());
*/
+ error = TRUE;
+ goto loop_cleanup;
}
- else
+
+ if (PyList_Append(pyListFiles, pyFilePath))
{
- // Print something else.
/*
- printf(
- "[%#x] %.*S: (unnamed)\n",
- handle->Handle,
- objectTypeInfo->Name.Length / 2,
- objectTypeInfo->Name.Buffer
- );
+ printf("[%d] PyList_Append (%#x): %#x \n",
+ dwPid,
+ hHandle->HandleValue,
+ GetLastError());
*/
- ;;
+ error = TRUE;
+ goto loop_cleanup;
}
- free(objectTypeInfo);
- free(objectNameInfo);
- CloseHandle(dupHandle);
+
+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;
}
- HeapFree(GetProcessHeap(), 0, handleInfo);
- CloseHandle(processHandle);
- return filesList;
-
-error_py_fun:
- Py_XDECREF(arg);
- Py_XDECREF(fileFromWchar);
- Py_DECREF(filesList);
- return NULL;
+
+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;
}
diff --git a/psutil/arch/windows/process_handles.h b/psutil/arch/windows/process_handles.h
index 342ce8fd..4cf4023e 100644
--- a/psutil/arch/windows/process_handles.h
+++ b/psutil/arch/windows/process_handles.h
@@ -4,7 +4,110 @@
* 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>
+#include <psapi.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(BOOL threaded);
PyObject* psutil_get_open_files(long pid, HANDLE processHandle);
+PyObject* psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess);
+PyObject* psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess);
+DWORD psutil_NtQueryObject(void);
+void psutil_NtQueryObjectThread(void);
+
+#endif // __PROCESS_HANDLES_H__