diff options
| author | Armin Gruner <ag-github@muc.de> | 2020-08-13 15:39:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-13 15:39:29 +0200 |
| commit | b09f5a28c98f1d4ee160e133ee5fc5446efba68f (patch) | |
| tree | 3b1eef9bdcc588f5f98acd718d2afae958ec0af8 | |
| parent | a0967043b5819b2edc61d9a12306289d5e7f98c2 (diff) | |
| download | psutil-b09f5a28c98f1d4ee160e133ee5fc5446efba68f.tar.gz | |
Implement Process.environ() on *BSD family (#1800) (patch by Armin Gruner)
| -rw-r--r-- | psutil/_psbsd.py | 13 | ||||
| -rw-r--r-- | psutil/_psutil_bsd.c | 150 | ||||
| -rw-r--r-- | psutil/_psutil_common.c | 20 | ||||
| -rw-r--r-- | psutil/_psutil_common.h | 6 | ||||
| -rw-r--r-- | psutil/arch/freebsd/specific.c | 4 | ||||
| -rw-r--r-- | psutil/arch/freebsd/sys_socks.c | 5 | ||||
| -rw-r--r-- | psutil/arch/openbsd/specific.c | 14 | ||||
| -rwxr-xr-x | psutil/tests/test_contracts.py | 3 | ||||
| -rwxr-xr-x | psutil/tests/test_process.py | 2 |
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: |
