blob: 53f7955e32c0e758f4d04e3807227c1b57eebaa8 [file] [log] [blame]
/*
* 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.
*
* Linux-specific functions.
*/
#include <Python.h>
#include <errno.h>
#include <stdlib.h>
#include <mntent.h>
#include <utmp.h>
#include <sched.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <linux/unistd.h>
#include "_psutil_linux.h"
#define HAS_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
#if HAS_IOPRIO
enum {
IOPRIO_WHO_PROCESS = 1,
};
static inline int
ioprio_get(int which, int who)
{
return syscall(__NR_ioprio_get, which, who);
}
static inline int
ioprio_set(int which, int who, int ioprio)
{
return syscall(__NR_ioprio_set, which, who, ioprio);
}
#define IOPRIO_CLASS_SHIFT 13
#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
/*
* Return a (ioclass, iodata) Python tuple representing process I/O priority.
*/
static PyObject*
linux_ioprio_get(PyObject* self, PyObject* args)
{
long pid;
int ioprio, ioclass, iodata;
if (! PyArg_ParseTuple(args, "l", &pid)) {
return NULL;
}
ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
if (ioprio == -1) {
return PyErr_SetFromErrno(PyExc_OSError);
}
ioclass = IOPRIO_PRIO_CLASS(ioprio);
iodata = IOPRIO_PRIO_DATA(ioprio);
return Py_BuildValue("ii", ioclass, iodata);
}
/*
* A wrapper around ioprio_set(); sets process I/O priority.
* ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE
* or 0. iodata goes from 0 to 7 depending on ioclass specified.
*/
static PyObject*
linux_ioprio_set(PyObject* self, PyObject* args)
{
long pid;
int ioprio, ioclass, iodata;
int retval;
if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) {
return NULL;
}
ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
if (retval == -1) {
return PyErr_SetFromErrno(PyExc_OSError);
}
Py_INCREF(Py_None);
return Py_None;
}
#endif
/*
* Return disk mounted partitions as a list of tuples including device,
* mount point and filesystem type
*/
static PyObject*
get_disk_partitions(PyObject* self, PyObject* args)
{
FILE *file = NULL;
struct mntent *entry;
PyObject* py_retlist = PyList_New(0);
PyObject* py_tuple = NULL;
if (py_retlist == NULL)
return NULL;
// MOUNTED constant comes from mntent.h and it's == '/etc/mtab'
Py_BEGIN_ALLOW_THREADS
file = setmntent(MOUNTED, "r");
Py_END_ALLOW_THREADS
if ((file == 0) || (file == NULL)) {
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
while ((entry = getmntent(file))) {
if (entry == NULL) {
PyErr_Format(PyExc_RuntimeError, "getmntent() failed");
goto error;
}
py_tuple = Py_BuildValue("(ssss)", entry->mnt_fsname, // device
entry->mnt_dir, // mount point
entry->mnt_type, // fs type
entry->mnt_opts); // options
if (! py_tuple)
goto error;
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_DECREF(py_tuple);
}
endmntent(file);
return py_retlist;
error:
if (file != NULL)
endmntent(file);
Py_XDECREF(py_tuple);
Py_DECREF(py_retlist);
return NULL;
}
/*
* A wrapper around sysinfo(), return system memory usage statistics.
*/
static PyObject*
get_sysinfo(PyObject* self, PyObject* args)
{
struct sysinfo info;
if (sysinfo(&info) != 0) {
return PyErr_SetFromErrno(PyExc_OSError);
}
// note: BOOT_TIME might also be determined from here
return Py_BuildValue("(KKKKKK)",
(unsigned long long)info.totalram * info.mem_unit, // total
(unsigned long long)info.freeram * info.mem_unit, // free
(unsigned long long)info.bufferram * info.mem_unit, // buffer
(unsigned long long)info.sharedram * info.mem_unit, // shared
(unsigned long long)info.totalswap * info.mem_unit, // swap tot
(unsigned long long)info.freeswap * info.mem_unit); // swap free
}
/*
* Return process CPU affinity as a Python long (the bitmask)
*/
static PyObject*
get_process_cpu_affinity(PyObject* self, PyObject* args)
{
unsigned long mask;
unsigned int len = sizeof(mask);
long pid;
if (!PyArg_ParseTuple(args, "i", &pid)) {
return NULL;
}
if (sched_getaffinity(pid, len, (cpu_set_t *)&mask) < 0) {
return PyErr_SetFromErrno(PyExc_OSError);
}
return Py_BuildValue("l", mask);
}
/*
* Set process CPU affinity; expects a bitmask
*/
static PyObject*
set_process_cpu_affinity(PyObject* self, PyObject* args)
{
unsigned long mask;
unsigned int len = sizeof(mask);
long pid;
if (!PyArg_ParseTuple(args, "ll", &pid, &mask)) {
return NULL;
}
if (sched_setaffinity(pid, len, (cpu_set_t *)&mask)) {
return PyErr_SetFromErrno(PyExc_OSError);
}
Py_INCREF(Py_None);
return Py_None;
}
/*
* Return currently connected users as a list of tuples.
*/
static PyObject*
get_system_users(PyObject* self, PyObject* args)
{
PyObject *ret_list = PyList_New(0);
PyObject *tuple = NULL;
PyObject *user_proc = NULL;
struct utmp *ut;
if (ret_list == NULL)
return NULL;
setutent();
while (NULL != (ut = getutent())) {
tuple = NULL;
user_proc = NULL;
if (ut->ut_type == USER_PROCESS)
user_proc = Py_True;
else
user_proc = Py_False;
tuple = Py_BuildValue("(sssfO)",
ut->ut_user, // username
ut->ut_line, // tty
ut->ut_host, // hostname
(float)ut->ut_tv.tv_sec, // tstamp
user_proc // (bool) user process
);
if (! tuple)
goto error;
if (PyList_Append(ret_list, tuple))
goto error;
Py_DECREF(tuple);
}
endutent();
return ret_list;
error:
Py_XDECREF(tuple);
Py_XDECREF(user_proc);
Py_DECREF(ret_list);
endutent();
return NULL;
}
/*
* Define the psutil C module methods and initialize the module.
*/
static PyMethodDef
PsutilMethods[] =
{
#if HAS_IOPRIO
{"ioprio_get", linux_ioprio_get, METH_VARARGS,
"Get process I/O priority"},
{"ioprio_set", linux_ioprio_set, METH_VARARGS,
"Set process I/O priority"},
#endif
{"get_disk_partitions", get_disk_partitions, METH_VARARGS,
"Return disk mounted partitions as a list of tuples including "
"device, mount point and filesystem type"},
{"get_sysinfo", get_sysinfo, METH_VARARGS,
"A wrapper around sysinfo(), return system memory usage statistics"},
{"get_process_cpu_affinity", get_process_cpu_affinity, METH_VARARGS,
"Return process CPU affinity as a Python long (the bitmask)."},
{"set_process_cpu_affinity", set_process_cpu_affinity, METH_VARARGS,
"Set process CPU affinity; expects a bitmask."},
{"get_system_users", get_system_users, METH_VARARGS,
"Return currently connected users as a list of tuples"},
{NULL, NULL, 0, NULL}
};
struct module_state {
PyObject *error;
};
#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
#endif
#if PY_MAJOR_VERSION >= 3
static int
psutil_linux_traverse(PyObject *m, visitproc visit, void *arg) {
Py_VISIT(GETSTATE(m)->error);
return 0;
}
static int
psutil_linux_clear(PyObject *m) {
Py_CLEAR(GETSTATE(m)->error);
return 0;
}
static struct PyModuleDef
moduledef = {
PyModuleDef_HEAD_INIT,
"psutil_linux",
NULL,
sizeof(struct module_state),
PsutilMethods,
NULL,
psutil_linux_traverse,
psutil_linux_clear,
NULL
};
#define INITERROR return NULL
PyObject *
PyInit__psutil_linux(void)
#else
#define INITERROR return
void init_psutil_linux(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create(&moduledef);
#else
PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods);
#endif
if (module == NULL) {
INITERROR;
}
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}