summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola' <g.rodola@gmail.com>2015-02-09 06:27:16 -0800
committerGiampaolo Rodola' <g.rodola@gmail.com>2015-02-09 06:27:16 -0800
commit2b983b5d294870a315292f5f006b717e5456b99a (patch)
treedfe35cd9f59a300c25a054ec2e064dc877dd9e16
parentea8853644e34ef3036912df77e8f1615e9251e87 (diff)
parente34c853ea6d48fd4e888d70a1501b152884cd332 (diff)
downloadpsutil-2b983b5d294870a315292f5f006b717e5456b99a.tar.gz
Merge pull request #588 from giampaolo/ifconfig-376
Ifconfig 376
-rw-r--r--docs/index.rst51
-rw-r--r--examples/ifconfig.py66
-rw-r--r--psutil/__init__.py43
-rw-r--r--psutil/_common.py2
-rw-r--r--psutil/_psbsd.py2
-rw-r--r--psutil/_pslinux.py4
-rw-r--r--psutil/_psosx.py2
-rw-r--r--psutil/_pssunos.py2
-rw-r--r--psutil/_psutil_posix.c175
-rw-r--r--psutil/_psutil_posix.h1
-rw-r--r--psutil/_psutil_windows.c174
-rw-r--r--psutil/_psutil_windows.h1
-rw-r--r--psutil/_pswindows.py2
-rw-r--r--setup.py2
-rw-r--r--test/_linux.py96
-rw-r--r--test/test_memory_leaks.py3
-rw-r--r--test/test_psutil.py53
17 files changed, 673 insertions, 6 deletions
diff --git a/docs/index.rst b/docs/index.rst
index 29972f98..92d7fe73 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -200,7 +200,7 @@ Memory
* **total**: total swap memory in bytes
* **used**: used swap memory in bytes
* **free**: free swap memory in bytes
- * **percent**: the percentage usage
+ * **percent**: the percentage usage calculated as ``(total - available) / total * 100``
* **sin**: the number of bytes the system has swapped in from disk
(cumulative)
* **sout**: the number of bytes the system has swapped out from disk
@@ -403,6 +403,47 @@ Network
.. versionadded:: 2.1.0
+.. function:: net_if_addrs()
+
+ Return the addresses associated to each NIC (network interface card)
+ installed on the system as a dictionary whose keys are the NIC names and
+ value is a list of namedtuples for each address assigned to the NIC.
+ Each namedtuple includes 4 fields:
+
+ - **family**
+ - **address**
+ - **netmask**
+ - **broadcast**
+
+ *family* can be either
+ `AF_INET <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
+ `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
+ or :const:`psutil.AF_LINK`, which refers to a MAC address.
+ *address* is the primary address, *netmask* and *broadcast* may be ``None``.
+ Example::
+
+ >>> import psutil
+ >>> psutil.net_if_addrs()
+ {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
+ snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
+ snic(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
+ 'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
+ snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
+ snic(family=<AddressFamily.AF_PACKET: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+ >>>
+
+ See also `examples/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/examples/ifconfig.py>`__
+ for an example application.
+
+ .. note:: if you're interested in others families (e.g. AF_BLUETOOTH) you can
+ use the more powerful `netifaces <https://pypi.python.org/pypi/netifaces/>`__
+ extension.
+
+ .. note:: you can have more than one address of the same family associated
+ with each interface (that's why dict values are lists).
+
+ *New in 3.0.0*
+
Other system info
-----------------
@@ -1255,3 +1296,11 @@ Constants
`man prlimit <http://linux.die.net/man/2/prlimit>`__ for futher information.
Availability: Linux
+
+.. _const-aflink:
+.. data:: AF_LINK
+
+ Constant which identifies a MAC address associated with a network interface.
+ To be used in conjunction with :func:`psutil.net_if_addrs()`.
+
+ *New in 3.0.0*
diff --git a/examples/ifconfig.py b/examples/ifconfig.py
new file mode 100644
index 00000000..7c47024d
--- /dev/null
+++ b/examples/ifconfig.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""
+A clone of 'ifconfig' on UNIX.
+
+$ python examples/ifconfig.py
+lo:
+ IPv4 address : 127.0.0.1
+ broadcast : 127.0.0.1
+ netmask : 255.0.0.0
+ IPv6 address : ::1
+ netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+ HWADDR address : 00:00:00:00:00:00
+ broadcast : 00:00:00:00:00:00
+
+wlan0:
+ IPv4 address : 192.168.1.3
+ broadcast : 192.168.1.255
+ netmask : 255.255.255.0
+ IPv6 address : fe80::c685:8ff:fe45:641%wlan0
+ netmask : ffff:ffff:ffff:ffff::
+ HWADDR address : c4:85:08:45:06:41
+ broadcast : ff:ff:ff:ff:ff:ff
+
+docker0:
+ IPv4 address : 172.17.42.1
+ broadcast : 172.17.42.1
+ netmask : 255.255.0.0
+ IPv6 address : fe80::ac6d:3aff:fe0d:a19c%docker0
+ netmask : ffff:ffff:ffff:ffff::
+ HWADDR address : ae:6d:3a:0d:a1:9c
+ broadcast : ff:ff:ff:ff:ff:ff
+"""
+
+from __future__ import print_function
+import socket
+
+import psutil
+
+
+af_map = {
+ socket.AF_INET: 'IPv4',
+ socket.AF_INET6: 'IPv6',
+ psutil.AF_LINK: 'HWADDR',
+}
+
+
+def main():
+ for nic, addrs in psutil.net_if_addrs().items():
+ print("%s:" % (nic))
+ for addr in addrs:
+ print(" %-8s" % af_map.get(addr.family, addr.family), end="")
+ print(" address : %s" % addr.address)
+ if addr.broadcast:
+ print(" broadcast : %s" % addr.broadcast)
+ if addr.netmask:
+ print(" netmask : %s" % addr.netmask)
+ print("")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 73ecccc7..a08e6453 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -11,6 +11,7 @@ in Python.
"""
from __future__ import division
+
import collections
import errno
import functools
@@ -31,7 +32,9 @@ from ._compat import PY3 as _PY3
from ._common import (deprecated_method as _deprecated_method,
deprecated as _deprecated,
sdiskio as _nt_sys_diskio,
- snetio as _nt_sys_netio)
+ snetio as _nt_sys_netio,
+ snic as _nt_snic,
+ )
from ._common import (STATUS_RUNNING, # NOQA
STATUS_SLEEPING,
@@ -144,13 +147,14 @@ __all__ = [
"CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
"CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
"CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE",
+ "AF_LINK",
# classes
"Process", "Popen",
# functions
"pid_exists", "pids", "process_iter", "wait_procs", # proc
"virtual_memory", "swap_memory", # memory
"cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
- "net_io_counters", "net_connections", # network
+ "net_io_counters", "net_connections", "net_if_addrs", # network
"disk_io_counters", "disk_partitions", "disk_usage", # disk
"users", "boot_time", # others
]
@@ -158,6 +162,7 @@ __all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
__version__ = "3.0.0"
version_info = tuple([int(num) for num in __version__.split('.')])
+AF_LINK = _psplatform.AF_LINK
_TOTAL_PHYMEM = None
_POSIX = os.name == 'posix'
_WINDOWS = os.name == 'nt'
@@ -1814,6 +1819,40 @@ def net_connections(kind='inet'):
return _psplatform.net_connections(kind)
+def net_if_addrs():
+ """Return the addresses associated to each NIC (network interface
+ card) installed on the system as a dictionary whose keys are the
+ NIC names and value is a list of namedtuples for each address
+ assigned to the NIC. Each namedtuple includes 4 fields:
+
+ - family
+ - address
+ - netmask
+ - broadcast
+
+ 'family' can be either socket.AF_INET, socket.AF_INET6 or
+ psutil.AF_LINK, which refers to a MAC address.
+ 'address' is the primary address, 'netmask' and 'broadcast'
+ may be None.
+ Note: you can have more than one address of the same family
+ associated with each interface.
+ """
+ has_enums = sys.version_info >= (3, 4)
+ if has_enums:
+ import socket
+ rawlist = _psplatform.net_if_addrs()
+ rawlist.sort(key=lambda x: x[1]) # sort by family
+ ret = collections.defaultdict(list)
+ for name, fam, addr, mask, broadcast in rawlist:
+ if has_enums:
+ try:
+ fam = socket.AddressFamily(fam)
+ except ValueError:
+ pass
+ ret[name].append(_nt_snic(fam, addr, mask, broadcast))
+ return dict(ret)
+
+
# =====================================================================
# --- other system related functions
# =====================================================================
diff --git a/psutil/_common.py b/psutil/_common.py
index 2c17a994..cc223cf8 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -238,6 +238,8 @@ suser = namedtuple('suser', ['name', 'terminal', 'host', 'started'])
# psutil.net_connections()
sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status', 'pid'])
+# psutil.net_if_addrs()
+snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast'])
# --- namedtuples for psutil.Process methods
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index cf2ee64c..87dfb7a6 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -50,6 +50,7 @@ TCP_STATUSES = {
}
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+AF_LINK = cext_posix.AF_LINK
# extend base mem ntuple with BSD-specific memory metrics
svmem = namedtuple(
@@ -211,6 +212,7 @@ pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
+net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index bfea5625..c21d089e 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -54,6 +54,7 @@ CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
BOOT_TIME = None # set later
DEFAULT_ENCODING = sys.getdefaultencoding()
+AF_LINK = socket.AF_PACKET
# ioprio_* constants http://linux.die.net/man/2/ioprio_get
IOPRIO_CLASS_NONE = 0
@@ -555,6 +556,9 @@ def net_io_counters():
return retdict
+net_if_addrs = cext_posix.net_if_addrs
+
+
# --- disks
def disk_io_counters():
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index 1fc6779f..141c9dae 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -24,6 +24,7 @@ __extra__all__ = []
# --- constants
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+AF_LINK = cext_posix.AF_LINK
# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
TCP_STATUSES = {
@@ -171,6 +172,7 @@ pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
+net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 907e216a..69281a8c 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -25,6 +25,7 @@ from ._compat import PY3
__extra__all__ = ["CONN_IDLE", "CONN_BOUND"]
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = socket.AF_LINK
CONN_IDLE = "IDLE"
CONN_BOUND = "BOUND"
@@ -73,6 +74,7 @@ TimeoutExpired = None
disk_io_counters = cext.disk_io_counters
net_io_counters = cext.net_io_counters
disk_usage = _psposix.disk_usage
+net_if_addrs = cext_posix.net_if_addrs
def virtual_memory():
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index d020837c..2f96c1b7 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -10,6 +10,20 @@
#include <errno.h>
#include <stdlib.h>
#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+
+#ifdef __linux
+#include <netdb.h>
+#include <linux/if_packet.h>
+#endif // end linux
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <netdb.h>
+#include <netinet/in.h>
+#include <net/if_dl.h>
+#endif
#include "_psutil_posix.h"
@@ -55,6 +69,160 @@ psutil_posix_setpriority(PyObject *self, PyObject *args)
/*
+ * Translate a sockaddr struct into a Python string.
+ * Return None if address family is not AF_INET* or AF_PACKET.
+ */
+static PyObject *
+psutil_convert_ipaddr(struct sockaddr *addr, int family)
+{
+ char buf[NI_MAXHOST];
+ int err;
+ int addrlen;
+ int n;
+ size_t len;
+ const char *data;
+ char *ptr;
+
+ if (addr == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ else if (family == AF_INET || family == AF_INET6) {
+ if (family == AF_INET)
+ addrlen = sizeof(struct sockaddr_in);
+ else
+ addrlen = sizeof(struct sockaddr_in6);
+ err = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST);
+ if (err != 0) {
+ // XXX we get here on FreeBSD when processing 'lo' / AF_INET6
+ // broadcast. Not sure what to do other than returning None.
+ // ifconfig does not show anything BTW.
+ //PyErr_Format(PyExc_RuntimeError, gai_strerror(err));
+ //return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ else {
+ return Py_BuildValue("s", buf);
+ }
+ }
+#ifdef __linux
+ else if (family == AF_PACKET) {
+ struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;
+ len = lladdr->sll_halen;
+ data = (const char *)lladdr->sll_addr;
+ }
+#endif
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ else if (addr->sa_family == AF_LINK) {
+ // Note: prior to Python 3.4 socket module does not expose
+ // AF_LINK so we'll do.
+ struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;
+ len = dladdr->sdl_alen;
+ data = LLADDR(dladdr);
+ }
+#endif
+ else {
+ // unknown family
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ // AF_PACKET or AF_LINK
+ if (len > 0) {
+ ptr = buf;
+ for (n = 0; n < len; ++n) {
+ sprintf(ptr, "%02x:", data[n] & 0xff);
+ ptr += 3;
+ }
+ *--ptr = '\0';
+ return Py_BuildValue("s", buf);
+ }
+ else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+
+/*
+ * Return NICs information a-la ifconfig as a list of tuples.
+ */
+static PyObject*
+psutil_net_if_addrs(PyObject* self, PyObject* args)
+{
+ struct ifaddrs *ifaddr, *ifa;
+ int family;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_address = NULL;
+ PyObject *py_netmask = NULL;
+ PyObject *py_broadcast = NULL;
+
+
+ if (getifaddrs(&ifaddr) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+ family = ifa->ifa_addr->sa_family;
+ py_address = psutil_convert_ipaddr(ifa->ifa_addr, family);
+ // If the primary address can't be determined just skip it.
+ // I've never seen this happen on Linux but I did on FreeBSD.
+ if (py_address == Py_None)
+ continue;
+ if (py_address == NULL)
+ goto error;
+ py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family);
+ if (py_netmask == NULL)
+ goto error;
+#ifdef __linux
+ py_broadcast = psutil_convert_ipaddr(ifa->ifa_ifu.ifu_broadaddr, family);
+#else
+ py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family);
+#endif
+ if (py_broadcast == NULL)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(siOOO)",
+ ifa->ifa_name,
+ family,
+ py_address,
+ py_netmask,
+ py_broadcast
+ );
+
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_DECREF(py_address);
+ Py_DECREF(py_netmask);
+ Py_DECREF(py_broadcast);
+ }
+
+ freeifaddrs(ifaddr);
+ return py_retlist;
+
+error:
+ if (ifaddr != NULL)
+ freeifaddrs(ifaddr);
+ Py_DECREF(py_retlist);
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_address);
+ Py_XDECREF(py_netmask);
+ Py_XDECREF(py_broadcast);
+ return NULL;
+}
+
+
+/*
* define the psutil C module methods and initialize the module.
*/
static PyMethodDef
@@ -64,6 +232,8 @@ PsutilMethods[] =
"Return process priority"},
{"setpriority", psutil_posix_setpriority, METH_VARARGS,
"Set process priority"},
+ {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
+ "Retrieve NICs information"},
{NULL, NULL, 0, NULL}
};
@@ -118,6 +288,11 @@ void init_psutil_posix(void)
#else
PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ PyModule_AddIntConstant(module, "AF_LINK", AF_LINK);
+#endif
+
if (module == NULL) {
INITERROR;
}
diff --git a/psutil/_psutil_posix.h b/psutil/_psutil_posix.h
index 5a4681d1..91e524b0 100644
--- a/psutil/_psutil_posix.h
+++ b/psutil/_psutil_posix.h
@@ -6,5 +6,6 @@
#include <Python.h>
+static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args);
static PyObject* psutil_posix_getpriority(PyObject* self, PyObject* args);
static PyObject* psutil_posix_setpriority(PyObject* self, PyObject* args);
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 70d26867..0427bc1d 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -20,6 +20,7 @@
#include <winsock2.h>
#include <iphlpapi.h>
#include <wtsapi32.h>
+#include <ws2tcpip.h>
// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
@@ -2988,6 +2989,176 @@ error:
}
+
+/*
+ * Return NICs addresses.
+ */
+
+#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
+#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
+
+static PyObject *
+psutil_net_if_addrs(PyObject *self, PyObject *args)
+{
+ DWORD dwSize = 0;
+ DWORD dwRetVal = 0;
+ unsigned int i = 0;
+ ULONG family;
+ LPVOID lpMsgBuf = NULL;
+ ULONG outBufLen = 0;
+ ULONG iterations = 0;
+ PCTSTR WSAAPI intRet;
+ char *ptr;
+ char buff[100];
+ char ifname[MAX_PATH];
+ DWORD bufflen = 100;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_address = NULL;
+ PyObject *py_mac_address = NULL;
+
+
+ // allocate a 15 KB buffer to start with
+ outBufLen = 15000;
+ do {
+ pAddresses = (IP_ADAPTER_ADDRESSES *)MALLOC(outBufLen);
+ if (pAddresses == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ dwRetVal = GetAdaptersAddresses(
+ AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);
+ if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+ FREE(pAddresses);
+ pAddresses = NULL;
+ }
+ else {
+ break;
+ }
+ iterations++;
+ } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (iterations < 3));
+
+ if (dwRetVal != NO_ERROR) {
+ PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses failed");
+ goto error;
+ }
+
+ pCurrAddresses = pAddresses;
+ while (pCurrAddresses) {
+ pUnicast = pCurrAddresses->FirstUnicastAddress;
+ sprintf(ifname, "%wS", pCurrAddresses->FriendlyName);
+
+ // MAC address
+ if (pCurrAddresses->PhysicalAddressLength != 0) {
+ ptr = buff;
+ *ptr = '\0';
+ for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) {
+ if (i == (pCurrAddresses->PhysicalAddressLength - 1)) {
+ sprintf(ptr, "%.2X\n",
+ (int)pCurrAddresses->PhysicalAddress[i]);
+ }
+ else {
+ sprintf(ptr, "%.2X-",
+ (int)pCurrAddresses->PhysicalAddress[i]);
+ }
+ ptr += 3;
+ }
+ *--ptr = '\0';
+
+ py_mac_address = PyString_FromString(buff);
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ py_tuple = Py_BuildValue(
+ "(siOOO)",
+ ifname,
+ -1, // this will be converted later to AF_LINK
+ py_mac_address,
+ Py_None,
+ Py_None
+ );
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_DECREF(py_mac_address);
+ }
+
+ // find out the IP address associated with the NIC
+ if (pUnicast != NULL) {
+ for (i = 0; pUnicast != NULL; i++) {
+ family = pUnicast->Address.lpSockaddr->sa_family;
+ if (family == AF_INET) {
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)
+ pUnicast->Address.lpSockaddr;
+ intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff,
+ bufflen);
+ }
+ else if (family == AF_INET6) {
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
+ pUnicast->Address.lpSockaddr;
+ intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr),
+ buff, bufflen);
+ }
+ else {
+ // we should never get here
+ pUnicast = pUnicast->Next;
+ continue;
+ }
+
+ if (intRet == NULL) {
+ PyErr_SetFromWindowsErr(GetLastError());
+ goto error;
+ }
+ py_address = PyString_FromString(buff);
+ if (py_address == NULL)
+ goto error;
+
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ py_tuple = Py_BuildValue(
+ "(siOOO)",
+ ifname,
+ family,
+ py_address,
+ Py_None,
+ Py_None
+ );
+
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_DECREF(py_address);
+
+ pUnicast = pUnicast->Next;
+ }
+ }
+
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+
+ if (pAddresses)
+ FREE(pAddresses);
+
+ return py_retlist;
+
+error:
+ if (pAddresses)
+ FREE(pAddresses);
+ Py_DECREF(py_retlist);
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_address);
+ return NULL;
+}
+
+
+
// ------------------------ Python init ---------------------------
static PyMethodDef
@@ -3092,7 +3263,8 @@ PsutilMethods[] =
"Return disk partitions."},
{"net_connections", psutil_net_connections, METH_VARARGS,
"Return system-wide connections"},
-
+ {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
+ "Return NICs addresses."},
// --- windows API bindings
{"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
diff --git a/psutil/_psutil_windows.h b/psutil/_psutil_windows.h
index 546704e3..862da9f2 100644
--- a/psutil/_psutil_windows.h
+++ b/psutil/_psutil_windows.h
@@ -60,6 +60,7 @@ static PyObject* psutil_pids(PyObject* self, PyObject* args);
static PyObject* psutil_ppid_map(PyObject* self, PyObject* args);
static PyObject* psutil_users(PyObject* self, PyObject* args);
static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
+static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args);
// --- windows API bindings
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index f0bf17b9..d3212b87 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -33,6 +33,7 @@ CONN_DELETE_TCB = "DELETE_TCB"
WAIT_TIMEOUT = 0x00000102 # 258 in decimal
ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES,
cext.ERROR_ACCESS_DENIED])
+AF_LINK = -1
TCP_STATUSES = {
cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
@@ -199,6 +200,7 @@ pid_exists = cext.pid_exists
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
ppid_map = cext.ppid_map # not meant to be public
+net_if_addrs = cext.net_if_addrs
def wrap_exceptions(fun):
diff --git a/setup.py b/setup.py
index 134af957..7a20c621 100644
--- a/setup.py
+++ b/setup.py
@@ -78,7 +78,7 @@ if sys.platform.startswith("win32"):
],
libraries=[
"psapi", "kernel32", "advapi32", "shell32", "netapi32", "iphlpapi",
- "wtsapi32",
+ "wtsapi32", "ws2_32",
],
# extra_compile_args=["/Z7"],
# extra_link_args=["/DEBUG"]
diff --git a/test/_linux.py b/test/_linux.py
index 77f6da27..8f02b45b 100644
--- a/test/_linux.py
+++ b/test/_linux.py
@@ -7,16 +7,82 @@
"""Linux specific tests. These are implicitly run by test_psutil.py."""
from __future__ import division
+import array
+import contextlib
+import fcntl
import os
+import pprint
import re
+import socket
+import struct
import sys
import time
from test_psutil import POSIX, TOLERANCE, TRAVIS
from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess,
- retry_before_failing, get_kernel_version, unittest)
+ retry_before_failing, get_kernel_version, unittest,
+ which)
import psutil
+from psutil._compat import PY3
+
+
+SIOCGIFADDR = 0x8915
+SIOCGIFCONF = 0x8912
+SIOCGIFHWADDR = 0x8927
+
+
+def get_ipv4_address(ifname):
+ ifname = ifname[:15]
+ if PY3:
+ ifname = bytes(ifname, 'ascii')
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ with contextlib.closing(s):
+ return socket.inet_ntoa(
+ fcntl.ioctl(s.fileno(),
+ SIOCGIFADDR,
+ struct.pack('256s', ifname))[20:24])
+
+
+def get_mac_address(ifname):
+ ifname = ifname[:15]
+ if PY3:
+ ifname = bytes(ifname, 'ascii')
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ with contextlib.closing(s):
+ info = fcntl.ioctl(
+ s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname))
+ if PY3:
+ def ord(x):
+ return x
+ else:
+ import __builtin__
+ ord = __builtin__.ord
+ return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]
+
+
+def get_nic_names():
+ """Find the names of all available network interfaces."""
+ # Thanks to:
+ # https://code.fluendo.com/flumotion/svn/flumotion/trunk/flumotion/common/
+ # netutils.py
+ ptr_size = len(struct.pack('P', 0))
+ size = 24 + 2 * (ptr_size)
+ max_possible = 128
+ inbytes = max_possible * size
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ with contextlib.closing(s):
+ names = array.array('B', b'\0' * inbytes)
+ outbytes = struct.unpack('iP', fcntl.ioctl(
+ s.fileno(),
+ SIOCGIFCONF,
+ struct.pack('iP', inbytes, names.buffer_info()[0])))[0]
+ namestr = names.tostring()
+ ret = [namestr[i:i + size].split(b'\0', 1)[0]
+ for i in range(0, outbytes, size)]
+ if PY3:
+ ret = [x.decode() for x in ret]
+ return ret
class LinuxSpecificTestCase(unittest.TestCase):
@@ -139,6 +205,34 @@ class LinuxSpecificTestCase(unittest.TestCase):
else:
self.assertNotIn('guest_nice', fields)
+ def test_net_if_addrs_ips(self):
+ for name, addrs in psutil.net_if_addrs().items():
+ for addr in addrs:
+ if addr.family == psutil.AF_LINK:
+ self.assertEqual(addr.address, get_mac_address(name))
+ elif addr.family == socket.AF_INET:
+ self.assertEqual(addr.address, get_ipv4_address(name))
+ # TODO: test for AF_INET6 family
+
+ @unittest.skipUnless(which('ip'), "'ip' utility not available")
+ @unittest.skipIf(TRAVIS, "skipped on Travis")
+ def test_net_if_names(self):
+ out = sh("ip addr").strip()
+ nics = psutil.net_if_addrs()
+ found = 0
+ for line in out.split('\n'):
+ line = line.strip()
+ if re.search("^\d+:", line):
+ found += 1
+ name = line.split(':')[1].strip()
+ self.assertIn(name, nics.keys())
+ self.assertEqual(len(nics), found, msg="%s\n---\n%s" % (
+ pprint.pformat(nics), out))
+
+ def test_net_if_names_2(self):
+ self.assertEqual(sorted(get_nic_names()),
+ sorted(psutil.net_if_addrs()))
+
# --- tests for specific kernel versions
@unittest.skipUnless(
diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py
index 827f9d47..1441daa1 100644
--- a/test/test_memory_leaks.py
+++ b/test/test_memory_leaks.py
@@ -409,6 +409,9 @@ class TestModuleFunctionsLeaks(Base):
def test_net_connections(self):
self.execute('net_connections')
+ def test_net_if_addrs(self):
+ self.execute('net_if_addrs')
+
def test_main():
test_suite = unittest.TestSuite()
diff --git a/test/test_psutil.py b/test/test_psutil.py
index 6c2176da..efbcead1 100644
--- a/test/test_psutil.py
+++ b/test/test_psutil.py
@@ -304,6 +304,8 @@ def check_ip_address(addr, family):
if not PY3:
addr = unicode(addr)
ipaddress.IPv6Address(addr)
+ elif family == psutil.AF_LINK:
+ assert re.match('([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr
else:
raise ValueError("unknown family %r", family)
@@ -1039,6 +1041,54 @@ class TestSystemAPIs(unittest.TestCase):
self.assertTrue(key)
check_ntuple(ret[key])
+ def test_net_if_addrs(self):
+ nics = psutil.net_if_addrs()
+ assert nics, nics
+
+ # Not reliable on all platforms (net_if_addrs() reports more
+ # interfaces).
+ # self.assertEqual(sorted(nics.keys()),
+ # sorted(psutil.net_io_counters(pernic=True).keys()))
+
+ families = set([socket.AF_INET, AF_INET6, psutil.AF_LINK])
+ for nic, addrs in nics.items():
+ self.assertEqual(len(set(addrs)), len(addrs))
+ for addr in addrs:
+ self.assertIsInstance(addr.family, int)
+ self.assertIsInstance(addr.address, str)
+ self.assertIsInstance(addr.netmask, (str, type(None)))
+ self.assertIsInstance(addr.broadcast, (str, type(None)))
+ self.assertIn(addr.family, families)
+ if sys.version_info >= (3, 4):
+ self.assertIsInstance(addr.family, socket.AddressFamily)
+ if addr.family == socket.AF_INET:
+ s = socket.socket(addr.family)
+ with contextlib.closing(s):
+ s.bind((addr.address, 0))
+ elif addr.family == socket.AF_INET6:
+ info = socket.getaddrinfo(
+ addr.address, 0, socket.AF_INET6, socket.SOCK_STREAM,
+ 0, socket.AI_PASSIVE)[0]
+ af, socktype, proto, canonname, sa = info
+ s = socket.socket(af, socktype, proto)
+ with contextlib.closing(s):
+ s.bind(sa)
+ for ip in (addr.address, addr.netmask, addr.broadcast):
+ if ip is not None:
+ # TODO: skip AF_INET6 for now because I get:
+ # AddressValueError: Only hex digits permitted in
+ # u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'
+ if addr.family != AF_INET6:
+ check_ip_address(ip, addr.family)
+
+ if BSD or OSX or SUNOS:
+ if hasattr(socket, "AF_LINK"):
+ self.assertEqual(psutil.AF_LINK, socket.AF_LINK)
+ elif LINUX:
+ self.assertEqual(psutil.AF_LINK, socket.AF_PACKET)
+ elif WINDOWS:
+ self.assertEqual(psutil.AF_LINK, -1)
+
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
'/proc/diskstats not available on this linux version')
def test_disk_io_counters(self):
@@ -2666,6 +2716,9 @@ class TestExampleScripts(unittest.TestCase):
def test_netstat(self):
self.assert_stdout('netstat.py')
+ def test_ifconfig(self):
+ self.assert_stdout('ifconfig.py')
+
def test_pmap(self):
self.assert_stdout('pmap.py', args=str(os.getpid()))