diff options
Diffstat (limited to 'psutil/arch/netbsd/proc.c')
-rw-r--r-- | psutil/arch/netbsd/proc.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/psutil/arch/netbsd/proc.c b/psutil/arch/netbsd/proc.c new file mode 100644 index 00000000..b87473a6 --- /dev/null +++ b/psutil/arch/netbsd/proc.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Platform-specific module methods for NetBSD. + */ + +#include <Python.h> +#include <sys/sysctl.h> +#include <kvm.h> + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" +#include "proc.h" + + +#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0) +#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + + +// ============================================================================ +// Utility functions +// ============================================================================ + + +int +psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) { + // Fills a kinfo_proc struct based on process pid. + int ret; + int mib[6]; + size_t size = sizeof(kinfo_proc); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC2; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + mib[4] = size; + mib[5] = 1; + + ret = sysctl((int*)mib, 6, proc, &size, NULL, 0); + if (ret == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + // sysctl stores 0 in the size if we can't find the process information. + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + return -1; + } + return 0; +} + + +struct kinfo_file * +kinfo_getfile(pid_t pid, int* cnt) { + // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an + // int as arg and returns an array with cnt struct kinfo_file. + int mib[6]; + size_t len; + struct kinfo_file* kf; + mib[0] = CTL_KERN; + mib[1] = KERN_FILE2; + mib[2] = KERN_FILE_BYPID; + mib[3] = (int) pid; + mib[4] = sizeof(struct kinfo_file); + mib[5] = 0; + + // get the size of what would be returned + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if ((kf = malloc(len)) == NULL) { + PyErr_NoMemory(); + return NULL; + } + mib[5] = (int)(len / sizeof(struct kinfo_file)); + if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + *cnt = (int)(len / sizeof(struct kinfo_file)); + return kf; +} + +PyObject * +psutil_proc_cwd(PyObject *self, PyObject *args) { + long pid; + + char path[MAXPATHLEN]; + size_t pathlen = sizeof path; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + +#ifdef KERN_PROC_CWD + int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD}; + if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) { + if (errno == ENOENT) + NoSuchProcess("sysctl -> ENOENT"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } +#else + char *buf; + if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) { + PyErr_NoMemory(); + return NULL; + } + + ssize_t len = readlink(buf, path, sizeof(path) - 1); + free(buf); + if (len == -1) { + if (errno == ENOENT) + NoSuchProcess("readlink -> ENOENT"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + path[len] = '\0'; +#endif + + return PyUnicode_DecodeFSDefault(path); +} + + +// XXX: This is no longer used as per +// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820 +// Current implementation uses /proc instead. +// Left here just in case. +/* +PyObject * +psutil_proc_exe(PyObject *self, PyObject *args) { +#if __NetBSD_Version__ >= 799000000 + pid_t pid; + char pathname[MAXPATHLEN]; + int error; + int mib[4]; + int ret; + size_t size; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (pid == 0) { + // else returns ENOENT + return Py_BuildValue("s", ""); + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pid; + mib[3] = KERN_PROC_PATHNAME; + + size = sizeof(pathname); + error = sysctl(mib, 4, NULL, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + error = sysctl(mib, 4, pathname, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (size == 0 || strlen(pathname) == 0) { + ret = psutil_pid_exists(pid); + if (ret == -1) + return NULL; + else if (ret == 0) + return NoSuchProcess("psutil_pid_exists -> 0"); + else + strcpy(pathname, ""); + } + + return PyUnicode_DecodeFSDefault(pathname); +#else + return Py_BuildValue("s", ""); +#endif +} +*/ + +PyObject * +psutil_proc_num_threads(PyObject *self, PyObject *args) { + // Return number of threads used by process as a Python integer. + long pid; + kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (psutil_kinfo_proc(pid, &kp) == -1) + return NULL; + return Py_BuildValue("l", (long)kp.p_nlwps); +} + +PyObject * +psutil_proc_threads(PyObject *self, PyObject *args) { + pid_t pid; + int mib[5]; + int i, nlwps; + ssize_t st; + size_t size; + struct kinfo_lwp *kl = NULL; + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + + if (py_retlist == NULL) + return NULL; + if (! PyArg_ParseTuple(args, "l", &pid)) + goto error; + + mib[0] = CTL_KERN; + mib[1] = KERN_LWP; + mib[2] = pid; + mib[3] = sizeof(struct kinfo_lwp); + mib[4] = 0; + + st = sysctl(mib, 5, NULL, &size, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + mib[4] = size / sizeof(size_t); + kl = malloc(size); + if (kl == NULL) { + PyErr_NoMemory(); + goto error; + } + + st = sysctl(mib, 5, kl, &size, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + nlwps = (int)(size / sizeof(struct kinfo_lwp)); + for (i = 0; i < nlwps; i++) { + py_tuple = Py_BuildValue("idd", + (&kl[i])->l_lid, + PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime), + PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime)); + if (py_tuple == NULL) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + free(kl); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (kl != NULL) + free(kl); + return NULL; +} + + +int +psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { + // Returns a list of all BSD processes on the system. This routine + // allocates the list and puts it in *procList and a count of the + // number of entries in *procCount. You are responsible for freeing + // this list (use "free" from System framework). + // On success, the function returns 0. + // On error, the function returns a BSD errno value. + kinfo_proc *result; + // Declaring name as const requires us to cast it when passing it to + // sysctl because the prototype doesn't include the const modifier. + char errbuf[_POSIX2_LINE_MAX]; + int cnt; + kvm_t *kd; + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + + if (kd == NULL) { + PyErr_Format( + PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf); + return 1; + } + + result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt); + if (result == NULL) { + PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed"); + kvm_close(kd); + return 1; + } + + *procCount = (size_t)cnt; + + size_t mlen = cnt * sizeof(kinfo_proc); + + if ((*procList = malloc(mlen)) == NULL) { + PyErr_NoMemory(); + kvm_close(kd); + return 1; + } + + memcpy(*procList, result, mlen); + assert(*procList != NULL); + kvm_close(kd); + + return 0; +} + + +char * +psutil_get_cmd_args(pid_t pid, size_t *argsize) { + int mib[4]; + int st; + size_t len; + char *procargs; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pid; + mib[3] = KERN_PROC_ARGV; + len = 0; + + st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + procargs = (char *)malloc(len); + if (procargs == NULL) { + PyErr_NoMemory(); + return NULL; + } + st = sysctl(mib, __arraycount(mib), procargs, &len, NULL, 0); + if (st == -1) { + free(procargs); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + *argsize = len; + return procargs; +} + + +// Return the command line as a python list object. +// XXX - most of the times sysctl() returns a truncated string. +// Also /proc/pid/cmdline behaves the same so it looks like this +// is a kernel bug. +PyObject * +psutil_get_cmdline(pid_t pid) { + char *argstr = NULL; + size_t pos = 0; + size_t argsize = 0; + PyObject *py_arg = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return NULL; + if (pid == 0) + return py_retlist; + + argstr = psutil_get_cmd_args(pid, &argsize); + if (argstr == NULL) + goto error; + + // args are returned as a flattened string with \0 separators between + // arguments add each string to the list then step forward to the next + // separator + if (argsize > 0) { + while (pos < argsize) { + py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]); + if (!py_arg) + goto error; + if (PyList_Append(py_retlist, py_arg)) + goto error; + Py_DECREF(py_arg); + pos = pos + strlen(&argstr[pos]) + 1; + } + } + + free(argstr); + return py_retlist; + +error: + Py_XDECREF(py_arg); + Py_DECREF(py_retlist); + if (argstr != NULL) + free(argstr); + return NULL; +} + + +PyObject * +psutil_proc_num_fds(PyObject *self, PyObject *args) { + long pid; + int cnt; + + struct kinfo_file *freep; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + + errno = 0; + freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { + psutil_raise_for_pid(pid, "kinfo_getfile()"); + return NULL; + } + free(freep); + + return Py_BuildValue("i", cnt); +} |