/* * Copyright (c) 2009, Jay Loden, 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. * * Windows platform-specific module methods for _psutil_windows */ // Fixes clash between winsock2.h and windows.h #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include #include #include #include // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") #include "_psutil_windows.h" #include "_psutil_common.h" #include "arch/windows/security.h" #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" #include "arch/windows/ntextapi.h" #include "arch/windows/inet_ntop.h" #ifdef __MINGW32__ #include "arch/windows/glpi.h" #endif /* * ============================================================================ * Utilities * ============================================================================ */ // a flag for connections without an actual status static int PSUTIL_CONN_NONE = 128; #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) #define LO_T ((float)1e-7) #define HI_T (LO_T*4294967296.0) #define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) #ifndef AF_INET6 #define AF_INET6 23 #endif #define _psutil_conn_decref_objs() \ Py_DECREF(_AF_INET); \ Py_DECREF(_AF_INET6);\ Py_DECREF(_SOCK_STREAM);\ Py_DECREF(_SOCK_DGRAM); typedef BOOL (WINAPI *LPFN_GLPI) (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); // fix for mingw32, see // https://github.com/giampaolo/psutil/issues/351#c2 typedef struct _DISK_PERFORMANCE_WIN_2008 { LARGE_INTEGER BytesRead; LARGE_INTEGER BytesWritten; LARGE_INTEGER ReadTime; LARGE_INTEGER WriteTime; LARGE_INTEGER IdleTime; DWORD ReadCount; DWORD WriteCount; DWORD QueueDepth; DWORD SplitCount; LARGE_INTEGER QueryTime; DWORD StorageDeviceNumber; WCHAR StorageManagerName[8]; } DISK_PERFORMANCE_WIN_2008; // --- network connections mingw32 support #ifndef _IPRTRMIB_H typedef struct _MIB_TCP6ROW_OWNER_PID { UCHAR ucLocalAddr[16]; DWORD dwLocalScopeId; DWORD dwLocalPort; UCHAR ucRemoteAddr[16]; DWORD dwRemoteScopeId; DWORD dwRemotePort; DWORD dwState; DWORD dwOwningPid; } MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; typedef struct _MIB_TCP6TABLE_OWNER_PID { DWORD dwNumEntries; MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; } MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; #endif #ifndef __IPHLPAPI_H__ typedef struct in6_addr { union { UCHAR Byte[16]; USHORT Word[8]; } u; } IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; typedef enum _UDP_TABLE_CLASS { UDP_TABLE_BASIC, UDP_TABLE_OWNER_PID, UDP_TABLE_OWNER_MODULE } UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; typedef struct _MIB_UDPROW_OWNER_PID { DWORD dwLocalAddr; DWORD dwLocalPort; DWORD dwOwningPid; } MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; typedef struct _MIB_UDPTABLE_OWNER_PID { DWORD dwNumEntries; MIB_UDPROW_OWNER_PID table[ANY_SIZE]; } MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; #endif typedef struct _MIB_UDP6ROW_OWNER_PID { UCHAR ucLocalAddr[16]; DWORD dwLocalScopeId; DWORD dwLocalPort; DWORD dwOwningPid; } MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; typedef struct _MIB_UDP6TABLE_OWNER_PID { DWORD dwNumEntries; MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; } MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; PIP_ADAPTER_ADDRESSES psutil_get_nic_addresses() { // allocate a 15 KB buffer to start with int outBufLen = 15000; DWORD dwRetVal = 0; ULONG attempts = 0; PIP_ADAPTER_ADDRESSES pAddresses = NULL; do { pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); if (pAddresses == NULL) { PyErr_NoMemory(); return NULL; } dwRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen); if (dwRetVal == ERROR_BUFFER_OVERFLOW) { free(pAddresses); pAddresses = NULL; } else { break; } attempts++; } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); if (dwRetVal != NO_ERROR) { PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); return NULL; } return pAddresses; } /* * ============================================================================ * Public Python API * ============================================================================ */ /* * Return a Python float representing the system uptime expressed in seconds * since the epoch. */ static PyObject * psutil_boot_time(PyObject *self, PyObject *args) { double uptime; time_t pt; FILETIME fileTime; long long ll; GetSystemTimeAsFileTime(&fileTime); /* HUGE thanks to: http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry This function converts the FILETIME structure to the 32 bit Unix time structure. The time_t is a 32-bit value for the number of seconds since January 1, 1970. A FILETIME is a 64-bit for the number of 100-nanosecond periods since January 1, 1601. Convert by subtracting the number of 100-nanosecond period betwee 01-01-1970 and 01-01-1601, from time_t the divide by 1e+7 to get to the same base granularity. */ ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) \ + fileTime.dwLowDateTime; pt = (time_t)((ll - 116444736000000000ull) / 10000000ull); // XXX - By using GetTickCount() time will wrap around to zero if the // system is run continuously for 49.7 days. uptime = GetTickCount() / 1000.00f; return Py_BuildValue("d", (double)pt - uptime); } /* * Return 1 if PID exists in the current process list, else 0. */ static PyObject * psutil_pid_exists(PyObject *self, PyObject *args) { long pid; int status; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; status = psutil_pid_is_running(pid); if (-1 == status) return NULL; // exception raised in psutil_pid_is_running() return PyBool_FromLong(status); } /* * Return a Python list of all the PIDs running on the system. */ static PyObject * psutil_pids(PyObject *self, PyObject *args) { DWORD *proclist = NULL; DWORD numberOfReturnedPIDs; DWORD i; PyObject *pid = NULL; PyObject *retlist = PyList_New(0); if (retlist == NULL) return NULL; proclist = psutil_get_pids(&numberOfReturnedPIDs); if (proclist == NULL) goto error; for (i = 0; i < numberOfReturnedPIDs; i++) { pid = Py_BuildValue("I", proclist[i]); if (!pid) goto error; if (PyList_Append(retlist, pid)) goto error; Py_DECREF(pid); } // free C array allocated for PIDs free(proclist); return retlist; error: Py_XDECREF(pid); Py_DECREF(retlist); if (proclist != NULL) free(proclist); return NULL; } /* * Kill a process given its PID. */ static PyObject * psutil_proc_kill(PyObject *self, PyObject *args) { HANDLE hProcess; long pid; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (pid == 0) return AccessDenied(); hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); if (hProcess == NULL) { if (GetLastError() == ERROR_INVALID_PARAMETER) { // see https://github.com/giampaolo/psutil/issues/24 NoSuchProcess(); } else { PyErr_SetFromWindowsErr(0); } return NULL; } // kill the process if (! TerminateProcess(hProcess, 0)) { PyErr_SetFromWindowsErr(0); CloseHandle(hProcess); return NULL; } CloseHandle(hProcess); Py_RETURN_NONE; } /* * Wait for process to terminate and return its exit code. */ static PyObject * psutil_proc_wait(PyObject *self, PyObject *args) { HANDLE hProcess; DWORD ExitCode; DWORD retVal; long pid; long timeout; if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) return NULL; if (pid == 0) return AccessDenied(); hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess == NULL) { if (GetLastError() == ERROR_INVALID_PARAMETER) { // no such process; we do not want to raise NSP but // return None instead. Py_RETURN_NONE; } else { PyErr_SetFromWindowsErr(0); return NULL; } } // wait until the process has terminated Py_BEGIN_ALLOW_THREADS retVal = WaitForSingleObject(hProcess, timeout); Py_END_ALLOW_THREADS if (retVal == WAIT_FAILED) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(GetLastError()); } if (retVal == WAIT_TIMEOUT) { CloseHandle(hProcess); return Py_BuildValue("l", WAIT_TIMEOUT); } // get the exit code; note: subprocess module (erroneously?) uses // what returned by WaitForSingleObject if (GetExitCodeProcess(hProcess, &ExitCode) == 0) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(GetLastError()); } CloseHandle(hProcess); #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong((long) ExitCode); #else return PyInt_FromLong((long) ExitCode); #endif } /* * Return a Python tuple (user_time, kernel_time) */ static PyObject * psutil_proc_cpu_times(PyObject *self, PyObject *args) { long pid; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) return NULL; if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { CloseHandle(hProcess); if (GetLastError() == ERROR_ACCESS_DENIED) { // usually means the process has died so we throw a NoSuchProcess // here return NoSuchProcess(); } else { PyErr_SetFromWindowsErr(0); return NULL; } } CloseHandle(hProcess); /* * User and kernel times are represented as a FILETIME structure * wich contains a 64-bit value representing the number of * 100-nanosecond intervals since January 1, 1601 (UTC): * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx * To convert it into a float representing the seconds that the * process has executed in user/kernel mode I borrowed the code * below from Python's Modules/posixmodule.c */ return Py_BuildValue( "(dd)", (double)(ftUser.dwHighDateTime * 429.4967296 + \ ftUser.dwLowDateTime * 1e-7), (double)(ftKernel.dwHighDateTime * 429.4967296 + \ ftKernel.dwLowDateTime * 1e-7) ); } /* * Return a Python float indicating the process create time expressed in * seconds since the epoch. */ static PyObject * psutil_proc_create_time(PyObject *self, PyObject *args) { long pid; long long unix_time; DWORD exitCode; HANDLE hProcess; BOOL ret; FILETIME ftCreate, ftExit, ftKernel, ftUser; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; // special case for PIDs 0 and 4, return system boot time if (0 == pid || 4 == pid) return psutil_boot_time(NULL, NULL); hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) return NULL; if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { CloseHandle(hProcess); if (GetLastError() == ERROR_ACCESS_DENIED) { // usually means the process has died so we throw a // NoSuchProcess here return NoSuchProcess(); } else { PyErr_SetFromWindowsErr(0); return NULL; } } // Make sure the process is not gone as OpenProcess alone seems to be // unreliable in doing so (it seems a previous call to p.wait() makes // it unreliable). // This check is important as creation time is used to make sure the // process is still running. ret = GetExitCodeProcess(hProcess, &exitCode); CloseHandle(hProcess); if (ret != 0) { if (exitCode != STILL_ACTIVE) return NoSuchProcess(); } else { // Ignore access denied as it means the process is still alive. // For all other errors, we want an exception. if (GetLastError() != ERROR_ACCESS_DENIED) { PyErr_SetFromWindowsErr(0); return NULL; } } /* Convert the FILETIME structure to a Unix time. It's the best I could find by googling and borrowing code here and there. The time returned has a precision of 1 second. */ unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; unix_time /= 10000000; return Py_BuildValue("d", (double)unix_time); } /* * Return the number of logical CPUs. */ static PyObject * psutil_cpu_count_logical(PyObject *self, PyObject *args) { SYSTEM_INFO system_info; system_info.dwNumberOfProcessors = 0; GetSystemInfo(&system_info); if (system_info.dwNumberOfProcessors == 0) Py_RETURN_NONE; // mimic os.cpu_count() else return Py_BuildValue("I", system_info.dwNumberOfProcessors); } /* * Return the number of physical CPU cores. */ static PyObject * psutil_cpu_count_phys(PyObject *self, PyObject *args) { LPFN_GLPI glpi; DWORD rc; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; DWORD length = 0; DWORD offset = 0; int ncpus = 0; glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); if (glpi == NULL) goto return_none; while (1) { rc = glpi(buffer, &length); if (rc == FALSE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if (buffer) free(buffer); buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc( length); if (NULL == buffer) { PyErr_NoMemory(); return NULL; } } else { goto return_none; } } else { break; } } ptr = buffer; while (offset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= length) { if (ptr->Relationship == RelationProcessorCore) ncpus += 1; offset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ptr++; } free(buffer); if (ncpus == 0) goto return_none; else return Py_BuildValue("i", ncpus); return_none: // mimic os.cpu_count() if (buffer != NULL) free(buffer); Py_RETURN_NONE; } /* * Return process cmdline as a Python list of cmdline arguments. */ static PyObject * psutil_proc_cmdline(PyObject *self, PyObject *args) { long pid; int pid_return; PyObject *arglist; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if ((pid == 0) || (pid == 4)) return Py_BuildValue("[]"); pid_return = psutil_pid_is_running(pid); if (pid_return == 0) return NoSuchProcess(); if (pid_return == -1) return NULL; // XXX the assumptio below probably needs to go away // May fail any of several ReadProcessMemory calls etc. and // not indicate a real problem so we ignore any errors and // just live without commandline. arglist = psutil_get_arg_list(pid); if ( NULL == arglist ) { // carry on anyway, clear any exceptions too PyErr_Clear(); return Py_BuildValue("[]"); } return arglist; } /* * Return process executable path. */ static PyObject * psutil_proc_exe(PyObject *self, PyObject *args) { long pid; HANDLE hProcess; wchar_t exe[MAX_PATH]; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION); if (NULL == hProcess) return NULL; if (GetProcessImageFileNameW(hProcess, exe, MAX_PATH) == 0) { CloseHandle(hProcess); PyErr_SetFromWindowsErr(0); return NULL; } CloseHandle(hProcess); return Py_BuildValue("u", exe); } /* * Return process base name. * Note: psutil_proc_exe() is attempted first because it's faster * but it raise AccessDenied for processes owned by other users * in which case we fall back on using this. */ static PyObject * psutil_proc_name(PyObject *self, PyObject *args) { long pid; int ok; PROCESSENTRY32 pentry; HANDLE hSnapShot; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); if (hSnapShot == INVALID_HANDLE_VALUE) { PyErr_SetFromWindowsErr(0); return NULL; } pentry.dwSize = sizeof(PROCESSENTRY32); ok = Process32First(hSnapShot, &pentry); if (! ok) { CloseHandle(hSnapShot); PyErr_SetFromWindowsErr(0); return NULL; } while (ok) { if (pentry.th32ProcessID == pid) { CloseHandle(hSnapShot); return Py_BuildValue("s", pentry.szExeFile); } ok = Process32Next(hSnapShot, &pentry); } CloseHandle(hSnapShot); NoSuchProcess(); return NULL; } /* * Return process memory information as a Python tuple. */ static PyObject * psutil_proc_memory_info(PyObject *self, PyObject *args) { HANDLE hProcess; DWORD pid; #if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 PROCESS_MEMORY_COUNTERS_EX cnt; #else PROCESS_MEMORY_COUNTERS cnt; #endif SIZE_T private = 0; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) return NULL; if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt, sizeof(cnt))) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } #if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 private = cnt.PrivateUsage; #endif CloseHandle(hProcess); // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits // is an (unsigned long long) and on 32bits is an (unsigned int). // "_WIN64" is defined if we're running a 64bit Python interpreter not // exclusively if the *system* is 64bit. #if defined(_WIN64) return Py_BuildValue( "(kKKKKKKKKK)", cnt.PageFaultCount, // unsigned long (unsigned long long)cnt.PeakWorkingSetSize, (unsigned long long)cnt.WorkingSetSize, (unsigned long long)cnt.QuotaPeakPagedPoolUsage, (unsigned long long)cnt.QuotaPagedPoolUsage, (unsigned long long)cnt.QuotaPeakNonPagedPoolUsage, (unsigned long long)cnt.QuotaNonPagedPoolUsage, (unsigned long long)cnt.PagefileUsage, (unsigned long long)cnt.PeakPagefileUsage, (unsigned long long)private); #else return Py_BuildValue( "(kIIIIIIIII)", cnt.PageFaultCount, // unsigned long (unsigned int)cnt.PeakWorkingSetSize, (unsigned int)cnt.WorkingSetSize, (unsigned int)cnt.QuotaPeakPagedPoolUsage, (unsigned int)cnt.QuotaPagedPoolUsage, (unsigned int)cnt.QuotaPeakNonPagedPoolUsage, (unsigned int)cnt.QuotaNonPagedPoolUsage, (unsigned int)cnt.PagefileUsage, (unsigned int)cnt.PeakPagefileUsage, (unsigned int)private); #endif } /* * Alternative implementation of the one above but bypasses ACCESS DENIED. */ static PyObject * psutil_proc_memory_info_2(PyObject *self, PyObject *args) { DWORD pid; PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; SIZE_T private; unsigned long pfault_count; #if defined(_WIN64) unsigned long long m1, m2, m3, m4, m5, m6, m7, m8; #else unsigned int m1, m2, m3, m4, m5, m6, m7, m8; #endif if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (! psutil_get_proc_info(pid, &process, &buffer)) return NULL; #if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 private = process->PrivatePageCount; #else private = 0; #endif pfault_count = process->PageFaultCount; m1 = process->PeakWorkingSetSize; m2 = process->WorkingSetSize; m3 = process->QuotaPeakPagedPoolUsage; m4 = process->QuotaPagedPoolUsage; m5 = process->QuotaPeakNonPagedPoolUsage; m6 = process->QuotaNonPagedPoolUsage; m7 = process->PagefileUsage; m8 = process->PeakPagefileUsage; free(buffer); // SYSTEM_PROCESS_INFORMATION values are defined as SIZE_T which on 64 // bits is an (unsigned long long) and on 32bits is an (unsigned int). // "_WIN64" is defined if we're running a 64bit Python interpreter not // exclusively if the *system* is 64bit. #if defined(_WIN64) return Py_BuildValue("(kKKKKKKKKK)", #else return Py_BuildValue("(kIIIIIIIII)", #endif pfault_count, m1, m2, m3, m4, m5, m6, m7, m8, private); } /* * Return a Python integer indicating the total amount of physical memory * in bytes. */ static PyObject * psutil_virtual_mem(PyObject *self, PyObject *args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); if (! GlobalMemoryStatusEx(&memInfo)) return PyErr_SetFromWindowsErr(0); return Py_BuildValue("(LLLLLL)", memInfo.ullTotalPhys, // total memInfo.ullAvailPhys, // avail memInfo.ullTotalPageFile, // total page file memInfo.ullAvailPageFile, // avail page file memInfo.ullTotalVirtual, // total virtual memInfo.ullAvailVirtual); // avail virtual } /* * Retrieves system CPU timing information as a (user, system, idle) * tuple. On a multiprocessor system, the values returned are the * sum of the designated times across all processors. */ static PyObject * psutil_cpu_times(PyObject *self, PyObject *args) { float idle, kernel, user, system; FILETIME idle_time, kernel_time, user_time; if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) return PyErr_SetFromWindowsErr(0); idle = (float)((HI_T * idle_time.dwHighDateTime) + \ (LO_T * idle_time.dwLowDateTime)); user = (float)((HI_T * user_time.dwHighDateTime) + \ (LO_T * user_time.dwLowDateTime)); kernel = (float)((HI_T * kernel_time.dwHighDateTime) + \ (LO_T * kernel_time.dwLowDateTime)); // Kernel time includes idle time. // We return only busy kernel time subtracting idle time from // kernel time. system = (kernel - idle); return Py_BuildValue("(fff)", user, system, idle); } /* * Same as above but for all system CPUs. */ static PyObject * psutil_per_cpu_times(PyObject *self, PyObject *args) { float idle, kernel, user; typedef DWORD (_stdcall * NTQSI_PROC) (int, PVOID, ULONG, PULONG); NTQSI_PROC NtQuerySystemInformation; HINSTANCE hNtDll; SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; SYSTEM_INFO si; UINT i; PyObject *arg = NULL; PyObject *retlist = PyList_New(0); if (retlist == NULL) return NULL; // dynamic linking is mandatory to use NtQuerySystemInformation hNtDll = LoadLibrary(TEXT("ntdll.dll")); if (hNtDll != NULL) { // gets NtQuerySystemInformation address NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( hNtDll, "NtQuerySystemInformation"); if (NtQuerySystemInformation != NULL) { // retrives number of processors GetSystemInfo(&si); // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION // structures, one per processor sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ malloc(si.dwNumberOfProcessors * \ sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); if (sppi != NULL) { // gets cpu time informations if (0 == NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, si.dwNumberOfProcessors * sizeof (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), NULL) ) { // computes system global times summing each // processor value idle = user = kernel = 0; for (i = 0; i < si.dwNumberOfProcessors; i++) { arg = NULL; user = (float)((HI_T * sppi[i].UserTime.HighPart) + (LO_T * sppi[i].UserTime.LowPart)); idle = (float)((HI_T * sppi[i].IdleTime.HighPart) + (LO_T * sppi[i].IdleTime.LowPart)); kernel = (float)((HI_T * sppi[i].KernelTime.HighPart) + (LO_T * sppi[i].KernelTime.LowPart)); // kernel time includes idle time on windows // we return only busy kernel time subtracting // idle time from kernel time arg = Py_BuildValue("(ddd)", user, kernel - idle, idle); if (!arg) goto error; if (PyList_Append(retlist, arg)) goto error; Py_DECREF(arg); } free(sppi); FreeLibrary(hNtDll); return retlist; } // END NtQuerySystemInformation } // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION } // END GetProcAddress } // END LoadLibrary goto error; error: Py_XDECREF(arg); Py_DECREF(retlist); if (sppi) free(sppi); if (hNtDll) FreeLibrary(hNtDll); PyErr_SetFromWindowsErr(0); return NULL; } /* * Return process current working directory as a Python string. */ static PyObject * psutil_proc_cwd(PyObject *self, PyObject *args) { long pid; HANDLE processHandle = NULL; PVOID pebAddress; PVOID rtlUserProcParamsAddress; UNICODE_STRING currentDirectory; WCHAR *currentDirectoryContent = NULL; PyObject *returnPyObj = NULL; PyObject *cwd_from_wchar = NULL; PyObject *cwd = NULL; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; processHandle = psutil_handle_from_pid(pid); if (processHandle == NULL) return NULL; pebAddress = psutil_get_peb_address(processHandle); // get the address of ProcessParameters #ifdef _WIN64 if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 32, &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) #else if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 0x10, &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) #endif { CloseHandle(processHandle); if (GetLastError() == ERROR_PARTIAL_COPY) { // this occurs quite often with system processes return AccessDenied(); } else { return PyErr_SetFromWindowsErr(0); } } // Read the currentDirectory UNICODE_STRING structure. // 0x24 refers to "CurrentDirectoryPath" of RTL_USER_PROCESS_PARAMETERS // structure, see: // http://wj32.wordpress.com/2009/01/24/ // howto-get-the-command-line-of-processes/ #ifdef _WIN64 if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 56, ¤tDirectory, sizeof(currentDirectory), NULL)) #else if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 0x24, ¤tDirectory, sizeof(currentDirectory), NULL)) #endif { CloseHandle(processHandle); if (GetLastError() == ERROR_PARTIAL_COPY) { // this occurs quite often with system processes return AccessDenied(); } else { return PyErr_SetFromWindowsErr(0); } } // allocate memory to hold cwd currentDirectoryContent = (WCHAR *)malloc(currentDirectory.Length + 1); if (currentDirectoryContent == NULL) { PyErr_NoMemory(); goto error; } // read cwd if (!ReadProcessMemory(processHandle, currentDirectory.Buffer, currentDirectoryContent, currentDirectory.Length, NULL)) { if (GetLastError() == ERROR_PARTIAL_COPY) { // this occurs quite often with system processes AccessDenied(); } else { PyErr_SetFromWindowsErr(0); } goto error; } // null-terminate the string to prevent wcslen from returning // incorrect length the length specifier is in characters, but // currentDirectory.Length is in bytes currentDirectoryContent[(currentDirectory.Length / sizeof(WCHAR))] = '\0'; // convert wchar array to a Python unicode string, and then to UTF8 cwd_from_wchar = PyUnicode_FromWideChar(currentDirectoryContent, wcslen(currentDirectoryContent)); if (cwd_from_wchar == NULL) goto error; #if PY_MAJOR_VERSION >= 3 cwd = PyUnicode_FromObject(cwd_from_wchar); #else cwd = PyUnicode_AsUTF8String(cwd_from_wchar); #endif if (cwd == NULL) goto error; // decrement the reference count on our temp unicode str to avoid // mem leak returnPyObj = Py_BuildValue("N", cwd); if (!returnPyObj) goto error; Py_DECREF(cwd_from_wchar); CloseHandle(processHandle); free(currentDirectoryContent); return returnPyObj; error: Py_XDECREF(cwd_from_wchar); Py_XDECREF(cwd); Py_XDECREF(returnPyObj); if (currentDirectoryContent != NULL) free(currentDirectoryContent); if (processHandle != NULL) CloseHandle(processHandle); return NULL; } /* * Resume or suspends a process */ int psutil_proc_suspend_or_resume(DWORD pid, int suspend) { // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx HANDLE hThreadSnap = NULL; THREADENTRY32 te32 = {0}; if (pid == 0) { AccessDenied(); return FALSE; } hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) { PyErr_SetFromWindowsErr(0); return FALSE; } // Fill in the size of the structure before using it te32.dwSize = sizeof(THREADENTRY32); if (! Thread32First(hThreadSnap, &te32)) { PyErr_SetFromWindowsErr(0); CloseHandle(hThreadSnap); return FALSE; } // Walk the thread snapshot to find all threads of the process. // If the thread belongs to the process, add its information // to the display list. do { if (te32.th32OwnerProcessID == pid) { HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID); if (hThread == NULL) { PyErr_SetFromWindowsErr(0); CloseHandle(hThread); CloseHandle(hThreadSnap); return FALSE; } if (suspend == 1) { if (SuspendThread(hThread) == (DWORD) - 1) { PyErr_SetFromWindowsErr(0); CloseHandle(hThread); CloseHandle(hThreadSnap); return FALSE; } } else { if (ResumeThread(hThread) == (DWORD) - 1) { PyErr_SetFromWindowsErr(0); CloseHandle(hThread); CloseHandle(hThreadSnap); return FALSE; } } CloseHandle(hThread); } } while (Thread32Next(hThreadSnap, &te32)); CloseHandle(hThreadSnap); return TRUE; } static PyObject * psutil_proc_suspend(PyObject *self, PyObject *args) { long pid; int suspend = 1; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (! psutil_proc_suspend_or_resume(pid, suspend)) return NULL; Py_RETURN_NONE; } static PyObject * psutil_proc_resume(PyObject *self, PyObject *args) { long pid; int suspend = 0; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (! psutil_proc_suspend_or_resume(pid, suspend)) return NULL; Py_RETURN_NONE; } static PyObject * psutil_proc_threads(PyObject *self, PyObject *args) { HANDLE hThread; THREADENTRY32 te32 = {0}; long pid; int pid_return; int rc; FILETIME ftDummy, ftKernel, ftUser; PyObject *retList = PyList_New(0); PyObject *pyTuple = NULL; HANDLE hThreadSnap = NULL; if (retList == NULL) return NULL; if (! PyArg_ParseTuple(args, "l", &pid)) goto error; if (pid == 0) { // raise AD instead of returning 0 as procexp is able to // retrieve useful information somehow AccessDenied(); goto error; } pid_return = psutil_pid_is_running(pid); if (pid_return == 0) { NoSuchProcess(); goto error; } if (pid_return == -1) goto error; hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) { PyErr_SetFromWindowsErr(0); goto error; } // Fill in the size of the structure before using it te32.dwSize = sizeof(THREADENTRY32); if (! Thread32First(hThreadSnap, &te32)) { PyErr_SetFromWindowsErr(0); goto error; } // Walk the thread snapshot to find all threads of the process. // If the thread belongs to the process, increase the counter. do { if (te32.th32OwnerProcessID == pid) { pyTuple = NULL; hThread = NULL; hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); if (hThread == NULL) { // thread has disappeared on us continue; } rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser); if (rc == 0) { PyErr_SetFromWindowsErr(0); goto error; } /* * User and kernel times are represented as a FILETIME structure * wich contains a 64-bit value representing the number of * 100-nanosecond intervals since January 1, 1601 (UTC): * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx * To convert it into a float representing the seconds that the * process has executed in user/kernel mode I borrowed the code * below from Python's Modules/posixmodule.c */ pyTuple = Py_BuildValue( "kdd", te32.th32ThreadID, (double)(ftUser.dwHighDateTime * 429.4967296 + \ ftUser.dwLowDateTime * 1e-7), (double)(ftKernel.dwHighDateTime * 429.4967296 + \ ftKernel.dwLowDateTime * 1e-7)); if (!pyTuple) goto error; if (PyList_Append(retList, pyTuple)) goto error; Py_DECREF(pyTuple); CloseHandle(hThread); } } while (Thread32Next(hThreadSnap, &te32)); CloseHandle(hThreadSnap); return retList; error: Py_XDECREF(pyTuple); Py_DECREF(retList); if (hThread != NULL) CloseHandle(hThread); if (hThreadSnap != NULL) CloseHandle(hThreadSnap); return NULL; } static PyObject * psutil_proc_open_files(PyObject *self, PyObject *args) { long pid; HANDLE processHandle; DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; PyObject *filesList; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; processHandle = psutil_handle_from_pid_waccess(pid, access); if (processHandle == NULL) return NULL; filesList = psutil_get_open_files(pid, processHandle); CloseHandle(processHandle); if (filesList == NULL) return PyErr_SetFromWindowsErr(0); return filesList; } /* Accept a filename's drive in native format like "\Device\HarddiskVolume1\" and return the corresponding drive letter (e.g. "C:\\"). If no match is found return an empty string. */ static PyObject * psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) { LPCTSTR lpDevicePath; TCHAR d = TEXT('A'); TCHAR szBuff[5]; if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) return NULL; while (d <= TEXT('Z')) { TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')}; TCHAR szTarget[512] = {0}; if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) { if (_tcscmp(lpDevicePath, szTarget) == 0) { _stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d); return Py_BuildValue("s", szBuff); } } d++; } return Py_BuildValue("s", ""); } /* * Return process username as a "DOMAIN//USERNAME" string. */ static PyObject * psutil_proc_username(PyObject *self, PyObject *args) { long pid; HANDLE processHandle; HANDLE tokenHandle; PTOKEN_USER user; ULONG bufferSize; PTSTR name; ULONG nameSize; PTSTR domainName; ULONG domainNameSize; SID_NAME_USE nameUse; PTSTR fullName; PyObject *returnObject; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; processHandle = psutil_handle_from_pid_waccess( pid, PROCESS_QUERY_INFORMATION); if (processHandle == NULL) return NULL; if (!OpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)) { CloseHandle(processHandle); return PyErr_SetFromWindowsErr(0); } CloseHandle(processHandle); // Get the user SID. bufferSize = 0x100; user = malloc(bufferSize); if (user == NULL) return PyErr_NoMemory(); if (!GetTokenInformation(tokenHandle, TokenUser, user, bufferSize, &bufferSize)) { free(user); user = malloc(bufferSize); if (user == NULL) { CloseHandle(tokenHandle); return PyErr_NoMemory(); } if (!GetTokenInformation(tokenHandle, TokenUser, user, bufferSize, &bufferSize)) { free(user); CloseHandle(tokenHandle); return PyErr_SetFromWindowsErr(0); } } CloseHandle(tokenHandle); // resolve the SID to a name nameSize = 0x100; domainNameSize = 0x100; name = malloc(nameSize * sizeof(TCHAR)); if (name == NULL) return PyErr_NoMemory(); domainName = malloc(domainNameSize * sizeof(TCHAR)); if (domainName == NULL) return PyErr_NoMemory(); if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, &domainNameSize, &nameUse)) { free(name); free(domainName); name = malloc(nameSize * sizeof(TCHAR)); if (name == NULL) return PyErr_NoMemory(); domainName = malloc(domainNameSize * sizeof(TCHAR)); if (domainName == NULL) return PyErr_NoMemory(); if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, &domainNameSize, &nameUse)) { free(name); free(domainName); free(user); return PyErr_SetFromWindowsErr(0); } } nameSize = _tcslen(name); domainNameSize = _tcslen(domainName); // build the full username string fullName = malloc((domainNameSize + 1 + nameSize + 1) * sizeof(TCHAR)); if (fullName == NULL) { free(name); free(domainName); free(user); return PyErr_NoMemory(); } memcpy(fullName, domainName, domainNameSize); fullName[domainNameSize] = '\\'; memcpy(&fullName[domainNameSize + 1], name, nameSize); fullName[domainNameSize + 1 + nameSize] = '\0'; returnObject = PyUnicode_Decode( fullName, _tcslen(fullName), Py_FileSystemDefaultEncoding, "replace"); free(fullName); free(name); free(domainName); free(user); return returnObject; } /* * Return a list of network connections opened by a process */ static PyObject * psutil_net_connections(PyObject *self, PyObject *args) { static long null_address[4] = { 0, 0, 0, 0 }; unsigned long pid; PyObject *connectionsList; PyObject *connectionTuple = NULL; PyObject *af_filter = NULL; PyObject *type_filter = NULL; PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); typedef PSTR (NTAPI * _RtlIpv4AddressToStringA)(struct in_addr *, PSTR); _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR); _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, TCP_TABLE_CLASS, ULONG); _GetExtendedTcpTable getExtendedTcpTable; typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, UDP_TABLE_CLASS, ULONG); _GetExtendedUdpTable getExtendedUdpTable; PVOID table = NULL; DWORD tableSize; PMIB_TCPTABLE_OWNER_PID tcp4Table; PMIB_UDPTABLE_OWNER_PID udp4Table; PMIB_TCP6TABLE_OWNER_PID tcp6Table; PMIB_UDP6TABLE_OWNER_PID udp6Table; ULONG i; CHAR addressBufferLocal[65]; PyObject *addressTupleLocal = NULL; CHAR addressBufferRemote[65]; PyObject *addressTupleRemote = NULL; if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { _psutil_conn_decref_objs(); return NULL; } if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { _psutil_conn_decref_objs(); PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); return NULL; } if (pid != -1) { if (psutil_pid_is_running(pid) == 0) { _psutil_conn_decref_objs(); return NoSuchProcess(); } } // Import some functions. { HMODULE ntdll; HMODULE iphlpapi; ntdll = LoadLibrary(TEXT("ntdll.dll")); rtlIpv4AddressToStringA = (_RtlIpv4AddressToStringA)GetProcAddress( ntdll, "RtlIpv4AddressToStringA"); rtlIpv6AddressToStringA = (_RtlIpv6AddressToStringA)GetProcAddress( ntdll, "RtlIpv6AddressToStringA"); /* TODO: Check these two function pointers */ iphlpapi = LoadLibrary(TEXT("iphlpapi.dll")); getExtendedTcpTable = (_GetExtendedTcpTable)GetProcAddress(iphlpapi, "GetExtendedTcpTable"); getExtendedUdpTable = (_GetExtendedUdpTable)GetProcAddress(iphlpapi, "GetExtendedUdpTable"); FreeLibrary(ntdll); FreeLibrary(iphlpapi); } if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { PyErr_SetString(PyExc_NotImplementedError, "feature not supported on this Windows version"); _psutil_conn_decref_objs(); return NULL; } connectionsList = PyList_New(0); if (connectionsList == NULL) { _psutil_conn_decref_objs(); return NULL; } // TCP IPv4 if ((PySequence_Contains(af_filter, _AF_INET) == 1) && (PySequence_Contains(type_filter, _SOCK_STREAM) == 1)) { table = NULL; connectionTuple = NULL; addressTupleLocal = NULL; addressTupleRemote = NULL; tableSize = 0; getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); table = malloc(tableSize); if (table == NULL) { PyErr_NoMemory(); goto error; } if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == 0) { tcp4Table = table; for (i = 0; i < tcp4Table->dwNumEntries; i++) { if (pid != -1) { if (tcp4Table->table[i].dwOwningPid != pid) { continue; } } if (tcp4Table->table[i].dwLocalAddr != 0 || tcp4Table->table[i].dwLocalPort != 0) { struct in_addr addr; addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; rtlIpv4AddressToStringA(&addr, addressBufferLocal); addressTupleLocal = Py_BuildValue( "(si)", addressBufferLocal, BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); } else { addressTupleLocal = PyTuple_New(0); } if (addressTupleLocal == NULL) goto error; // On Windows <= XP, remote addr is filled even if socket // is in LISTEN mode in which case we just ignore it. if ((tcp4Table->table[i].dwRemoteAddr != 0 || tcp4Table->table[i].dwRemotePort != 0) && (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) { struct in_addr addr; addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; rtlIpv4AddressToStringA(&addr, addressBufferRemote); addressTupleRemote = Py_BuildValue( "(si)", addressBufferRemote, BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); } else { addressTupleRemote = PyTuple_New(0); } if (addressTupleRemote == NULL) goto error; connectionTuple = Py_BuildValue( "(iiiNNiI)", -1, AF_INET, SOCK_STREAM, addressTupleLocal, addressTupleRemote, tcp4Table->table[i].dwState, tcp4Table->table[i].dwOwningPid); if (!connectionTuple) goto error; if (PyList_Append(connectionsList, connectionTuple)) goto error; Py_DECREF(connectionTuple); } } free(table); } // TCP IPv6 if ((PySequence_Contains(af_filter, _AF_INET6) == 1) && (PySequence_Contains(type_filter, _SOCK_STREAM) == 1)) { table = NULL; connectionTuple = NULL; addressTupleLocal = NULL; addressTupleRemote = NULL; tableSize = 0; getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0); table = malloc(tableSize); if (table == NULL) { PyErr_NoMemory(); goto error; } if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) == 0) { tcp6Table = table; for (i = 0; i < tcp6Table->dwNumEntries; i++) { if (pid != -1) { if (tcp6Table->table[i].dwOwningPid != pid) { continue; } } if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || tcp6Table->table[i].dwLocalPort != 0) { struct in6_addr addr; memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); rtlIpv6AddressToStringA(&addr, addressBufferLocal); addressTupleLocal = Py_BuildValue( "(si)", addressBufferLocal, BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); } else { addressTupleLocal = PyTuple_New(0); } if (addressTupleLocal == NULL) goto error; // On Windows <= XP, remote addr is filled even if socket // is in LISTEN mode in which case we just ignore it. if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) != 0 || tcp6Table->table[i].dwRemotePort != 0) && (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) { struct in6_addr addr; memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); rtlIpv6AddressToStringA(&addr, addressBufferRemote); addressTupleRemote = Py_BuildValue( "(si)", addressBufferRemote, BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); } else { addressTupleRemote = PyTuple_New(0); } if (addressTupleRemote == NULL) goto error; connectionTuple = Py_BuildValue( "(iiiNNiI)", -1, AF_INET6, SOCK_STREAM, addressTupleLocal, addressTupleRemote, tcp6Table->table[i].dwState, tcp6Table->table[i].dwOwningPid); if (!connectionTuple) goto error; if (PyList_Append(connectionsList, connectionTuple)) goto error; Py_DECREF(connectionTuple); } } free(table); } // UDP IPv4 if ((PySequence_Contains(af_filter, _AF_INET) == 1) && (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1)) { table = NULL; connectionTuple = NULL; addressTupleLocal = NULL; addressTupleRemote = NULL; tableSize = 0; getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0); table = malloc(tableSize); if (table == NULL) { PyErr_NoMemory(); goto error; } if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) == 0) { udp4Table = table; for (i = 0; i < udp4Table->dwNumEntries; i++) { if (pid != -1) { if (udp4Table->table[i].dwOwningPid != pid) { continue; } } if (udp4Table->table[i].dwLocalAddr != 0 || udp4Table->table[i].dwLocalPort != 0) { struct in_addr addr; addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; rtlIpv4AddressToStringA(&addr, addressBufferLocal); addressTupleLocal = Py_BuildValue( "(si)", addressBufferLocal, BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); } else { addressTupleLocal = PyTuple_New(0); } if (addressTupleLocal == NULL) goto error; connectionTuple = Py_BuildValue( "(iiiNNiI)", -1, AF_INET, SOCK_DGRAM, addressTupleLocal, PyTuple_New(0), PSUTIL_CONN_NONE, udp4Table->table[i].dwOwningPid); if (!connectionTuple) goto error; if (PyList_Append(connectionsList, connectionTuple)) goto error; Py_DECREF(connectionTuple); } } free(table); } // UDP IPv6 if ((PySequence_Contains(af_filter, _AF_INET6) == 1) && (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1)) { table = NULL; connectionTuple = NULL; addressTupleLocal = NULL; addressTupleRemote = NULL; tableSize = 0; getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0); table = malloc(tableSize); if (table == NULL) { PyErr_NoMemory(); goto error; } if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) == 0) { udp6Table = table; for (i = 0; i < udp6Table->dwNumEntries; i++) { if (pid != -1) { if (udp6Table->table[i].dwOwningPid != pid) { continue; } } if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || udp6Table->table[i].dwLocalPort != 0) { struct in6_addr addr; memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); rtlIpv6AddressToStringA(&addr, addressBufferLocal); addressTupleLocal = Py_BuildValue( "(si)", addressBufferLocal, BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); } else { addressTupleLocal = PyTuple_New(0); } if (addressTupleLocal == NULL) goto error; connectionTuple = Py_BuildValue( "(iiiNNiI)", -1, AF_INET6, SOCK_DGRAM, addressTupleLocal, PyTuple_New(0), PSUTIL_CONN_NONE, udp6Table->table[i].dwOwningPid); if (!connectionTuple) goto error; if (PyList_Append(connectionsList, connectionTuple)) goto error; Py_DECREF(connectionTuple); } } free(table); } _psutil_conn_decref_objs(); return connectionsList; error: _psutil_conn_decref_objs(); Py_XDECREF(connectionTuple); Py_XDECREF(addressTupleLocal); Py_XDECREF(addressTupleRemote); Py_DECREF(connectionsList); if (table != NULL) free(table); return NULL; } /* * Get process priority as a Python integer. */ static PyObject * psutil_proc_priority_get(PyObject *self, PyObject *args) { long pid; DWORD priority; HANDLE hProcess; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { return NULL; } priority = GetPriorityClass(hProcess); CloseHandle(hProcess); if (priority == 0) { PyErr_SetFromWindowsErr(0); return NULL; } return Py_BuildValue("i", priority); } /* * Set process priority. */ static PyObject * psutil_proc_priority_set(PyObject *self, PyObject *args) { long pid; int priority; int retval; HANDLE hProcess; DWORD dwDesiredAccess = \ PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; if (! PyArg_ParseTuple(args, "li", &pid, &priority)) { return NULL; } hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess); if (hProcess == NULL) { return NULL; } retval = SetPriorityClass(hProcess, priority); CloseHandle(hProcess); if (retval == 0) { PyErr_SetFromWindowsErr(0); return NULL; } Py_RETURN_NONE; } #if (_WIN32_WINNT >= 0x0600) // Windows Vista /* * Get process IO priority as a Python integer. */ static PyObject * psutil_proc_io_priority_get(PyObject *self, PyObject *args) { long pid; HANDLE hProcess; PULONG IoPriority; _NtQueryInformationProcess NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { return NULL; } NtQueryInformationProcess( hProcess, ProcessIoPriority, &IoPriority, sizeof(ULONG), NULL ); CloseHandle(hProcess); return Py_BuildValue("i", IoPriority); } /* * Set process IO priority. */ static PyObject * psutil_proc_io_priority_set(PyObject *self, PyObject *args) { long pid; int prio; HANDLE hProcess; _NtSetInformationProcess NtSetInformationProcess = (_NtSetInformationProcess)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtSetInformationProcess"); if (NtSetInformationProcess == NULL) { PyErr_SetString(PyExc_RuntimeError, "couldn't get NtSetInformationProcess"); return NULL; } if (! PyArg_ParseTuple(args, "li", &pid, &prio)) { return NULL; } hProcess = psutil_handle_from_pid_waccess(pid, PROCESS_ALL_ACCESS); if (hProcess == NULL) { return NULL; } NtSetInformationProcess( hProcess, ProcessIoPriority, (PVOID)&prio, sizeof((PVOID)prio) ); CloseHandle(hProcess); Py_RETURN_NONE; } #endif /* * Return a Python tuple referencing process I/O counters. */ static PyObject * psutil_proc_io_counters(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess; IO_COUNTERS IoCounters; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) { return NULL; } if (! GetProcessIoCounters(hProcess, &IoCounters)) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } CloseHandle(hProcess); return Py_BuildValue("(KKKK)", IoCounters.ReadOperationCount, IoCounters.WriteOperationCount, IoCounters.ReadTransferCount, IoCounters.WriteTransferCount); } /* * Return process CPU affinity as a bitmask */ static PyObject * psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess; DWORD_PTR proc_mask; DWORD_PTR system_mask; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) { return NULL; } if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } CloseHandle(hProcess); #ifdef _WIN64 return Py_BuildValue("K", (unsigned long long)proc_mask); #else return Py_BuildValue("k", (unsigned long)proc_mask); #endif } /* * Set process CPU affinity */ static PyObject * psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess; DWORD dwDesiredAccess = \ PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; DWORD_PTR mask; #ifdef _WIN64 if (! PyArg_ParseTuple(args, "lK", &pid, &mask)) #else if (! PyArg_ParseTuple(args, "lk", &pid, &mask)) #endif { return NULL; } hProcess = psutil_handle_from_pid_waccess(pid, dwDesiredAccess); if (hProcess == NULL) { return NULL; } if (SetProcessAffinityMask(hProcess, mask) == 0) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } CloseHandle(hProcess); Py_RETURN_NONE; } /* * Return True if one of the process threads is in a waiting or * suspended status. */ static PyObject * psutil_proc_is_suspended(PyObject *self, PyObject *args) { DWORD pid; ULONG i; PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (! psutil_get_proc_info(pid, &process, &buffer)) { return NULL; } for (i = 0; i < process->NumberOfThreads; i++) { if (process->Threads[i].ThreadState != Waiting || process->Threads[i].WaitReason != Suspended) { free(buffer); Py_RETURN_FALSE; } } free(buffer); Py_RETURN_TRUE; } /* * Return path's disk total and free as a Python tuple. */ static PyObject * psutil_disk_usage(PyObject *self, PyObject *args) { BOOL retval; ULARGE_INTEGER _, total, free; char *path; if (PyArg_ParseTuple(args, "u", &path)) { Py_BEGIN_ALLOW_THREADS retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free); Py_END_ALLOW_THREADS goto return_; } // on Python 2 we also want to accept plain strings other // than Unicode #if PY_MAJOR_VERSION <= 2 PyErr_Clear(); // drop the argument parsing error if (PyArg_ParseTuple(args, "s", &path)) { Py_BEGIN_ALLOW_THREADS retval = GetDiskFreeSpaceEx(path, &_, &total, &free); Py_END_ALLOW_THREADS goto return_; } #endif return NULL; return_: if (retval == 0) return PyErr_SetFromWindowsErr(0); else return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); } /* * Return a Python list of named tuples with overall network I/O information */ static PyObject * psutil_net_io_counters(PyObject *self, PyObject *args) { char ifname[MAX_PATH]; DWORD dwRetVal = 0; MIB_IFROW *pIfRow = NULL; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; PyObject *py_retdict = PyDict_New(); PyObject *py_nic_info = NULL; PyObject *py_nic_name = NULL; if (py_retdict == NULL) return NULL; pAddresses = psutil_get_nic_addresses(); if (pAddresses == NULL) goto error; pCurrAddresses = pAddresses; while (pCurrAddresses) { py_nic_name = NULL; py_nic_info = NULL; pIfRow = (MIB_IFROW *) malloc(sizeof(MIB_IFROW)); if (pIfRow == NULL) { PyErr_NoMemory(); goto error; } pIfRow->dwIndex = pCurrAddresses->IfIndex; dwRetVal = GetIfEntry(pIfRow); if (dwRetVal != NO_ERROR) { PyErr_SetString(PyExc_RuntimeError, "GetIfEntry() failed."); goto error; } py_nic_info = Py_BuildValue("(kkkkkkkk)", pIfRow->dwOutOctets, pIfRow->dwInOctets, pIfRow->dwOutUcastPkts, pIfRow->dwInUcastPkts, pIfRow->dwInErrors, pIfRow->dwOutErrors, pIfRow->dwInDiscards, pIfRow->dwOutDiscards); if (!py_nic_info) goto error; sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); py_nic_name = PyUnicode_Decode( ifname, _tcslen(ifname), Py_FileSystemDefaultEncoding, "replace"); if (py_nic_name == NULL) goto error; if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) goto error; Py_XDECREF(py_nic_name); Py_XDECREF(py_nic_info); free(pIfRow); pCurrAddresses = pCurrAddresses->Next; } free(pAddresses); return py_retdict; error: Py_XDECREF(py_nic_name); Py_XDECREF(py_nic_info); Py_DECREF(py_retdict); if (pAddresses != NULL) free(pAddresses); if (pIfRow != NULL) free(pIfRow); return NULL; } /* * Return a Python dict of tuples for disk I/O information */ static PyObject * psutil_disk_io_counters(PyObject *self, PyObject *args) { DISK_PERFORMANCE_WIN_2008 diskPerformance; DWORD dwSize; HANDLE hDevice = NULL; char szDevice[MAX_PATH]; char szDeviceDisplay[MAX_PATH]; int devNum; PyObject *py_retdict = PyDict_New(); PyObject *py_disk_info = NULL; if (py_retdict == NULL) { return NULL; } // Apparently there's no way to figure out how many times we have // to iterate in order to find valid drives. // Let's assume 32, which is higher than 26, the number of letters // in the alphabet (from A:\ to Z:\). for (devNum = 0; devNum <= 32; ++devNum) { py_disk_info = NULL; sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum); hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice == INVALID_HANDLE_VALUE) { continue; } if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance, sizeof(diskPerformance), &dwSize, NULL)) { sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum); py_disk_info = Py_BuildValue( "(IILLKK)", diskPerformance.ReadCount, diskPerformance.WriteCount, diskPerformance.BytesRead, diskPerformance.BytesWritten, (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000, (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000); if (!py_disk_info) goto error; if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_disk_info)) { goto error; } Py_XDECREF(py_disk_info); } else { // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when // compiling with mingw32; not sure what to do. // return PyErr_SetFromWindowsErr(0); ;; } CloseHandle(hDevice); } return py_retdict; error: Py_XDECREF(py_disk_info); Py_DECREF(py_retdict); if (hDevice != NULL) CloseHandle(hDevice); return NULL; } static char *psutil_get_drive_type(int type) { switch (type) { case DRIVE_FIXED: return "fixed"; case DRIVE_CDROM: return "cdrom"; case DRIVE_REMOVABLE: return "removable"; case DRIVE_UNKNOWN: return "unknown"; case DRIVE_NO_ROOT_DIR: return "unmounted"; case DRIVE_REMOTE: return "remote"; case DRIVE_RAMDISK: return "ramdisk"; default: return "?"; } } #ifndef _ARRAYSIZE #define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #endif /* * Return disk partitions as a list of tuples such as * (drive_letter, drive_letter, type, "") */ static PyObject * psutil_disk_partitions(PyObject *self, PyObject *args) { DWORD num_bytes; char drive_strings[255]; char *drive_letter = drive_strings; int all; int type; int ret; char opts[20]; LPTSTR fs_type[MAX_PATH + 1] = { 0 }; DWORD pflags = 0; PyObject *py_all; PyObject *py_retlist = PyList_New(0); PyObject *py_tuple = NULL; if (py_retlist == NULL) { return NULL; } // avoid to visualize a message box in case something goes wrong // see https://github.com/giampaolo/psutil/issues/264 SetErrorMode(SEM_FAILCRITICALERRORS); if (! PyArg_ParseTuple(args, "O", &py_all)) { goto error; } all = PyObject_IsTrue(py_all); Py_BEGIN_ALLOW_THREADS num_bytes = GetLogicalDriveStrings(254, drive_letter); Py_END_ALLOW_THREADS if (num_bytes == 0) { PyErr_SetFromWindowsErr(0); goto error; } while (*drive_letter != 0) { py_tuple = NULL; opts[0] = 0; fs_type[0] = 0; Py_BEGIN_ALLOW_THREADS type = GetDriveType(drive_letter); Py_END_ALLOW_THREADS // by default we only show hard drives and cd-roms if (all == 0) { if ((type == DRIVE_UNKNOWN) || (type == DRIVE_NO_ROOT_DIR) || (type == DRIVE_REMOTE) || (type == DRIVE_RAMDISK)) { goto next; } // floppy disk: skip it by default as it introduces a // considerable slowdown. if ((type == DRIVE_REMOVABLE) && (strcmp(drive_letter, "A:\\") == 0)) { goto next; } } ret = GetVolumeInformation( (LPCTSTR)drive_letter, NULL, _ARRAYSIZE(drive_letter), NULL, NULL, &pflags, (LPTSTR)fs_type, _ARRAYSIZE(fs_type)); if (ret == 0) { // We might get here in case of a floppy hard drive, in // which case the error is (21, "device not ready"). // Let's pretend it didn't happen as we already have // the drive name and type ('removable'). strcat_s(opts, _countof(opts), ""); SetLastError(0); } else { if (pflags & FILE_READ_ONLY_VOLUME) { strcat_s(opts, _countof(opts), "ro"); } else { strcat_s(opts, _countof(opts), "rw"); } if (pflags & FILE_VOLUME_IS_COMPRESSED) { strcat_s(opts, _countof(opts), ",compressed"); } } if (strlen(opts) > 0) { strcat_s(opts, _countof(opts), ","); } strcat_s(opts, _countof(opts), psutil_get_drive_type(type)); py_tuple = Py_BuildValue( "(ssss)", drive_letter, drive_letter, fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS opts); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); goto next; next: drive_letter = strchr(drive_letter, 0) + 1; } SetErrorMode(0); return py_retlist; error: SetErrorMode(0); Py_XDECREF(py_tuple); Py_DECREF(py_retlist); return NULL; } /* * Return a Python dict of tuples for disk I/O information */ static PyObject * psutil_users(PyObject *self, PyObject *args) { HANDLE hServer = NULL; LPTSTR buffer_user = NULL; LPTSTR buffer_addr = NULL; PWTS_SESSION_INFO sessions = NULL; DWORD count; DWORD i; DWORD sessionId; DWORD bytes; PWTS_CLIENT_ADDRESS address; char address_str[50]; long long unix_time; PWINSTATIONQUERYINFORMATIONW WinStationQueryInformationW; WINSTATION_INFO station_info; HINSTANCE hInstWinSta = NULL; ULONG returnLen; PyObject *py_retlist = PyList_New(0); PyObject *py_tuple = NULL; PyObject *py_address = NULL; PyObject *py_buffer_user_encoded = NULL; if (py_retlist == NULL) { return NULL; } hInstWinSta = LoadLibraryA("winsta.dll"); WinStationQueryInformationW = (PWINSTATIONQUERYINFORMATIONW) \ GetProcAddress(hInstWinSta, "WinStationQueryInformationW"); hServer = WTSOpenServer('\0'); if (hServer == NULL) { PyErr_SetFromWindowsErr(0); goto error; } if (WTSEnumerateSessions(hServer, 0, 1, &sessions, &count) == 0) { PyErr_SetFromWindowsErr(0); goto error; } for (i = 0; i < count; i++) { py_address = NULL; py_tuple = NULL; sessionId = sessions[i].SessionId; if (buffer_user != NULL) { WTSFreeMemory(buffer_user); } if (buffer_addr != NULL) { WTSFreeMemory(buffer_addr); } buffer_user = NULL; buffer_addr = NULL; // username bytes = 0; if (WTSQuerySessionInformation(hServer, sessionId, WTSUserName, &buffer_user, &bytes) == 0) { PyErr_SetFromWindowsErr(0); goto error; } if (bytes == 1) { continue; } // address bytes = 0; if (WTSQuerySessionInformation(hServer, sessionId, WTSClientAddress, &buffer_addr, &bytes) == 0) { PyErr_SetFromWindowsErr(0); goto error; } address = (PWTS_CLIENT_ADDRESS)buffer_addr; if (address->AddressFamily == 0) { // AF_INET sprintf_s(address_str, _countof(address_str), "%u.%u.%u.%u", address->Address[0], address->Address[1], address->Address[2], address->Address[3]); py_address = Py_BuildValue("s", address_str); if (!py_address) goto error; } else { py_address = Py_None; } // login time if (!WinStationQueryInformationW(hServer, sessionId, WinStationInformation, &station_info, sizeof(station_info), &returnLen)) { goto error; } unix_time = ((LONGLONG)station_info.ConnectTime.dwHighDateTime) << 32; unix_time += \ station_info.ConnectTime.dwLowDateTime - 116444736000000000LL; unix_time /= 10000000; py_buffer_user_encoded = PyUnicode_Decode( buffer_user, _tcslen(buffer_user), Py_FileSystemDefaultEncoding, "replace"); py_tuple = Py_BuildValue("OOd", py_buffer_user_encoded, py_address, (double)unix_time); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; Py_XDECREF(py_buffer_user_encoded); Py_XDECREF(py_address); Py_XDECREF(py_tuple); } WTSCloseServer(hServer); WTSFreeMemory(sessions); WTSFreeMemory(buffer_user); WTSFreeMemory(buffer_addr); FreeLibrary(hInstWinSta); return py_retlist; error: Py_XDECREF(py_buffer_user_encoded); Py_XDECREF(py_tuple); Py_XDECREF(py_address); Py_DECREF(py_retlist); if (hInstWinSta != NULL) { FreeLibrary(hInstWinSta); } if (hServer != NULL) { WTSCloseServer(hServer); } if (sessions != NULL) { WTSFreeMemory(sessions); } if (buffer_user != NULL) { WTSFreeMemory(buffer_user); } if (buffer_addr != NULL) { WTSFreeMemory(buffer_addr); } return NULL; } /* * Return the number of handles opened by process. */ static PyObject * psutil_proc_num_handles(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess; DWORD handleCount; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) { return NULL; } if (! GetProcessHandleCount(hProcess, &handleCount)) { CloseHandle(hProcess); return PyErr_SetFromWindowsErr(0); } CloseHandle(hProcess); return Py_BuildValue("k", handleCount); } /* * Get various process information by using NtQuerySystemInformation. * We use this as a fallback when faster functions fail with access * denied. This is slower because it iterates over all processes. * Returned tuple includes the following process info: * * - num_threads * - ctx_switches * - num_handles (fallback) * - user/kernel times (fallback) * - create time (fallback) * - io counters (fallback) */ static PyObject * psutil_proc_info(PyObject *self, PyObject *args) { DWORD pid; PSYSTEM_PROCESS_INFORMATION process; PVOID buffer; ULONG num_handles; ULONG i; ULONG ctx_switches = 0; double user_time; double kernel_time; long long create_time; int num_threads; LONGLONG io_rcount, io_wcount, io_rbytes, io_wbytes; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; if (! psutil_get_proc_info(pid, &process, &buffer)) return NULL; num_handles = process->HandleCount; for (i = 0; i < process->NumberOfThreads; i++) ctx_switches += process->Threads[i].ContextSwitches; user_time = (double)process->UserTime.HighPart * 429.4967296 + \ (double)process->UserTime.LowPart * 1e-7; kernel_time = (double)process->KernelTime.HighPart * 429.4967296 + \ (double)process->KernelTime.LowPart * 1e-7; // Convert the LARGE_INTEGER union to a Unix time. // It's the best I could find by googling and borrowing code here // and there. The time returned has a precision of 1 second. if (0 == pid || 4 == pid) { // the python module will translate this into BOOT_TIME later create_time = 0; } else { create_time = ((LONGLONG)process->CreateTime.HighPart) << 32; create_time += process->CreateTime.LowPart - 116444736000000000LL; create_time /= 10000000; } num_threads = (int)process->NumberOfThreads; io_rcount = process->ReadOperationCount.QuadPart; io_wcount = process->WriteOperationCount.QuadPart; io_rbytes = process->ReadTransferCount.QuadPart; io_wbytes = process->WriteTransferCount.QuadPart; free(buffer); return Py_BuildValue( "kkdddiKKKK", num_handles, ctx_switches, user_time, kernel_time, (double)create_time, num_threads, io_rcount, io_wcount, io_rbytes, io_wbytes ); } static char *get_region_protection_string(ULONG protection) { switch (protection & 0xff) { case PAGE_NOACCESS: return ""; case PAGE_READONLY: return "r"; case PAGE_READWRITE: return "rw"; case PAGE_WRITECOPY: return "wc"; case PAGE_EXECUTE: return "x"; case PAGE_EXECUTE_READ: return "xr"; case PAGE_EXECUTE_READWRITE: return "xrw"; case PAGE_EXECUTE_WRITECOPY: return "xwc"; default: return "?"; } } /* * Return a list of process's memory mappings. */ static PyObject * psutil_proc_memory_maps(PyObject *self, PyObject *args) { DWORD pid; HANDLE hProcess = NULL; MEMORY_BASIC_INFORMATION basicInfo; PVOID baseAddress; PVOID previousAllocationBase; CHAR mappedFileName[MAX_PATH]; SYSTEM_INFO system_info; LPVOID maxAddr; PyObject *py_list = PyList_New(0); PyObject *py_tuple = NULL; if (py_list == NULL) { return NULL; } if (! PyArg_ParseTuple(args, "l", &pid)) { goto error; } hProcess = psutil_handle_from_pid(pid); if (NULL == hProcess) { goto error; } GetSystemInfo(&system_info); maxAddr = system_info.lpMaximumApplicationAddress; baseAddress = NULL; previousAllocationBase = NULL; while (VirtualQueryEx(hProcess, baseAddress, &basicInfo, sizeof(MEMORY_BASIC_INFORMATION))) { py_tuple = NULL; if (baseAddress > maxAddr) { break; } if (GetMappedFileNameA(hProcess, baseAddress, mappedFileName, sizeof(mappedFileName))) { py_tuple = Py_BuildValue( "(kssI)", (unsigned long)baseAddress, get_region_protection_string(basicInfo.Protect), mappedFileName, basicInfo.RegionSize); if (!py_tuple) goto error; if (PyList_Append(py_list, py_tuple)) goto error; Py_DECREF(py_tuple); } previousAllocationBase = basicInfo.AllocationBase; baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize; } CloseHandle(hProcess); return py_list; error: Py_XDECREF(py_tuple); Py_DECREF(py_list); if (hProcess != NULL) CloseHandle(hProcess); return NULL; } /* * Return a {pid:ppid, ...} dict for all running processes. */ static PyObject * psutil_ppid_map(PyObject *self, PyObject *args) { PyObject *pid = NULL; PyObject *ppid = NULL; PyObject *py_retdict = PyDict_New(); HANDLE handle = NULL; PROCESSENTRY32 pe = {0}; pe.dwSize = sizeof(PROCESSENTRY32); if (py_retdict == NULL) return NULL; handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (handle == INVALID_HANDLE_VALUE) { PyErr_SetFromWindowsErr(0); Py_DECREF(py_retdict); return NULL; } if (Process32First(handle, &pe)) { do { pid = Py_BuildValue("I", pe.th32ProcessID); if (pid == NULL) goto error; ppid = Py_BuildValue("I", pe.th32ParentProcessID); if (ppid == NULL) goto error; if (PyDict_SetItem(py_retdict, pid, ppid)) goto error; Py_DECREF(pid); Py_DECREF(ppid); } while (Process32Next(handle, &pe)); } CloseHandle(handle); return py_retdict; error: Py_XDECREF(pid); Py_XDECREF(ppid); Py_DECREF(py_retdict); CloseHandle(handle); return NULL; } /* * Return NICs addresses. */ static PyObject * psutil_net_if_addrs(PyObject *self, PyObject *args) { unsigned int i = 0; ULONG family; PCTSTR intRet; char *ptr; char buff[100]; char ifname[MAX_PATH]; DWORD bufflen = 100; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; PyObject *py_retlist = PyList_New(0); PyObject *py_tuple = NULL; PyObject *py_address = NULL; PyObject *py_mac_address = NULL; if (py_retlist == NULL) return NULL; pAddresses = psutil_get_nic_addresses(); if (pAddresses == NULL) goto error; pCurrAddresses = pAddresses; while (pCurrAddresses) { pUnicast = pCurrAddresses->FirstUnicastAddress; sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); // MAC address if (pCurrAddresses->PhysicalAddressLength != 0) { ptr = buff; *ptr = '\0'; for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { sprintf_s(ptr, _countof(buff), "%.2X\n", (int)pCurrAddresses->PhysicalAddress[i]); } else { sprintf_s(ptr, _countof(buff), "%.2X-", (int)pCurrAddresses->PhysicalAddress[i]); } ptr += 3; } *--ptr = '\0'; #if PY_MAJOR_VERSION >= 3 py_mac_address = PyUnicode_FromString(buff); #else py_mac_address = PyString_FromString(buff); #endif if (py_mac_address == NULL) goto error; Py_INCREF(Py_None); Py_INCREF(Py_None); py_tuple = Py_BuildValue( "(siOOO)", ifname, -1, // this will be converted later to AF_LINK py_mac_address, Py_None, Py_None ); if (! py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); Py_DECREF(py_mac_address); } // find out the IP address associated with the NIC if (pUnicast != NULL) { for (i = 0; pUnicast != NULL; i++) { family = pUnicast->Address.lpSockaddr->sa_family; if (family == AF_INET) { struct sockaddr_in *sa_in = (struct sockaddr_in *) pUnicast->Address.lpSockaddr; intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff, bufflen); } else if (family == AF_INET6) { struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) pUnicast->Address.lpSockaddr; intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr), buff, bufflen); } else { // we should never get here pUnicast = pUnicast->Next; continue; } if (intRet == NULL) { PyErr_SetFromWindowsErr(GetLastError()); goto error; } #if PY_MAJOR_VERSION >= 3 py_address = PyUnicode_FromString(buff); #else py_address = PyString_FromString(buff); #endif if (py_address == NULL) goto error; Py_INCREF(Py_None); Py_INCREF(Py_None); py_tuple = Py_BuildValue( "(siOOO)", ifname, family, py_address, Py_None, Py_None ); if (! py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; Py_DECREF(py_tuple); Py_DECREF(py_address); pUnicast = pUnicast->Next; } } pCurrAddresses = pCurrAddresses->Next; } free(pAddresses); return py_retlist; error: if (pAddresses) free(pAddresses); Py_DECREF(py_retlist); Py_XDECREF(py_tuple); Py_XDECREF(py_address); return NULL; } /* * Provides stats about NIC interfaces installed on the system. * TODO: get 'duplex' (currently it's hard coded to '2', aka 'full duplex') */ static PyObject * psutil_net_if_stats(PyObject *self, PyObject *args) { int i; DWORD dwSize = 0; DWORD dwRetVal = 0; MIB_IFTABLE *pIfTable; MIB_IFROW *pIfRow; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; char friendly_name[MAX_PATH]; char descr[MAX_PATH]; int ifname_found; PyObject *py_retdict = PyDict_New(); PyObject *py_ifc_info = NULL; PyObject *py_is_up = NULL; if (py_retdict == NULL) return NULL; pAddresses = psutil_get_nic_addresses(); if (pAddresses == NULL) goto error; pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); if (pIfTable == NULL) { PyErr_NoMemory(); goto error; } dwSize = sizeof(MIB_IFTABLE); if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) { free(pIfTable); pIfTable = (MIB_IFTABLE *) malloc(dwSize); if (pIfTable == NULL) { PyErr_NoMemory(); goto error; } } // Make a second call to GetIfTable to get the actual // data we want. if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) { PyErr_SetString(PyExc_RuntimeError, "GetIfTable() failed"); goto error; } for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { pIfRow = (MIB_IFROW *) & pIfTable->table[i]; // GetIfTable is not able to give us NIC with "friendly names" // so we determine them via GetAdapterAddresses() which // provides friendly names *and* descriptions and find the // ones that match. ifname_found = 0; pCurrAddresses = pAddresses; while (pCurrAddresses) { sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description); if (lstrcmp(descr, pIfRow->bDescr) == 0) { sprintf_s(friendly_name, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); ifname_found = 1; break; } pCurrAddresses = pCurrAddresses->Next; } if (ifname_found == 0) { // Name not found means GetAdapterAddresses() doesn't list // this NIC, only GetIfTable, meaning it's not really a NIC // interface so we skip it. continue; } // is up? if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && pIfRow->dwAdminStatus == 1 ) { py_is_up = Py_True; } else { py_is_up = Py_False; } Py_INCREF(py_is_up); py_ifc_info = Py_BuildValue( "(Oikk)", py_is_up, 2, // there's no way to know duplex so let's assume 'full' pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb pIfRow->dwMtu ); if (!py_ifc_info) goto error; if (PyDict_SetItemString(py_retdict, friendly_name, py_ifc_info)) goto error; Py_DECREF(py_ifc_info); } free(pIfTable); free(pAddresses); return py_retdict; error: Py_XDECREF(py_is_up); Py_XDECREF(py_ifc_info); Py_DECREF(py_retdict); if (pIfTable != NULL) free(pIfTable); if (pAddresses != NULL) free(pAddresses); return NULL; } // ------------------------ Python init --------------------------- static PyMethodDef PsutilMethods[] = { // --- per-process functions {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS, "Return process cmdline as a list of cmdline arguments"}, {"proc_exe", psutil_proc_exe, METH_VARARGS, "Return path of the process executable"}, {"proc_name", psutil_proc_name, METH_VARARGS, "Return process name"}, {"proc_kill", psutil_proc_kill, METH_VARARGS, "Kill the process identified by the given PID"}, {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS, "Return tuple of user/kern time for the given PID"}, {"proc_create_time", psutil_proc_create_time, METH_VARARGS, "Return a float indicating the process create time expressed in " "seconds since the epoch"}, {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, "Return a tuple of process memory information"}, {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, "Alternate implementation"}, {"proc_cwd", psutil_proc_cwd, METH_VARARGS, "Return process current working directory"}, {"proc_suspend", psutil_proc_suspend, METH_VARARGS, "Suspend a process"}, {"proc_resume", psutil_proc_resume, METH_VARARGS, "Resume a process"}, {"proc_open_files", psutil_proc_open_files, METH_VARARGS, "Return files opened by process"}, {"proc_username", psutil_proc_username, METH_VARARGS, "Return the username of a process"}, {"proc_threads", psutil_proc_threads, METH_VARARGS, "Return process threads information as a list of tuple"}, {"proc_wait", psutil_proc_wait, METH_VARARGS, "Wait for process to terminate and return its exit code."}, {"proc_priority_get", psutil_proc_priority_get, METH_VARARGS, "Return process priority."}, {"proc_priority_set", psutil_proc_priority_set, METH_VARARGS, "Set process priority."}, #if (_WIN32_WINNT >= 0x0600) // Windows Vista {"proc_io_priority_get", psutil_proc_io_priority_get, METH_VARARGS, "Return process IO priority."}, {"proc_io_priority_set", psutil_proc_io_priority_set, METH_VARARGS, "Set process IO priority."}, #endif {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS, "Return process CPU affinity as a bitmask."}, {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS, "Set process CPU affinity."}, {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS, "Get process I/O counters."}, {"proc_is_suspended", psutil_proc_is_suspended, METH_VARARGS, "Return True if one of the process threads is in a suspended state"}, {"proc_num_handles", psutil_proc_num_handles, METH_VARARGS, "Return the number of handles opened by process."}, {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, "Return a list of process's memory mappings"}, // --- alternative pinfo interface {"proc_info", psutil_proc_info, METH_VARARGS, "Various process information"}, // --- system-related functions {"pids", psutil_pids, METH_VARARGS, "Returns a list of PIDs currently running on the system"}, {"ppid_map", psutil_ppid_map, METH_VARARGS, "Return a {pid:ppid, ...} dict for all running processes"}, {"pid_exists", psutil_pid_exists, METH_VARARGS, "Determine if the process exists in the current process list."}, {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS, "Returns the number of logical CPUs on the system"}, {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS, "Returns the number of physical CPUs on the system"}, {"boot_time", psutil_boot_time, METH_VARARGS, "Return the system boot time expressed in seconds since the epoch."}, {"virtual_mem", psutil_virtual_mem, METH_VARARGS, "Return the total amount of physical memory, in bytes"}, {"cpu_times", psutil_cpu_times, METH_VARARGS, "Return system cpu times as a list"}, {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS, "Return system per-cpu times as a list of tuples"}, {"disk_usage", psutil_disk_usage, METH_VARARGS, "Return path's disk total and free as a Python tuple."}, {"net_io_counters", psutil_net_io_counters, METH_VARARGS, "Return dict of tuples of networks I/O information."}, {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, "Return dict of tuples of disks I/O information."}, {"users", psutil_users, METH_VARARGS, "Return a list of currently connected users."}, {"disk_partitions", psutil_disk_partitions, METH_VARARGS, "Return disk partitions."}, {"net_connections", psutil_net_connections, METH_VARARGS, "Return system-wide connections"}, {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS, "Return NICs addresses."}, {"net_if_stats", psutil_net_if_stats, METH_VARARGS, "Return NICs stats."}, // --- windows API bindings {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS, "QueryDosDevice binding"}, {NULL, NULL, 0, NULL} }; struct module_state { PyObject *error; }; #if PY_MAJOR_VERSION >= 3 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) #else #define GETSTATE(m) (&_state) static struct module_state _state; #endif #if PY_MAJOR_VERSION >= 3 static int psutil_windows_traverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GETSTATE(m)->error); return 0; } static int psutil_windows_clear(PyObject *m) { Py_CLEAR(GETSTATE(m)->error); return 0; } static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "psutil_windows", NULL, sizeof(struct module_state), PsutilMethods, NULL, psutil_windows_traverse, psutil_windows_clear, NULL }; #define INITERROR return NULL PyMODINIT_FUNC PyInit__psutil_windows(void) #else #define INITERROR return void init_psutil_windows(void) #endif { struct module_state *st = NULL; #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else PyObject *module = Py_InitModule("_psutil_windows", PsutilMethods); #endif if (module == NULL) { INITERROR; } st = GETSTATE(module); st->error = PyErr_NewException("_psutil_windows.Error", NULL, NULL); if (st->error == NULL) { Py_DECREF(module); INITERROR; } PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); // process status constants // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx PyModule_AddIntConstant( module, "ABOVE_NORMAL_PRIORITY_CLASS", ABOVE_NORMAL_PRIORITY_CLASS); PyModule_AddIntConstant( module, "BELOW_NORMAL_PRIORITY_CLASS", BELOW_NORMAL_PRIORITY_CLASS); PyModule_AddIntConstant( module, "HIGH_PRIORITY_CLASS", HIGH_PRIORITY_CLASS); PyModule_AddIntConstant( module, "IDLE_PRIORITY_CLASS", IDLE_PRIORITY_CLASS); PyModule_AddIntConstant( module, "NORMAL_PRIORITY_CLASS", NORMAL_PRIORITY_CLASS); PyModule_AddIntConstant( module, "REALTIME_PRIORITY_CLASS", REALTIME_PRIORITY_CLASS); // connection status constants // http://msdn.microsoft.com/en-us/library/cc669305.aspx PyModule_AddIntConstant( module, "MIB_TCP_STATE_CLOSED", MIB_TCP_STATE_CLOSED); PyModule_AddIntConstant( module, "MIB_TCP_STATE_CLOSING", MIB_TCP_STATE_CLOSING); PyModule_AddIntConstant( module, "MIB_TCP_STATE_CLOSE_WAIT", MIB_TCP_STATE_CLOSE_WAIT); PyModule_AddIntConstant( module, "MIB_TCP_STATE_LISTEN", MIB_TCP_STATE_LISTEN); PyModule_AddIntConstant( module, "MIB_TCP_STATE_ESTAB", MIB_TCP_STATE_ESTAB); PyModule_AddIntConstant( module, "MIB_TCP_STATE_SYN_SENT", MIB_TCP_STATE_SYN_SENT); PyModule_AddIntConstant( module, "MIB_TCP_STATE_SYN_RCVD", MIB_TCP_STATE_SYN_RCVD); PyModule_AddIntConstant( module, "MIB_TCP_STATE_FIN_WAIT1", MIB_TCP_STATE_FIN_WAIT1); PyModule_AddIntConstant( module, "MIB_TCP_STATE_FIN_WAIT2", MIB_TCP_STATE_FIN_WAIT2); PyModule_AddIntConstant( module, "MIB_TCP_STATE_LAST_ACK", MIB_TCP_STATE_LAST_ACK); PyModule_AddIntConstant( module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); PyModule_AddIntConstant( module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT); PyModule_AddIntConstant( module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB); PyModule_AddIntConstant( module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); // ...for internal use in _psutil_windows.py PyModule_AddIntConstant( module, "INFINITE", INFINITE); PyModule_AddIntConstant( module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED); // set SeDebug for the current process psutil_set_se_debug(); #if PY_MAJOR_VERSION >= 3 return module; #endif }