diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-01-02 12:09:01 -0800 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2020-01-02 12:09:01 -0800 |
commit | d13bd69d68e4077ab6f2e05c204fcc9fa635c9d9 (patch) | |
tree | a0f4e7ad08f6020048a9027b38c6d0bbb0b76985 | |
parent | ac61e733137dbd392635a8573db58c58207eb671 (diff) | |
download | psutil-win-refact.tar.gz |
move disk/net functions in their own C fileswin-refact
-rw-r--r-- | psutil/_psutil_windows.c | 787 | ||||
-rw-r--r-- | psutil/arch/windows/disk.c | 378 | ||||
-rw-r--r-- | psutil/arch/windows/disk.h | 12 | ||||
-rw-r--r-- | psutil/arch/windows/net.c | 449 | ||||
-rw-r--r-- | psutil/arch/windows/net.h | 11 | ||||
-rwxr-xr-x | setup.py | 2 |
6 files changed, 857 insertions, 782 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 5ab41695..e0105cb2 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -21,12 +21,10 @@ #include <windows.h> #include <Psapi.h> #include <signal.h> -#include <WinIoCtl.h> // disk_io_counters() #include <tchar.h> #include <tlhelp32.h> #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 +35,19 @@ #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/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)) +// Raised by Process.wait(). +static PyObject *TimeoutExpired; +static PyObject *TimeoutAbandoned; #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; -} /* @@ -127,16 +80,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. */ @@ -1574,418 +1517,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,314 +1913,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 * diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c new file mode 100644 index 00000000..f2eaeb60 --- /dev/null +++ b/psutil/arch/windows/disk.c @@ -0,0 +1,378 @@ +/* + * 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 <signal.h> +#include <Psapi.h> +#include <tlhelp32.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/net.c b/psutil/arch/windows/net.c new file mode 100644 index 00000000..f2aaecf2 --- /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 <ws2tcpip.h> + +#include "ntextapi.h" +#include "global.h" +#include "../../_psutil_common.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); @@ -145,6 +145,8 @@ 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/security.c', 'psutil/arch/windows/inet_ntop.c', 'psutil/arch/windows/services.c', |