Skip to content

Commit

Permalink
Merge branch 'nicstats-250' of github.com:giampaolo/psutil into nicst…
Browse files Browse the repository at this point in the history
…ats-250
  • Loading branch information
giampaolo committed Feb 12, 2015
2 parents dd280bd + f8bad8c commit 97f15ca
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 3 deletions.
13 changes: 12 additions & 1 deletion psutil/_pssunos.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
__extra__all__ = ["CONN_IDLE", "CONN_BOUND"]

PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
AF_LINK = socket.AF_LINK
AF_LINK = cext_posix.AF_LINK

CONN_IDLE = "IDLE"
CONN_BOUND = "BOUND"
Expand Down Expand Up @@ -231,6 +231,17 @@ def net_connections(kind, _pid=-1):
return list(ret)


def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
ret = cext.net_if_stats()
for name, items in ret.items():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret


def wrap_exceptions(fun):
"""Call callable into a try/except clause and translate ENOENT,
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
Expand Down
7 changes: 6 additions & 1 deletion psutil/_psutil_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include <net/if_dl.h>
#endif

#if defined(__sun)
#include <netdb.h>
#endif

#include "_psutil_posix.h"


Expand Down Expand Up @@ -148,6 +152,7 @@ psutil_convert_ipaddr(struct sockaddr *addr, int family)

/*
* Return NICs information a-la ifconfig as a list of tuples.
* TODO: on Solaris we won't get any MAC address.
*/
static PyObject*
psutil_net_if_addrs(PyObject* self, PyObject* args)
Expand Down Expand Up @@ -289,7 +294,7 @@ void init_psutil_posix(void)
PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
#endif

#if defined(__FreeBSD__) || defined(__APPLE__)
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__sun)
PyModule_AddIntConstant(module, "AF_LINK", AF_LINK);
#endif

Expand Down
112 changes: 112 additions & 0 deletions psutil/_psutil_sunos.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <sys/mntent.h> // for MNTTAB
#include <sys/mnttab.h>
#include <sys/procfs.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <utmpx.h>
#include <kstat.h>
Expand All @@ -38,6 +40,7 @@
#include <stropts.h>
#include <inet/tcp.h>
#include <arpa/inet.h>
#include <net/if.h>

#include "_psutil_sunos.h"

Expand Down Expand Up @@ -1151,6 +1154,113 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args)
}


/*
* Return stats about a particular network
* interface. References:
* https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
* http://www.i-scream.org/libstatgrab/
*/
static PyObject*
psutil_net_if_stats(PyObject* self, PyObject* args)
{
kstat_ctl_t *kc = NULL;
kstat_t *ksp;
kstat_named_t *knp;
int ret;
int sock = 0;
int speed;
int duplex;
int mtu;

PyObject *py_retdict = PyDict_New();
PyObject *py_ifc_info = NULL;
PyObject *py_is_up = NULL;
PyObject *py_ret = NULL;

if (py_retdict == NULL)
return NULL;
kc = kstat_open();
if (kc == NULL)
return PyErr_SetFromErrno(PyExc_OSError);

sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
return PyErr_SetFromErrno(PyExc_OSError);

for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
if (strcmp(ksp->ks_class, "net") == 0) {
struct ifreq ifr;

kstat_read(kc, ksp, NULL);
if (ksp->ks_type != KSTAT_TYPE_NAMED)
continue;
if (strcmp(ksp->ks_class, "net") != 0)
continue;

strncpy(ifr.ifr_name, ksp->ks_name, sizeof(ifr.ifr_name));
ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
if (ret == -1)
continue; // not a network interface

// is up?
if ((ifr.ifr_flags & IFF_UP) != 0) {
if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) {
if (knp->value.ui32 != 0u)
py_is_up = Py_True;
else
py_is_up = Py_False;
}
else {
py_is_up = Py_True;
}
}
else {
py_is_up = Py_False;
}
Py_INCREF(py_is_up);

// duplex
duplex = 0; // unknown
if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
if (knp->value.ui32 == 1)
duplex = 1; // half
else if (knp->value.ui32 == 2)
duplex = 2; // full
}

// speed
if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL)
speed = knp->value.ui64 / 10000000;
else
speed = 0;

// mtu
ret = ioctl(sock, SIOCGIFMTU, &ifr);
if (ret == -1)
goto error;
mtu = ifr.ifr_mtu;

py_ifc_info = Py_BuildValue("(Oiii)", py_is_up, duplex, speed, mtu);
if (!py_ifc_info)
goto error;
if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))
goto error;
Py_DECREF(py_ifc_info);
}
}

close(sock);
return py_retdict;

error:
Py_XDECREF(py_is_up);
if (sock != 0)
close(sock);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}


/*
* define the psutil C module methods and initialize the module.
*/
Expand Down Expand Up @@ -1192,6 +1302,8 @@ PsutilMethods[] =
"Return the number of physical CPUs on the system."},
{"net_connections", psutil_net_connections, METH_VARARGS,
"Return TCP and UDP syste-wide open connections."},
{"net_if_stats", psutil_net_if_stats, METH_VARARGS,
"Return NIC stats (isup, duplex, speed, mtu)"},

{NULL, NULL, 0, NULL}
};
Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@ def get_description():

# POSIX
if os.name == 'posix':
libraries = []
if sys.platform.startswith("sunos"):
libraries.append('socket')

posix_extension = Extension(
'psutil._psutil_posix',
sources=['psutil/_psutil_posix.c'],
libraries=libraries,
)
# Windows
if sys.platform.startswith("win32"):
Expand Down Expand Up @@ -126,7 +131,7 @@ def get_winver():
'psutil._psutil_sunos',
sources=['psutil/_psutil_sunos.c'],
define_macros=[VERSION_MACRO],
libraries=['kstat', 'nsl'],),
libraries=['kstat', 'nsl', 'socket']),
posix_extension,
]
else:
Expand Down
6 changes: 6 additions & 0 deletions test/_sunos.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ def test_swap_memory(self):
self.assertEqual(psutil_swap.used, used)
self.assertEqual(psutil_swap.free, free)

def test_nic_names(self):
# Internally the C implementation uses almost the same
# routine so we want to make sure NIC names are the same.
self.assertEqual(sorted(psutil.net_io_counters(pernic=True).keys()),
sorted(psutil.net_if_stats().keys()))


def test_main():
test_suite = unittest.TestSuite()
Expand Down

0 comments on commit 97f15ca

Please sign in to comment.