summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Gruner <ag-github@muc.de>2020-08-13 15:39:29 +0200
committerGitHub <noreply@github.com>2020-08-13 15:39:29 +0200
commitb09f5a28c98f1d4ee160e133ee5fc5446efba68f (patch)
tree3b1eef9bdcc588f5f98acd718d2afae958ec0af8
parenta0967043b5819b2edc61d9a12306289d5e7f98c2 (diff)
downloadpsutil-b09f5a28c98f1d4ee160e133ee5fc5446efba68f.tar.gz
Implement Process.environ() on *BSD family (#1800) (patch by Armin Gruner)
-rw-r--r--psutil/_psbsd.py13
-rw-r--r--psutil/_psutil_bsd.c150
-rw-r--r--psutil/_psutil_common.c20
-rw-r--r--psutil/_psutil_common.h6
-rw-r--r--psutil/arch/freebsd/specific.c4
-rw-r--r--psutil/arch/freebsd/sys_socks.c5
-rw-r--r--psutil/arch/openbsd/specific.c14
-rwxr-xr-xpsutil/tests/test_contracts.py3
-rwxr-xr-xpsutil/tests/test_process.py2
9 files changed, 193 insertions, 24 deletions
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index d53eb042..0568e3a8 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -51,7 +51,7 @@ if FREEBSD:
cext.SWAIT: _common.STATUS_WAITING,
cext.SLOCK: _common.STATUS_LOCKED,
}
-elif OPENBSD or NETBSD:
+elif OPENBSD:
PROC_STATUSES = {
cext.SIDL: _common.STATUS_IDLE,
cext.SSLEEP: _common.STATUS_SLEEPING,
@@ -76,12 +76,11 @@ elif OPENBSD or NETBSD:
elif NETBSD:
PROC_STATUSES = {
cext.SIDL: _common.STATUS_IDLE,
- cext.SACTIVE: _common.STATUS_RUNNING,
- cext.SDYING: _common.STATUS_ZOMBIE,
+ cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SSTOP: _common.STATUS_STOPPED,
cext.SZOMB: _common.STATUS_ZOMBIE,
- cext.SDEAD: _common.STATUS_DEAD,
- cext.SSUSPENDED: _common.STATUS_SUSPENDED, # unique to NetBSD
+ cext.SRUN: _common.STATUS_WAKING,
+ cext.SONPROC: _common.STATUS_RUNNING,
}
TCP_STATUSES = {
@@ -670,6 +669,10 @@ class Process(object):
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']]
tmap = _psposix.get_terminal_map()
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index 953fcd08..c4450d7d 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -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"
@@ -392,6 +393,145 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) {
/*
+ * Return process environment as a Python dictionary
+ */
+PyObject *
+psutil_proc_environ(PyObject *self, PyObject *args) {
+ int i, cnt = -1;
+ long pid;
+ char *s, **envs, errbuf[_POSIX2_LINE_MAX];
+ PyObject *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, &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) {
+ NoSuchProcess("kvm_getprocs");
+ goto error;
+ }
+ if (cnt <= 0) {
+ NoSuchProcess(cnt < 0 ? kvm_geterr(kd) : "kvm_getprocs: cnt==0");
+ goto error;
+ }
+
+ // On *BSD kernels there are a few kernel-only system processes without an
+ // environment (See e.g. "procstat -e 0 | 1 | 2 ..." on FreeBSD.)
+ //
+ // Some system process have no stats attached at all
+ // (they are marked with P_SYSTEM.)
+ //
+ // On FreeBSD, it's possible that the process is swapped or paged out,
+ // then there no access to the environ stored in the process' user area.
+ //
+ // On NetBSD, we cannot call kvm_getenvv2() for a zombie process.
+ //
+ // To make unittest suite happy, return an empty environment.
+ //
+#if defined(PSUTIL_FREEBSD)
+#if (defined(__FreeBSD_version) && __FreeBSD_version >= 700000)
+ if (!((p)->ki_flag & P_INMEM) || ((p)->ki_flag & P_SYSTEM)) {
+#else
+ if ((p)->ki_flag & P_SYSTEM) {
+#endif
+#elif defined(PSUTIL_NETBSD)
+ if ((p)->p_stat == SZOMB) {
+#elif defined(PSUTIL_OPENBSD)
+ if ((p)->p_flag & P_SYSTEM) {
+#endif
+ 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 to "psutil" general high-level exceptions
+ switch (errno) {
+ case 0:
+ // Process has cleared it's environment, return empty one
+ kvm_close(kd);
+ return py_retdict;
+ case EPERM:
+ AccessDenied("kvm_getenvv");
+ break;
+ case ESRCH:
+ NoSuchProcess("kvm_getenvv");
+ break;
+#if defined(PSUTIL_FREEBSD)
+ case ENOMEM:
+ // Unfortunately, under FreeBSD kvm_getenvv() returns
+ // failure for certain processes ( e.g. try
+ // "sudo procstat -e <pid of your XOrg server>".)
+ // Map the error condition to 'AccessDenied'.
+ sprintf(errbuf,
+ "kvm_getenvv(pid=%ld, ki_uid=%d): errno=ENOMEM",
+ pid, p->ki_uid);
+ AccessDenied(errbuf);
+ break;
+#endif
+ default:
+ sprintf(errbuf, "kvm_getenvv(pid=%ld)", pid);
+ PyErr_SetFromOSErrnoWithSyscall(errbuf);
+ break;
+ }
+ goto error;
+ }
+
+ 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 NULL;
+}
+
+/*
* Return the number of logical CPUs in the system.
* XXX this could be shared with macOS
*/
@@ -617,8 +757,10 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
strlcat(opts, ",softdep", sizeof(opts));
if (flags & MNT_NOSYMFOLLOW)
strlcat(opts, ",nosymfollow", sizeof(opts));
+#ifdef MNT_GJOURNAL
if (flags & MNT_GJOURNAL)
strlcat(opts, ",gjournal", sizeof(opts));
+#endif
if (flags & MNT_MULTILABEL)
strlcat(opts, ",multilabel", sizeof(opts));
if (flags & MNT_ACLS)
@@ -627,8 +769,10 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
strlcat(opts, ",noclusterr", sizeof(opts));
if (flags & MNT_NOCLUSTERW)
strlcat(opts, ",noclusterw", sizeof(opts));
+#ifdef MNT_NFS4ACLS
if (flags & MNT_NFS4ACLS)
strlcat(opts, ",nfs4acls", sizeof(opts));
+#endif
#elif PSUTIL_NETBSD
if (flags & MNT_NODEV)
strlcat(opts, ",nodev", sizeof(opts));
@@ -831,7 +975,7 @@ psutil_users(PyObject *self, PyObject *args) {
py_tty, // tty
py_hostname, // hostname
(float)ut.ut_time, // start time
-#ifdef PSUTIL_OPENBSD
+#if defined(PSUTIL_OPENBSD) || (defined(__FreeBSD_version) && __FreeBSD_version < 900000)
-1 // process id (set to None later)
#else
ut.ut_pid // TODO: use PyLong_FromPid
@@ -956,6 +1100,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
@@ -1060,7 +1206,9 @@ static PyMethodDef mod_methods[] = {
if (PyModule_AddIntConstant(mod, "SSLEEP", LSSLEEP)) INITERR;
if (PyModule_AddIntConstant(mod, "SSTOP", LSSTOP)) INITERR;
if (PyModule_AddIntConstant(mod, "SZOMB", LSZOMB)) INITERR;
+#if __NetBSD_Version__ < 500000000
if (PyModule_AddIntConstant(mod, "SDEAD", LSDEAD)) INITERR;
+#endif
if (PyModule_AddIntConstant(mod, "SONPROC", LSONPROC)) INITERR;
// unique to NetBSD
if (PyModule_AddIntConstant(mod, "SSUSPENDED", LSSUSPENDED)) INITERR;
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index 7a87c3c1..f821aba3 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -167,6 +167,26 @@ psutil_setup(void) {
}
+// ============================================================================
+// Utility functions (BSD)
+// ============================================================================
+
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+void
+convert_kvm_err(const char *syscall, char *errbuf) {
+ char fullmsg[8192];
+
+ sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf);
+ if (strstr(errbuf, "Permission denied") != NULL)
+ AccessDenied(fullmsg);
+ else if (strstr(errbuf, "Operation not permitted") != NULL)
+ AccessDenied(fullmsg);
+ else
+ PyErr_Format(PyExc_RuntimeError, fullmsg);
+}
+#endif
+
+
// ====================================================================
// --- Windows
// ====================================================================
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 2408c9f6..3162772e 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -105,6 +105,12 @@ void psutil_debug(const char* format, ...);
int psutil_setup(void);
// ====================================================================
+// --- BSD
+// ====================================================================
+
+void convert_kvm_err(const char *syscall, char *errbuf);
+
+// ====================================================================
// --- Windows
// ====================================================================
diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c
index 3f37a08e..c7832647 100644
--- a/psutil/arch/freebsd/specific.c
+++ b/psutil/arch/freebsd/specific.c
@@ -517,7 +517,7 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
}
-#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 701000
PyObject *
psutil_proc_cwd(PyObject *self, PyObject *args) {
pid_t pid;
@@ -795,9 +795,11 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
case KVME_TYPE_DEAD:
path = "[dead]";
break;
+#ifdef KVME_TYPE_SG
case KVME_TYPE_SG:
path = "[sg]";
break;
+#endif
case KVME_TYPE_UNKNOWN:
path = "[unknown]";
break;
diff --git a/psutil/arch/freebsd/sys_socks.c b/psutil/arch/freebsd/sys_socks.c
index ab61f393..9f7cf8d5 100644
--- a/psutil/arch/freebsd/sys_socks.c
+++ b/psutil/arch/freebsd/sys_socks.c
@@ -16,6 +16,9 @@
#include <sys/un.h>
#include <sys/unpcb.h>
#include <sys/sysctl.h>
+#if defined(__FreeBSD_version) && __FreeBSD_version < 800000
+#include <netinet/in_systm.h>
+#endif
#include <netinet/in.h> // for xinpcb struct
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
@@ -30,7 +33,7 @@ static int psutil_nxfiles;
int
-psutil_populate_xfiles() {
+psutil_populate_xfiles(void) {
size_t len;
if ((psutil_xfiles = malloc(len = sizeof *psutil_xfiles)) == NULL) {
diff --git a/psutil/arch/openbsd/specific.c b/psutil/arch/openbsd/specific.c
index d97a8f9b..aa4568f5 100644
--- a/psutil/arch/openbsd/specific.c
+++ b/psutil/arch/openbsd/specific.c
@@ -47,20 +47,6 @@
// ============================================================================
-static void
-convert_kvm_err(const char *syscall, char *errbuf) {
- char fullmsg[8192];
-
- sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf);
- if (strstr(errbuf, "Permission denied") != NULL)
- AccessDenied(fullmsg);
- else if (strstr(errbuf, "Operation not permitted") != NULL)
- AccessDenied(fullmsg);
- else
- PyErr_Format(PyExc_RuntimeError, fullmsg);
-}
-
-
int
psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
// Fills a kinfo_proc struct based on process pid.
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index 51bbb9f0..2d9e5917 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -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)
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index e3394799..8b5e5c56 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -298,7 +298,7 @@ class TestProcess(PsutilTestCase):
@unittest.skipIf(TRAVIS or CIRRUS, 'not reliable on TRAVIS/CIRRUS')
def test_terminal(self):
terminal = psutil.Process().terminal()
- if sys.stdout.isatty():
+ if sys.stdin.isatty():
tty = os.path.realpath(sh('tty'))
self.assertEqual(terminal, tty)
else: