summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-01-02 12:09:01 -0800
committerGiampaolo Rodola <g.rodola@gmail.com>2020-01-02 12:09:01 -0800
commitd13bd69d68e4077ab6f2e05c204fcc9fa635c9d9 (patch)
treea0f4e7ad08f6020048a9027b38c6d0bbb0b76985
parentac61e733137dbd392635a8573db58c58207eb671 (diff)
downloadpsutil-win-refact.tar.gz
move disk/net functions in their own C fileswin-refact
-rw-r--r--psutil/_psutil_windows.c787
-rw-r--r--psutil/arch/windows/disk.c378
-rw-r--r--psutil/arch/windows/disk.h12
-rw-r--r--psutil/arch/windows/net.c449
-rw-r--r--psutil/arch/windows/net.h11
-rwxr-xr-xsetup.py2
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);
diff --git a/setup.py b/setup.py
index 1ebad30a..4253bb30 100755
--- a/setup.py
+++ b/setup.py
@@ -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',