Skip to content

Commit

Permalink
Implement Process.environ() on *BSD family
Browse files Browse the repository at this point in the history
Tested-on: FreeBSD-13-CURRENT (r363681), FreeBSD-12.1,
           FreeBSD-11.4, FreeBSD-11.3, FreeBSD-10.4,
           FreeBSD-9.3, FreeBSD-8.4, FreeBSD-7.1, FreeBSD-6.4,
           OpenBSD-6.7, OpenBSD-5.7, NetBSD-9.0, NetBSD-8.2
  • Loading branch information
Armin Gruner committed Aug 4, 2020
1 parent 3c9aa68 commit 2378208
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 1 deletion.
4 changes: 4 additions & 0 deletions psutil/_psbsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,10 @@ def cmdline(self):
else:
return cext.proc_cmdline(self.pid)

@wrap_exceptions
def environ(self):
return cext.proc_environ(self.pid)

@wrap_exceptions
def terminal(self):
tty_nr = self.oneshot()[kinfo_proc_map['ttynr']]
Expand Down
110 changes: 110 additions & 0 deletions psutil/_psutil_bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <net/route.h>
#include <netinet/in.h> // process open files/connections
#include <sys/un.h>
#include <kvm.h>

#include "_psutil_common.h"
#include "_psutil_posix.h"
Expand All @@ -74,6 +75,10 @@
#else
#include <utmpx.h>
#endif
#include <fcntl.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#elif PSUTIL_OPENBSD
#include "arch/openbsd/specific.h"

Expand Down Expand Up @@ -391,6 +396,109 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) {
}


/*
* Return process environment as a Python dictionary
*/
PyObject *
psutil_proc_environ(PyObject *self, PyObject *args) {
unsigned int i, cnt = 0;
long pid;
char *s, **envs, errbuf[_POSIX2_LINE_MAX];
PyObject *py_err=NULL, *py_value=NULL, *py_retdict=NULL;
kvm_t *kd;
#ifdef PSUTIL_NETBSD
struct kinfo_proc2 *p;
#else
struct kinfo_proc *p;
#endif

if (!PyArg_ParseTuple(args, "l", &pid))
return NULL;

#if defined(PSUTIL_FREEBSD)
kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
#else
kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
#endif
if (!kd) {
convert_kvm_err("kvm_openfiles", errbuf);
return NULL;
}

py_retdict = PyDict_New();
if (!py_retdict)
goto error;

#if defined(PSUTIL_FREEBSD)
p = kvm_getprocs(kd, KERN_PROC_PID, pid, (int *)&cnt);
#elif defined(PSUTIL_OPENBSD)
p = kvm_getprocs(kd, KERN_PROC_PID, pid, sizeof(*p), &cnt);
#elif defined(PSUTIL_NETBSD)
p = kvm_getproc2(kd, KERN_PROC_PID, pid, sizeof(*p), &cnt);
#endif
if (!p) {
py_err = NoSuchProcess("kvm_getprocs");
goto error;
}
if (!cnt) {
py_err = NoSuchProcess("kvm_getprocs: cnt==0");
goto error;
}

/* On *BSD kernels there are a few kernel-only system processes without an environment.
* To make unittest suite happy, return an empty environment
* (See e.g. "procstat -e 0 | 1 | 2 ..." on FreeBSD.)
*/
if (p->ki_ppid == 0) {
/* Process is a direct [kernel-only] descendant of process 0 */
kvm_close(kd);
return py_retdict;
}


#if defined(PSUTIL_NETBSD)
envs = kvm_getenvv2(kd, p, 0);
#else
envs = kvm_getenvv(kd, p, 0);
#endif
if (!envs) {
/* Map the most obvious stuff to general exceptions exported by "psutil" */
if (errno == EPERM)
py_err = AccessDenied("kvm_getenvv");
else if (errno == ESRCH)
py_err = NoSuchProcess("kvm_getenvv");
else
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}

/* Indicate we need to return libpython error if we return failure */
errno = 0;

for (i = 0; envs[i] != NULL; i++) {
s = strchr(envs[i], '=');
if (!s)
continue;
*s++ = 0;
py_value = PyUnicode_DecodeFSDefault(s);
if (!py_value)
goto error;
if (PyDict_SetItemString(py_retdict, envs[i], py_value)) {
goto error;
}
Py_DECREF(py_value);
}

kvm_close(kd);
return py_retdict;

error:
Py_XDECREF(py_value);
Py_XDECREF(py_retdict);
kvm_close(kd);
return py_err;
}

/*
* Return the number of logical CPUs in the system.
* XXX this could be shared with macOS
Expand Down Expand Up @@ -958,6 +1066,8 @@ static PyMethodDef mod_methods[] = {
{"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
"Return an XML string to determine the number physical CPUs."},
#endif
{"proc_environ", psutil_proc_environ, METH_VARARGS,
"Return process environment"},

// --- system-related functions

Expand Down
3 changes: 2 additions & 1 deletion psutil/tests/test_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ class TestAvailProcessAPIs(PsutilTestCase):

def test_environ(self):
self.assertEqual(hasattr(psutil.Process, "environ"),
LINUX or MACOS or WINDOWS or AIX or SUNOS)
LINUX or MACOS or WINDOWS or AIX or SUNOS or
FREEBSD or OPENBSD or NETBSD)

def test_uids(self):
self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
Expand Down

0 comments on commit 2378208

Please sign in to comment.