diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-01-02 13:05:10 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-02 13:05:10 -0800 |
commit | 1358fb7d734878b7e238c39af93ec19dfbe7c77e (patch) | |
tree | 8a652270f50a7e10c04e8195d24a25b6ff4c2434 | |
parent | ac61e733137dbd392635a8573db58c58207eb671 (diff) | |
download | psutil-1358fb7d734878b7e238c39af93ec19dfbe7c77e.tar.gz |
Windows: split C modules (#1655)
-rw-r--r-- | psutil/_psutil_windows.c | 1077 | ||||
-rw-r--r-- | psutil/arch/windows/cpu.c | 413 | ||||
-rw-r--r-- | psutil/arch/windows/cpu.h | 14 | ||||
-rw-r--r-- | psutil/arch/windows/disk.c | 375 | ||||
-rw-r--r-- | psutil/arch/windows/disk.h | 12 | ||||
-rw-r--r-- | psutil/arch/windows/global.h | 3 | ||||
-rw-r--r-- | psutil/arch/windows/net.c | 449 | ||||
-rw-r--r-- | psutil/arch/windows/net.h | 11 | ||||
-rw-r--r-- | psutil/arch/windows/ntextapi.h | 10 | ||||
-rw-r--r-- | psutil/arch/windows/process_handles.c | 4 | ||||
-rw-r--r-- | psutil/arch/windows/process_info.c | 24 | ||||
-rw-r--r-- | psutil/arch/windows/process_info.h | 11 | ||||
-rw-r--r-- | psutil/arch/windows/process_utils.c | 3 | ||||
-rw-r--r-- | psutil/arch/windows/services.h | 2 | ||||
-rw-r--r-- | psutil/arch/windows/socks.c | 2 | ||||
-rw-r--r-- | psutil/arch/windows/wmi.c | 4 | ||||
-rw-r--r-- | psutil/arch/windows/wmi.h | 4 | ||||
-rwxr-xr-x | setup.py | 3 |
18 files changed, 1309 insertions, 1112 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 5ab41695..9c430c5f 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -19,14 +19,10 @@ #include <Python.h> #include <windows.h> -#include <Psapi.h> +#include <Psapi.h> // memory_info(), memory_maps() #include <signal.h> -#include <WinIoCtl.h> // disk_io_counters() -#include <tchar.h> -#include <tlhelp32.h> +#include <tlhelp32.h> // threads(), PROCESSENTRY32 #include <wtsapi32.h> // users() -#include <PowrProf.h> // cpu_freq() -#include <ws2tcpip.h> // net_io_counters() // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") @@ -37,64 +33,18 @@ #include "arch/windows/process_utils.h" #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" +#include "arch/windows/disk.h" +#include "arch/windows/cpu.h" +#include "arch/windows/net.h" #include "arch/windows/inet_ntop.h" #include "arch/windows/services.h" #include "arch/windows/socks.h" #include "arch/windows/wmi.h" #include "_psutil_common.h" - -/* - * ============================================================================ - * Utilities - * ============================================================================ - */ - -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) -#define LO_T 1e-7 -#define HI_T 429.4967296 -#ifndef AF_INET6 -#define AF_INET6 23 -#endif - - -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() syscall failed."); - return NULL; - } - - return pAddresses; -} +// Raised by Process.wait(). +static PyObject *TimeoutExpired; +static PyObject *TimeoutAbandoned; /* @@ -127,16 +77,6 @@ psutil_get_num_cpus(int fail_on_err) { /* - * ============================================================================ - * Public Python API - * ============================================================================ - */ - -// Raised by Process.wait(). -static PyObject *TimeoutExpired; -static PyObject *TimeoutAbandoned; - -/* * Return a Python float representing the system uptime expressed in seconds * since the epoch. */ @@ -466,104 +406,6 @@ psutil_proc_create_time(PyObject *self, PyObject *args) { /* - * Return the number of active, logical CPUs. - */ -static PyObject * -psutil_cpu_count_logical(PyObject *self, PyObject *args) { - unsigned int ncpus; - - ncpus = psutil_get_num_cpus(0); - if (ncpus != 0) - return Py_BuildValue("I", ncpus); - else - Py_RETURN_NONE; // mimick os.cpu_count() -} - - -/* - * Return the number of physical CPU cores (hyper-thread CPUs count - * is excluded). - */ -static PyObject * -psutil_cpu_count_phys(PyObject *self, PyObject *args) { - DWORD rc; - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL; - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL; - DWORD length = 0; - DWORD offset = 0; - DWORD ncpus = 0; - DWORD prev_processor_info_size = 0; - - // GetLogicalProcessorInformationEx() is available from Windows 7 - // onward. Differently from GetLogicalProcessorInformation() - // it supports process groups, meaning this is able to report more - // than 64 CPUs. See: - // https://bugs.python.org/issue33166 - if (psutil_GetLogicalProcessorInformationEx == NULL) { - psutil_debug("Win < 7; cpu_count_phys() forced to None"); - Py_RETURN_NONE; - } - - while (1) { - rc = psutil_GetLogicalProcessorInformationEx( - RelationAll, buffer, &length); - if (rc == FALSE) { - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - if (buffer) { - free(buffer); - } - buffer = \ - (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length); - if (NULL == buffer) { - PyErr_NoMemory(); - return NULL; - } - } - else { - psutil_debug("GetLogicalProcessorInformationEx() returned ", - GetLastError()); - goto return_none; - } - } - else { - break; - } - } - - ptr = buffer; - while (offset < length) { - // Advance ptr by the size of the previous - // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. - ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)\ - (((char*)ptr) + prev_processor_info_size); - - if (ptr->Relationship == RelationProcessorCore) { - ncpus += 1; - } - - // When offset == length, we've reached the last processor - // info struct in the buffer. - offset += ptr->Size; - prev_processor_info_size = ptr->Size; - } - - free(buffer); - if (ncpus != 0) { - return Py_BuildValue("I", ncpus); - } - else { - psutil_debug("GetLogicalProcessorInformationEx() count was 0"); - Py_RETURN_NONE; // mimick os.cpu_count() - } - -return_none: - if (buffer != NULL) - free(buffer); - Py_RETURN_NONE; -} - - -/* * Return process cmdline as a Python list of cmdline arguments. */ static PyObject * @@ -1175,35 +1017,6 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { /* - 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 * @@ -1574,418 +1387,6 @@ psutil_proc_is_suspended(PyObject *self, PyObject *args) { /* - * 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_SetFromWindowsErrWithFilename(0, path); - 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) { - DWORD dwRetVal = 0; - MIB_IF_ROW2 *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_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); - if (pIfRow == NULL) { - PyErr_NoMemory(); - goto error; - } - - SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); - pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; - dwRetVal = GetIfEntry2(pIfRow); - if (dwRetVal != NO_ERROR) { - PyErr_SetString(PyExc_RuntimeError, - "GetIfEntry() or GetIfEntry2() syscalls failed."); - goto error; - } - - py_nic_info = Py_BuildValue("(KKKKKKKK)", - pIfRow->OutOctets, - pIfRow->InOctets, - (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts), - (pIfRow->InUcastPkts + pIfRow->InNUcastPkts), - pIfRow->InErrors, - pIfRow->OutErrors, - pIfRow->InDiscards, - pIfRow->OutDiscards); - if (!py_nic_info) - goto error; - - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - - if (py_nic_name == NULL) - goto error; - if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) - goto error; - Py_CLEAR(py_nic_name); - Py_CLEAR(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. This may - * require running "diskperf -y" command first. - */ -static PyObject * -psutil_disk_io_counters(PyObject *self, PyObject *args) { - DISK_PERFORMANCE diskPerformance; - DWORD dwSize; - HANDLE hDevice = NULL; - char szDevice[MAX_PATH]; - char szDeviceDisplay[MAX_PATH]; - int devNum; - int i; - DWORD ioctrlSize; - BOOL ret; - PyObject *py_retdict = PyDict_New(); - PyObject *py_tuple = 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_tuple = 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; - - // DeviceIoControl() sucks! - i = 0; - ioctrlSize = sizeof(diskPerformance); - while (1) { - i += 1; - ret = DeviceIoControl( - hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance, - ioctrlSize, &dwSize, NULL); - if (ret != 0) - break; // OK! - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - // Retry with a bigger buffer (+ limit for retries). - if (i <= 1024) { - ioctrlSize *= 2; - continue; - } - } - else if (GetLastError() == ERROR_INVALID_FUNCTION) { - // This happens on AppVeyor: - // https://ci.appveyor.com/project/giampaolo/psutil/build/ - // 1364/job/ascpdi271b06jle3 - // Assume it means we're dealing with some exotic disk - // and go on. - psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; " - "ignore PhysicalDrive%i", devNum); - goto next; - } - else if (GetLastError() == ERROR_NOT_SUPPORTED) { - // Again, let's assume we're dealing with some exotic disk. - psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; " - "ignore PhysicalDrive%i", devNum); - goto next; - } - // XXX: it seems we should also catch ERROR_INVALID_PARAMETER: - // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ - // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/ - // openafs-1.4.14/src/usd/usd_nt.c - - // XXX: we can also bump into ERROR_MORE_DATA in which case - // (quoting doc) we're supposed to retry with a bigger buffer - // and specify a new "starting point", whatever it means. - PyErr_SetFromWindowsErr(0); - goto error; - } - - sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum); - py_tuple = Py_BuildValue( - "(IILLKK)", - diskPerformance.ReadCount, - diskPerformance.WriteCount, - diskPerformance.BytesRead, - diskPerformance.BytesWritten, - // convert to ms: - // https://github.com/giampaolo/psutil/issues/1012 - (unsigned long long) - (diskPerformance.ReadTime.QuadPart) / 10000000, - (unsigned long long) - (diskPerformance.WriteTime.QuadPart) / 10000000); - if (!py_tuple) - goto error; - if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple)) - goto error; - Py_CLEAR(py_tuple); - -next: - CloseHandle(hDevice); - } - - return py_retdict; - -error: - Py_XDECREF(py_tuple); - 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; - char mp_buf[MAX_PATH]; - char mp_path[MAX_PATH]; - int all; - int type; - int ret; - unsigned int old_mode = 0; - char opts[20]; - HANDLE mp_h; - BOOL mp_flag= TRUE; - 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 - old_mode = 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"); - - // Check for mount points on this volume and add/get info - // (checks first to know if we can even have mount points) - if (pflags & FILE_SUPPORTS_REPARSE_POINTS) { - - mp_h = FindFirstVolumeMountPoint(drive_letter, mp_buf, MAX_PATH); - if (mp_h != INVALID_HANDLE_VALUE) { - while (mp_flag) { - - // Append full mount path with drive letter - strcpy_s(mp_path, _countof(mp_path), drive_letter); - strcat_s(mp_path, _countof(mp_path), mp_buf); - - py_tuple = Py_BuildValue( - "(ssss)", - drive_letter, - mp_path, - fs_type, // Typically NTFS - opts); - - if (!py_tuple || PyList_Append(py_retlist, py_tuple) == -1) { - FindVolumeMountPointClose(mp_h); - goto error; - } - - Py_CLEAR(py_tuple); - - // Continue looking for more mount points - mp_flag = FindNextVolumeMountPoint(mp_h, mp_buf, MAX_PATH); - } - FindVolumeMountPointClose(mp_h); - } - - } - } - - 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_CLEAR(py_tuple); - goto next; - -next: - drive_letter = strchr(drive_letter, 0) + 1; - } - - SetErrorMode(old_mode); - return py_retlist; - -error: - SetErrorMode(old_mode); - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - return NULL; -} - -/* * Return a Python dict of tuples for disk I/O information */ static PyObject * @@ -2382,468 +1783,6 @@ error: /* - * Return NICs addresses. - */ - -static PyObject * -psutil_net_if_addrs(PyObject *self, PyObject *args) { - unsigned int i = 0; - ULONG family; - PCTSTR intRet; - PCTSTR netmaskIntRet; - char *ptr; - char buff_addr[1024]; - char buff_macaddr[1024]; - char buff_netmask[1024]; - DWORD dwRetVal = 0; - ULONG converted_netmask; - UINT netmask_bits; - struct in_addr in_netmask; - 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; - PyObject *py_nic_name = NULL; - PyObject *py_netmask = NULL; - - if (py_retlist == NULL) - return NULL; - - pAddresses = psutil_get_nic_addresses(); - if (pAddresses == NULL) - goto error; - pCurrAddresses = pAddresses; - - while (pCurrAddresses) { - pUnicast = pCurrAddresses->FirstUnicastAddress; - - netmaskIntRet = NULL; - py_nic_name = NULL; - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - if (py_nic_name == NULL) - goto error; - - // MAC address - if (pCurrAddresses->PhysicalAddressLength != 0) { - ptr = buff_macaddr; - *ptr = '\0'; - for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { - if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { - sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n", - (int)pCurrAddresses->PhysicalAddress[i]); - } - else { - sprintf_s(ptr, _countof(buff_macaddr), "%.2X-", - (int)pCurrAddresses->PhysicalAddress[i]); - } - ptr += 3; - } - *--ptr = '\0'; - - py_mac_address = Py_BuildValue("s", buff_macaddr); - if (py_mac_address == NULL) - goto error; - - Py_INCREF(Py_None); - Py_INCREF(Py_None); - Py_INCREF(Py_None); - py_tuple = Py_BuildValue( - "(OiOOOO)", - py_nic_name, - -1, // this will be converted later to AF_LINK - py_mac_address, - Py_None, // netmask (not supported) - Py_None, // broadcast (not supported) - Py_None // ptp (not supported on Windows) - ); - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_CLEAR(py_tuple); - Py_CLEAR(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_addr, - sizeof(buff_addr)); - if (!intRet) - goto error; - netmask_bits = pUnicast->OnLinkPrefixLength; - dwRetVal = ConvertLengthToIpv4Mask(netmask_bits, &converted_netmask); - if (dwRetVal == NO_ERROR) { - in_netmask.s_addr = converted_netmask; - netmaskIntRet = inet_ntop( - AF_INET, &in_netmask, buff_netmask, - sizeof(buff_netmask)); - if (!netmaskIntRet) - goto error; - } - } - 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_addr, sizeof(buff_addr)); - if (!intRet) - goto error; - } - else { - // we should never get here - pUnicast = pUnicast->Next; - continue; - } - -#if PY_MAJOR_VERSION >= 3 - py_address = PyUnicode_FromString(buff_addr); -#else - py_address = PyString_FromString(buff_addr); -#endif - if (py_address == NULL) - goto error; - - if (netmaskIntRet != NULL) { -#if PY_MAJOR_VERSION >= 3 - py_netmask = PyUnicode_FromString(buff_netmask); -#else - py_netmask = PyString_FromString(buff_netmask); -#endif - } else { - Py_INCREF(Py_None); - py_netmask = Py_None; - } - - Py_INCREF(Py_None); - Py_INCREF(Py_None); - py_tuple = Py_BuildValue( - "(OiOOOO)", - py_nic_name, - family, - py_address, - py_netmask, - Py_None, // broadcast (not supported) - Py_None // ptp (not supported on Windows) - ); - - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_CLEAR(py_tuple); - Py_CLEAR(py_address); - Py_CLEAR(py_netmask); - - pUnicast = pUnicast->Next; - } - } - Py_CLEAR(py_nic_name); - 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); - Py_XDECREF(py_nic_name); - Py_XDECREF(py_netmask); - 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 descr[MAX_PATH]; - int ifname_found; - - PyObject *py_nic_name = NULL; - 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() syscall 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) { - py_nic_name = PyUnicode_FromWideChar( - pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - if (py_nic_name == NULL) - goto error; - 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_SetItem(py_retdict, py_nic_name, py_ifc_info)) - goto error; - Py_CLEAR(py_nic_name); - Py_CLEAR(py_ifc_info); - } - - free(pIfTable); - free(pAddresses); - return py_retdict; - -error: - Py_XDECREF(py_is_up); - Py_XDECREF(py_ifc_info); - Py_XDECREF(py_nic_name); - Py_DECREF(py_retdict); - if (pIfTable != NULL) - free(pIfTable); - if (pAddresses != NULL) - free(pAddresses); - return NULL; -} - - -/* - * Return CPU statistics. - */ -static PyObject * -psutil_cpu_stats(PyObject *self, PyObject *args) { - NTSTATUS status; - _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; - _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; - _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL; - unsigned int ncpus; - UINT i; - ULONG64 dpcs = 0; - ULONG interrupts = 0; - - // retrieves number of processors - ncpus = psutil_get_num_cpus(1); - if (ncpus == 0) - goto error; - - // get syscalls / ctx switches - spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \ - malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)); - if (spi == NULL) { - PyErr_NoMemory(); - goto error; - } - status = psutil_NtQuerySystemInformation( - SystemPerformanceInformation, - spi, - ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemPerformanceInformation)"); - goto error; - } - - // get DPCs - InterruptInformation = \ - malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus); - if (InterruptInformation == NULL) { - PyErr_NoMemory(); - goto error; - } - - status = psutil_NtQuerySystemInformation( - SystemInterruptInformation, - InterruptInformation, - ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, "NtQuerySystemInformation(SystemInterruptInformation)"); - goto error; - } - for (i = 0; i < ncpus; i++) { - dpcs += InterruptInformation[i].DpcCount; - } - - // get interrupts - sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ - malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); - if (sppi == NULL) { - PyErr_NoMemory(); - goto error; - } - - status = psutil_NtQuerySystemInformation( - SystemProcessorPerformanceInformation, - sppi, - ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), - NULL); - if (! NT_SUCCESS(status)) { - psutil_SetFromNTStatusErr( - status, - "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"); - goto error; - } - - for (i = 0; i < ncpus; i++) { - interrupts += sppi[i].InterruptCount; - } - - // done - free(spi); - free(InterruptInformation); - free(sppi); - return Py_BuildValue( - "kkkk", - spi->ContextSwitches, - interrupts, - (unsigned long)dpcs, - spi->SystemCalls - ); - -error: - if (spi) - free(spi); - if (InterruptInformation) - free(InterruptInformation); - if (sppi) - free(sppi); - return NULL; -} - - -/* - * Return CPU frequency. - */ -static PyObject * -psutil_cpu_freq(PyObject *self, PyObject *args) { - PROCESSOR_POWER_INFORMATION *ppi; - NTSTATUS ret; - ULONG size; - LPBYTE pBuffer = NULL; - ULONG current; - ULONG max; - unsigned int ncpus; - - // Get the number of CPUs. - ncpus = psutil_get_num_cpus(1); - if (ncpus == 0) - return NULL; - - // Allocate size. - size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION); - pBuffer = (BYTE*)LocalAlloc(LPTR, size); - if (! pBuffer) - return PyErr_SetFromWindowsErr(0); - - // Syscall. - ret = CallNtPowerInformation( - ProcessorInformation, NULL, 0, pBuffer, size); - if (ret != 0) { - PyErr_SetString(PyExc_RuntimeError, - "CallNtPowerInformation syscall failed"); - goto error; - } - - // Results. - ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer; - max = ppi->MaxMhz; - current = ppi->CurrentMhz; - LocalFree(pBuffer); - - return Py_BuildValue("kk", current, max); - -error: - if (pBuffer != NULL) - LocalFree(pBuffer); - return NULL; -} - - -/* * Return battery usage stats. */ static PyObject * diff --git a/psutil/arch/windows/cpu.c b/psutil/arch/windows/cpu.c new file mode 100644 index 00000000..e891dcc8 --- /dev/null +++ b/psutil/arch/windows/cpu.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <Python.h> +#include <windows.h> +#include <PowrProf.h> + +#include "ntextapi.h" +#include "global.h" +#include "../../_psutil_common.h" + + +/* + * Return the number of logical, active CPUs. Return 0 if undetermined. + * See discussion at: https://bugs.python.org/issue33166#msg314631 + */ +static unsigned int +psutil_get_num_cpus(int fail_on_err) { + unsigned int ncpus = 0; + + // Minimum requirement: Windows 7 + if (psutil_GetActiveProcessorCount != NULL) { + ncpus = psutil_GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + if ((ncpus == 0) && (fail_on_err == 1)) { + PyErr_SetFromWindowsErr(0); + } + } + else { + psutil_debug("GetActiveProcessorCount() not available; " + "using GetSystemInfo()"); + ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors; + if ((ncpus <= 0) && (fail_on_err == 1)) { + PyErr_SetString( + PyExc_RuntimeError, + "GetSystemInfo() failed to retrieve CPU count"); + } + } + return ncpus; +} + + +/* + * 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. + */ +PyObject * +psutil_cpu_times(PyObject *self, PyObject *args) { + double idle, kernel, user, system; + FILETIME idle_time, kernel_time, user_time; + + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) + return PyErr_SetFromWindowsErr(0); + + idle = (double)((HI_T * idle_time.dwHighDateTime) + \ + (LO_T * idle_time.dwLowDateTime)); + user = (double)((HI_T * user_time.dwHighDateTime) + \ + (LO_T * user_time.dwLowDateTime)); + kernel = (double)((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("(ddd)", user, system, idle); +} + + +/* + * Same as above but for all system CPUs. + */ +PyObject * +psutil_per_cpu_times(PyObject *self, PyObject *args) { + double idle, kernel, systemt, user, interrupt, dpc; + NTSTATUS status; + _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + UINT i; + unsigned int ncpus; + PyObject *py_tuple = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return NULL; + + // retrieves number of processors + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + goto error; + + // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + // structures, one per processor + sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi == NULL) { + PyErr_NoMemory(); + goto error; + } + + // gets cpu time informations + status = psutil_NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtQuerySystemInformation(SystemProcessorPerformanceInformation)" + ); + goto error; + } + + // computes system global times summing each + // processor value + idle = user = kernel = interrupt = dpc = 0; + for (i = 0; i < ncpus; i++) { + py_tuple = NULL; + user = (double)((HI_T * sppi[i].UserTime.HighPart) + + (LO_T * sppi[i].UserTime.LowPart)); + idle = (double)((HI_T * sppi[i].IdleTime.HighPart) + + (LO_T * sppi[i].IdleTime.LowPart)); + kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) + + (LO_T * sppi[i].KernelTime.LowPart)); + interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) + + (LO_T * sppi[i].InterruptTime.LowPart)); + dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) + + (LO_T * sppi[i].DpcTime.LowPart)); + + // kernel time includes idle time on windows + // we return only busy kernel time subtracting + // idle time from kernel time + systemt = kernel - idle; + py_tuple = Py_BuildValue( + "(ddddd)", + user, + systemt, + idle, + interrupt, + dpc + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + } + + free(sppi); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (sppi) + free(sppi); + return NULL; +} + + +/* + * Return the number of active, logical CPUs. + */ +PyObject * +psutil_cpu_count_logical(PyObject *self, PyObject *args) { + unsigned int ncpus; + + ncpus = psutil_get_num_cpus(0); + if (ncpus != 0) + return Py_BuildValue("I", ncpus); + else + Py_RETURN_NONE; // mimick os.cpu_count() +} + + +/* + * Return the number of physical CPU cores (hyper-thread CPUs count + * is excluded). + */ +PyObject * +psutil_cpu_count_phys(PyObject *self, PyObject *args) { + DWORD rc; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL; + DWORD length = 0; + DWORD offset = 0; + DWORD ncpus = 0; + DWORD prev_processor_info_size = 0; + + // GetLogicalProcessorInformationEx() is available from Windows 7 + // onward. Differently from GetLogicalProcessorInformation() + // it supports process groups, meaning this is able to report more + // than 64 CPUs. See: + // https://bugs.python.org/issue33166 + if (psutil_GetLogicalProcessorInformationEx == NULL) { + psutil_debug("Win < 7; cpu_count_phys() forced to None"); + Py_RETURN_NONE; + } + + while (1) { + rc = psutil_GetLogicalProcessorInformationEx( + RelationAll, buffer, &length); + if (rc == FALSE) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + if (buffer) { + free(buffer); + } + buffer = \ + (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length); + if (NULL == buffer) { + PyErr_NoMemory(); + return NULL; + } + } + else { + psutil_debug("GetLogicalProcessorInformationEx() returned ", + GetLastError()); + goto return_none; + } + } + else { + break; + } + } + + ptr = buffer; + while (offset < length) { + // Advance ptr by the size of the previous + // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)\ + (((char*)ptr) + prev_processor_info_size); + + if (ptr->Relationship == RelationProcessorCore) { + ncpus += 1; + } + + // When offset == length, we've reached the last processor + // info struct in the buffer. + offset += ptr->Size; + prev_processor_info_size = ptr->Size; + } + + free(buffer); + if (ncpus != 0) { + return Py_BuildValue("I", ncpus); + } + else { + psutil_debug("GetLogicalProcessorInformationEx() count was 0"); + Py_RETURN_NONE; // mimick os.cpu_count() + } + +return_none: + if (buffer != NULL) + free(buffer); + Py_RETURN_NONE; +} + + +/* + * Return CPU statistics. + */ +PyObject * +psutil_cpu_stats(PyObject *self, PyObject *args) { + NTSTATUS status; + _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; + _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL; + unsigned int ncpus; + UINT i; + ULONG64 dpcs = 0; + ULONG interrupts = 0; + + // retrieves number of processors + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + goto error; + + // get syscalls / ctx switches + spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)); + if (spi == NULL) { + PyErr_NoMemory(); + goto error; + } + status = psutil_NtQuerySystemInformation( + SystemPerformanceInformation, + spi, + ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, "NtQuerySystemInformation(SystemPerformanceInformation)"); + goto error; + } + + // get DPCs + InterruptInformation = \ + malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus); + if (InterruptInformation == NULL) { + PyErr_NoMemory(); + goto error; + } + + status = psutil_NtQuerySystemInformation( + SystemInterruptInformation, + InterruptInformation, + ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, "NtQuerySystemInformation(SystemInterruptInformation)"); + goto error; + } + for (i = 0; i < ncpus; i++) { + dpcs += InterruptInformation[i].DpcCount; + } + + // get interrupts + sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi == NULL) { + PyErr_NoMemory(); + goto error; + } + + status = psutil_NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL); + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr( + status, + "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"); + goto error; + } + + for (i = 0; i < ncpus; i++) { + interrupts += sppi[i].InterruptCount; + } + + // done + free(spi); + free(InterruptInformation); + free(sppi); + return Py_BuildValue( + "kkkk", + spi->ContextSwitches, + interrupts, + (unsigned long)dpcs, + spi->SystemCalls + ); + +error: + if (spi) + free(spi); + if (InterruptInformation) + free(InterruptInformation); + if (sppi) + free(sppi); + return NULL; +} + + +/* + * Return CPU frequency. + */ +PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + PROCESSOR_POWER_INFORMATION *ppi; + NTSTATUS ret; + ULONG size; + LPBYTE pBuffer = NULL; + ULONG current; + ULONG max; + unsigned int ncpus; + + // Get the number of CPUs. + ncpus = psutil_get_num_cpus(1); + if (ncpus == 0) + return NULL; + + // Allocate size. + size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION); + pBuffer = (BYTE*)LocalAlloc(LPTR, size); + if (! pBuffer) + return PyErr_SetFromWindowsErr(0); + + // Syscall. + ret = CallNtPowerInformation( + ProcessorInformation, NULL, 0, pBuffer, size); + if (ret != 0) { + PyErr_SetString(PyExc_RuntimeError, + "CallNtPowerInformation syscall failed"); + goto error; + } + + // Results. + ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer; + max = ppi->MaxMhz; + current = ppi->CurrentMhz; + LocalFree(pBuffer); + + return Py_BuildValue("kk", current, max); + +error: + if (pBuffer != NULL) + LocalFree(pBuffer); + return NULL; +} diff --git a/psutil/arch/windows/cpu.h b/psutil/arch/windows/cpu.h new file mode 100644 index 00000000..d88c2212 --- /dev/null +++ b/psutil/arch/windows/cpu.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <Python.h> + +PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args); +PyObject *psutil_cpu_count_phys(PyObject *self, PyObject *args); +PyObject *psutil_cpu_freq(PyObject *self, PyObject *args); +PyObject *psutil_cpu_stats(PyObject *self, PyObject *args); +PyObject *psutil_cpu_times(PyObject *self, PyObject *args); +PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args); diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c new file mode 100644 index 00000000..821f687d --- /dev/null +++ b/psutil/arch/windows/disk.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <Python.h> +#include <windows.h> +#include <tchar.h> + +#include "ntextapi.h" +#include "global.h" +#include "../../_psutil_common.h" + + +#ifndef _ARRAYSIZE +#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +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 "?"; + } +} + + +/* + * Return path's disk total and free as a Python tuple. + */ +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_SetFromWindowsErrWithFilename(0, path); + else + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); +} + + +/* + * Return a Python dict of tuples for disk I/O information. This may + * require running "diskperf -y" command first. + */ +PyObject * +psutil_disk_io_counters(PyObject *self, PyObject *args) { + DISK_PERFORMANCE diskPerformance; + DWORD dwSize; + HANDLE hDevice = NULL; + char szDevice[MAX_PATH]; + char szDeviceDisplay[MAX_PATH]; + int devNum; + int i; + DWORD ioctrlSize; + BOOL ret; + PyObject *py_retdict = PyDict_New(); + PyObject *py_tuple = 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_tuple = 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; + + // DeviceIoControl() sucks! + i = 0; + ioctrlSize = sizeof(diskPerformance); + while (1) { + i += 1; + ret = DeviceIoControl( + hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance, + ioctrlSize, &dwSize, NULL); + if (ret != 0) + break; // OK! + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + // Retry with a bigger buffer (+ limit for retries). + if (i <= 1024) { + ioctrlSize *= 2; + continue; + } + } + else if (GetLastError() == ERROR_INVALID_FUNCTION) { + // This happens on AppVeyor: + // https://ci.appveyor.com/project/giampaolo/psutil/build/ + // 1364/job/ascpdi271b06jle3 + // Assume it means we're dealing with some exotic disk + // and go on. + psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; " + "ignore PhysicalDrive%i", devNum); + goto next; + } + else if (GetLastError() == ERROR_NOT_SUPPORTED) { + // Again, let's assume we're dealing with some exotic disk. + psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; " + "ignore PhysicalDrive%i", devNum); + goto next; + } + // XXX: it seems we should also catch ERROR_INVALID_PARAMETER: + // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/ + // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/ + // openafs-1.4.14/src/usd/usd_nt.c + + // XXX: we can also bump into ERROR_MORE_DATA in which case + // (quoting doc) we're supposed to retry with a bigger buffer + // and specify a new "starting point", whatever it means. + PyErr_SetFromWindowsErr(0); + goto error; + } + + sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum); + py_tuple = Py_BuildValue( + "(IILLKK)", + diskPerformance.ReadCount, + diskPerformance.WriteCount, + diskPerformance.BytesRead, + diskPerformance.BytesWritten, + // convert to ms: + // https://github.com/giampaolo/psutil/issues/1012 + (unsigned long long) + (diskPerformance.ReadTime.QuadPart) / 10000000, + (unsigned long long) + (diskPerformance.WriteTime.QuadPart) / 10000000); + if (!py_tuple) + goto error; + if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + +next: + CloseHandle(hDevice); + } + + return py_retdict; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retdict); + if (hDevice != NULL) + CloseHandle(hDevice); + return NULL; +} + + +/* + * Return disk partitions as a list of tuples such as + * (drive_letter, drive_letter, type, "") + */ +PyObject * +psutil_disk_partitions(PyObject *self, PyObject *args) { + DWORD num_bytes; + char drive_strings[255]; + char *drive_letter = drive_strings; + char mp_buf[MAX_PATH]; + char mp_path[MAX_PATH]; + int all; + int type; + int ret; + unsigned int old_mode = 0; + char opts[20]; + HANDLE mp_h; + BOOL mp_flag= TRUE; + 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 + old_mode = 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"); + + // Check for mount points on this volume and add/get info + // (checks first to know if we can even have mount points) + if (pflags & FILE_SUPPORTS_REPARSE_POINTS) { + mp_h = FindFirstVolumeMountPoint( + drive_letter, mp_buf, MAX_PATH); + if (mp_h != INVALID_HANDLE_VALUE) { + while (mp_flag) { + + // Append full mount path with drive letter + strcpy_s(mp_path, _countof(mp_path), drive_letter); + strcat_s(mp_path, _countof(mp_path), mp_buf); + + py_tuple = Py_BuildValue( + "(ssss)", + drive_letter, + mp_path, + fs_type, // Typically NTFS + opts); + + if (!py_tuple || + PyList_Append(py_retlist, py_tuple) == -1) { + FindVolumeMountPointClose(mp_h); + goto error; + } + + Py_CLEAR(py_tuple); + + // Continue looking for more mount points + mp_flag = FindNextVolumeMountPoint( + mp_h, mp_buf, MAX_PATH); + } + FindVolumeMountPointClose(mp_h); + } + + } + } + + 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_CLEAR(py_tuple); + goto next; + +next: + drive_letter = strchr(drive_letter, 0) + 1; + } + + SetErrorMode(old_mode); + return py_retlist; + +error: + SetErrorMode(old_mode); + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; +} + + +/* + 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. +*/ +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", ""); +} diff --git a/psutil/arch/windows/disk.h b/psutil/arch/windows/disk.h new file mode 100644 index 00000000..298fb6ba --- /dev/null +++ b/psutil/arch/windows/disk.h @@ -0,0 +1,12 @@ +/* + * 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. + */ + +#include <Python.h> + +PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args); +PyObject *psutil_disk_partitions(PyObject *self, PyObject *args); +PyObject *psutil_disk_usage(PyObject *self, PyObject *args); +PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args); diff --git a/psutil/arch/windows/global.h b/psutil/arch/windows/global.h index 4cf77cde..18d9a640 100644 --- a/psutil/arch/windows/global.h +++ b/psutil/arch/windows/global.h @@ -18,6 +18,9 @@ extern SYSTEM_INFO PSUTIL_SYSTEM_INFO; #define PSUTIL_WINDOWS_10 100 #define PSUTIL_WINDOWS_NEW MAXLONG +#define LO_T 1e-7 +#define HI_T 429.4967296 + int psutil_load_globals(); PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); diff --git a/psutil/arch/windows/net.c b/psutil/arch/windows/net.c new file mode 100644 index 00000000..565e844c --- /dev/null +++ b/psutil/arch/windows/net.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Fixes clash between winsock2.h and windows.h +#define WIN32_LEAN_AND_MEAN + +#include <Python.h> +#include <windows.h> +#include <wchar.h> +#include <ws2tcpip.h> + +#include "ntextapi.h" +#include "global.h" + + +#ifndef AF_INET6 +#define AF_INET6 23 +#endif + + +static 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() syscall failed."); + return NULL; + } + + return pAddresses; +} + + +/* + * Return a Python list of named tuples with overall network I/O information + */ +PyObject * +psutil_net_io_counters(PyObject *self, PyObject *args) { + DWORD dwRetVal = 0; + MIB_IF_ROW2 *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_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2)); + if (pIfRow == NULL) { + PyErr_NoMemory(); + goto error; + } + + SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2)); + pIfRow->InterfaceIndex = pCurrAddresses->IfIndex; + dwRetVal = GetIfEntry2(pIfRow); + if (dwRetVal != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, + "GetIfEntry() or GetIfEntry2() syscalls failed."); + goto error; + } + + py_nic_info = Py_BuildValue( + "(KKKKKKKK)", + pIfRow->OutOctets, + pIfRow->InOctets, + (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts), + (pIfRow->InUcastPkts + pIfRow->InNUcastPkts), + pIfRow->InErrors, + pIfRow->OutErrors, + pIfRow->InDiscards, + pIfRow->OutDiscards); + if (!py_nic_info) + goto error; + + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + + if (py_nic_name == NULL) + goto error; + if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) + goto error; + Py_CLEAR(py_nic_name); + Py_CLEAR(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 NICs addresses. + */ +PyObject * +psutil_net_if_addrs(PyObject *self, PyObject *args) { + unsigned int i = 0; + ULONG family; + PCTSTR intRet; + PCTSTR netmaskIntRet; + char *ptr; + char buff_addr[1024]; + char buff_macaddr[1024]; + char buff_netmask[1024]; + DWORD dwRetVal = 0; + ULONG converted_netmask; + UINT netmask_bits; + struct in_addr in_netmask; + 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; + PyObject *py_nic_name = NULL; + PyObject *py_netmask = NULL; + + if (py_retlist == NULL) + return NULL; + + pAddresses = psutil_get_nic_addresses(); + if (pAddresses == NULL) + goto error; + pCurrAddresses = pAddresses; + + while (pCurrAddresses) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + + netmaskIntRet = NULL; + py_nic_name = NULL; + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; + + // MAC address + if (pCurrAddresses->PhysicalAddressLength != 0) { + ptr = buff_macaddr; + *ptr = '\0'; + for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) { + if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { + sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n", + (int)pCurrAddresses->PhysicalAddress[i]); + } + else { + sprintf_s(ptr, _countof(buff_macaddr), "%.2X-", + (int)pCurrAddresses->PhysicalAddress[i]); + } + ptr += 3; + } + *--ptr = '\0'; + + py_mac_address = Py_BuildValue("s", buff_macaddr); + if (py_mac_address == NULL) + goto error; + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(OiOOOO)", + py_nic_name, + -1, // this will be converted later to AF_LINK + py_mac_address, + Py_None, // netmask (not supported) + Py_None, // broadcast (not supported) + Py_None // ptp (not supported on Windows) + ); + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + Py_CLEAR(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_addr, + sizeof(buff_addr)); + if (!intRet) + goto error; + netmask_bits = pUnicast->OnLinkPrefixLength; + dwRetVal = ConvertLengthToIpv4Mask( + netmask_bits, &converted_netmask); + if (dwRetVal == NO_ERROR) { + in_netmask.s_addr = converted_netmask; + netmaskIntRet = inet_ntop( + AF_INET, &in_netmask, buff_netmask, + sizeof(buff_netmask)); + if (!netmaskIntRet) + goto error; + } + } + 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_addr, sizeof(buff_addr)); + if (!intRet) + goto error; + } + else { + // we should never get here + pUnicast = pUnicast->Next; + continue; + } + +#if PY_MAJOR_VERSION >= 3 + py_address = PyUnicode_FromString(buff_addr); +#else + py_address = PyString_FromString(buff_addr); +#endif + if (py_address == NULL) + goto error; + + if (netmaskIntRet != NULL) { +#if PY_MAJOR_VERSION >= 3 + py_netmask = PyUnicode_FromString(buff_netmask); +#else + py_netmask = PyString_FromString(buff_netmask); +#endif + } else { + Py_INCREF(Py_None); + py_netmask = Py_None; + } + + Py_INCREF(Py_None); + Py_INCREF(Py_None); + py_tuple = Py_BuildValue( + "(OiOOOO)", + py_nic_name, + family, + py_address, + py_netmask, + Py_None, // broadcast (not supported) + Py_None // ptp (not supported on Windows) + ); + + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + Py_CLEAR(py_address); + Py_CLEAR(py_netmask); + + pUnicast = pUnicast->Next; + } + } + Py_CLEAR(py_nic_name); + 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); + Py_XDECREF(py_nic_name); + Py_XDECREF(py_netmask); + return NULL; +} + + +/* + * Provides stats about NIC interfaces installed on the system. + * TODO: get 'duplex' (currently it's hard coded to '2', aka + 'full duplex') + */ +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 descr[MAX_PATH]; + int ifname_found; + + PyObject *py_nic_name = NULL; + 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() syscall 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) { + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; + 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_SetItem(py_retdict, py_nic_name, py_ifc_info)) + goto error; + Py_CLEAR(py_nic_name); + Py_CLEAR(py_ifc_info); + } + + free(pIfTable); + free(pAddresses); + return py_retdict; + +error: + Py_XDECREF(py_is_up); + Py_XDECREF(py_ifc_info); + Py_XDECREF(py_nic_name); + Py_DECREF(py_retdict); + if (pIfTable != NULL) + free(pIfTable); + if (pAddresses != NULL) + free(pAddresses); + return NULL; +} diff --git a/psutil/arch/windows/net.h b/psutil/arch/windows/net.h new file mode 100644 index 00000000..7a6158d1 --- /dev/null +++ b/psutil/arch/windows/net.h @@ -0,0 +1,11 @@ +/* + * 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. + */ + +#include <Python.h> + +PyObject *psutil_net_if_addrs(PyObject *self, PyObject *args); +PyObject *psutil_net_if_stats(PyObject *self, PyObject *args); +PyObject *psutil_net_io_counters(PyObject *self, PyObject *args); diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index b6f23d99..6fa38edb 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -10,11 +10,15 @@ typedef LONG NTSTATUS; -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 -#define STATUS_BUFFER_TOO_SMALL 0xC0000023L +// https://github.com/ajkhoury/TestDll/blob/master/nt_ddk.h +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) + #define SystemExtendedHandleInformation 64 #define MemoryWorkingSetInformation 0x1 -#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) /* * ================================================================ diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c index 40b209ee..04b30f11 100644 --- a/psutil/arch/windows/process_handles.c +++ b/psutil/arch/windows/process_handles.c @@ -6,14 +6,12 @@ */ #include <windows.h> -#include <Psapi.h> +#include <Psapi.h> // GetMappedFileName() #include <Python.h> #include "ntextapi.h" #include "global.h" -#include "process_handles.h" #include "process_utils.h" -#include "../../_psutil_common.h" CRITICAL_SECTION g_cs; BOOL g_initialized = FALSE; diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c index a9c2c131..757cabbe 100644 --- a/psutil/arch/windows/process_info.c +++ b/psutil/arch/windows/process_info.c @@ -9,17 +9,21 @@ #include <Python.h> #include <windows.h> -#include <Psapi.h> -#include <tlhelp32.h> #include "ntextapi.h" #include "global.h" -#include "security.h" -#include "process_info.h" #include "process_utils.h" #include "../../_psutil_common.h" +#define PSUTIL_FIRST_PROCESS(Processes) ( \ + (PSYSTEM_PROCESS_INFORMATION)(Processes)) +#define PSUTIL_NEXT_PROCESS(Process) ( \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ + (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL) + + // ==================================================================== // Helper structures to access the memory correctly. // Some of these might also be defined in the winternl.h header file @@ -124,21 +128,11 @@ typedef struct { #endif -#define PSUTIL_FIRST_PROCESS(Processes) ( \ - (PSYSTEM_PROCESS_INFORMATION)(Processes)) -#define PSUTIL_NEXT_PROCESS(Process) ( \ - ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ - (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ - ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL) - - // ==================================================================== -// Process and PIDs utiilties. +// Process / PEB functions. // ==================================================================== -#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) - /* Given a pointer into a process's memory, figure out how much data can be * read from it. */ static int diff --git a/psutil/arch/windows/process_info.h b/psutil/arch/windows/process_info.h index afbbb72d..110d01df 100644 --- a/psutil/arch/windows/process_info.h +++ b/psutil/arch/windows/process_info.h @@ -4,21 +4,10 @@ * found in the LICENSE file. */ -#if !defined(__PROCESS_INFO_H) -#define __PROCESS_INFO_H - #include <Python.h> -#include <windows.h> -#include "security.h" -#include "ntextapi.h" - -#define HANDLE_TO_PYNUM(handle) PyLong_FromUnsignedLong((unsigned long) handle) -#define PYNUM_TO_HANDLE(obj) ((HANDLE)PyLong_AsUnsignedLong(obj)) int psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retBuffer); PyObject* psutil_get_cmdline(long pid, int use_peb); PyObject* psutil_get_cwd(long pid); PyObject* psutil_get_environ(long pid); - -#endif diff --git a/psutil/arch/windows/process_utils.c b/psutil/arch/windows/process_utils.c index a81a3253..1a721ba5 100644 --- a/psutil/arch/windows/process_utils.c +++ b/psutil/arch/windows/process_utils.c @@ -8,8 +8,7 @@ #include <Python.h> #include <windows.h> -#include <Psapi.h> -#include <tlhelp32.h> +#include <Psapi.h> // EnumProcesses #include "ntextapi.h" #include "global.h" diff --git a/psutil/arch/windows/services.h b/psutil/arch/windows/services.h index 286ed232..ebcfa5ef 100644 --- a/psutil/arch/windows/services.h +++ b/psutil/arch/windows/services.h @@ -8,7 +8,7 @@ #include <Winsvc.h> SC_HANDLE psutil_get_service_handle( -char service_name, DWORD scm_access, DWORD access); + char service_name, DWORD scm_access, DWORD access); PyObject *psutil_winservice_enumerate(PyObject *self, PyObject *args); PyObject *psutil_winservice_query_config(PyObject *self, PyObject *args); PyObject *psutil_winservice_query_status(PyObject *self, PyObject *args); diff --git a/psutil/arch/windows/socks.c b/psutil/arch/windows/socks.c index b44a247b..5bf34151 100644 --- a/psutil/arch/windows/socks.c +++ b/psutil/arch/windows/socks.c @@ -9,9 +9,7 @@ #include <Python.h> #include <windows.h> -#if (_WIN32_WINNT >= 0x0600) // Windows >= Vista #include <ws2tcpip.h> -#endif #include "ntextapi.h" #include "global.h" diff --git a/psutil/arch/windows/wmi.c b/psutil/arch/windows/wmi.c index f43d790c..b790c08e 100644 --- a/psutil/arch/windows/wmi.c +++ b/psutil/arch/windows/wmi.c @@ -10,8 +10,6 @@ #include <windows.h> #include <pdh.h> -#include "../../_psutil_common.h" - // We use an exponentially weighted moving average, just like Unix systems do // https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation @@ -104,7 +102,7 @@ error: /* - * Gets the emulated 1 minute, 5 minute and 15 minute load averages + * Gets the emulated 1 minute, 5 minute and 15 minute load averages * (processor queue length) for the system. * `init_loadavg_counter` must be called before this function to engage the * mechanism that records load values. diff --git a/psutil/arch/windows/wmi.h b/psutil/arch/windows/wmi.h index 0210f2d6..311242a3 100644 --- a/psutil/arch/windows/wmi.h +++ b/psutil/arch/windows/wmi.h @@ -1,11 +1,9 @@ /* - * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - - #include <Python.h> PyObject* psutil_init_loadavg_counter(); @@ -145,6 +145,9 @@ if WINDOWS: 'psutil/arch/windows/process_utils.c', 'psutil/arch/windows/process_info.c', 'psutil/arch/windows/process_handles.c', + 'psutil/arch/windows/disk.c', + 'psutil/arch/windows/net.c', + 'psutil/arch/windows/cpu.c', 'psutil/arch/windows/security.c', 'psutil/arch/windows/inet_ntop.c', 'psutil/arch/windows/services.c', |