From f65fe4451a26205e3c0f9a97e7e49902bf275c3b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 14 Oct 2024 20:58:52 +0200 Subject: [PATCH] [OpenBSD] Process `num_fds()` and `open_files()` may raise NSP for PID 0 (#2460) This is weird, because of all the sudden the tests below started failing. This is despite PID 0 exists. The syscall failing with `ESRCH` is `kinfo_getfile`: https://github.com/giampaolo/psutil/blob/f8b929b0754a4abbdadfdcb8aae382ddee8a13f9/psutil/arch/openbsd/proc.c#L57 We therefore return "null" fallback values instead of raising `NoSuchProcess`. ``` =================================== FAILURES =================================== ___________________________ TestProcess.test_as_dict ___________________________ psutil/_psbsd.py:604: in wrapper return fun(self, *args, **kwargs) psutil/_psbsd.py:914: in open_files rawlist = cext.proc_open_files(self.pid) E ProcessLookupError: [Errno 3] No such process (originated from sysctl(kinfo_file) (1/2)) During handling of the above exception, another exception occurred: psutil/tests/test_process.py:1309: in test_as_dict d = p.as_dict() psutil/__init__.py:558: in as_dict ret = meth() psutil/__init__.py:1199: in open_files return self._proc.open_files() psutil/_psbsd.py:609: in wrapper raise NoSuchProcess(self.pid, self._name) E psutil.NoSuchProcess: process no longer exists (pid=0) ____________________________ TestProcess.test_pid_0 ____________________________ psutil/_psbsd.py:604: in wrapper return fun(self, *args, **kwargs) psutil/_psbsd.py:914: in open_files rawlist = cext.proc_open_files(self.pid) E ProcessLookupError: [Errno 3] No such process (originated from sysctl(kinfo_file) (1/2)) During handling of the above exception, another exception occurred: psutil/tests/test_process.py:1513: in test_pid_0 ret = fun() psutil/__init__.py:1199: in open_files return self._proc.open_files() psutil/_psbsd.py:609: in wrapper raise NoSuchProcess(self.pid, self._name) E psutil.NoSuchProcess: process no longer exists (pid=0) ________________________ TestFetchAllProcesses.test_all ________________________ psutil/_psbsd.py:604: in wrapper return fun(self, *args, **kwargs) psutil/_psbsd.py:914: in open_files rawlist = cext.proc_open_files(self.pid) E ProcessLookupError: [Errno 3] No such process (originated from sysctl(kinfo_file) (1/2)) During handling of the above exception, another exception occurred: psutil/tests/test_process_all.py:93: in proc_info info[fun_name] = fun() psutil/__init__.py:1199: in open_files return self._proc.open_files() psutil/_psbsd.py:609: in wrapper raise NoSuchProcess(self.pid, self._name) E psutil.NoSuchProcess: process no longer exists (pid=0, name='swapper') During handling of the above exception, another exception occurred: psutil/tests/test_process_all.py:135: in test_all for info in self.iter_proc_info(): psutil/tests/test_process_all.py:130: in iter_proc_info ls.append(proc_info(pid)) psutil/tests/test_process_all.py:95: in proc_info check_exception(exc, proc, name, ppid) psutil/tests/test_process_all.py:65: in check_exception tcase.assertProcessGone(proc) psutil/tests/__init__.py:1069: in assertProcessGone self.assertPidGone(proc.pid) psutil/tests/__init__.py:1057: in assertPidGone with pytest.raises(psutil.NoSuchProcess) as cm: E Failed: DID NOT RAISE ``` --- HISTORY.rst | 3 +++ INSTALL.rst | 4 ++-- psutil/arch/bsd/proc.c | 11 ++++++++++- psutil/arch/openbsd/proc.c | 19 +++++++++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index c854ef80b..6ed9bd3da 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -25,6 +25,9 @@ XXXX-XX-XX Python 3.13. (patch by Sam Gross) - 2455_, [Linux]: ``IndexError`` may occur when reading /proc/pid/stat and field 40 (blkio_ticks) is missing. +- 2460_, [OpenBSD]: `Process.num_fds()`_ and `Process.open_files()`_ may fail + with `NoSuchProcess`_ for PID 0. Instead, we now return "null" values (0 and + [] respectively). 6.0.0 ====== diff --git a/INSTALL.rst b/INSTALL.rst index a425400fd..8e06d400e 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -82,7 +82,7 @@ OpenBSD :: - export PKG_PATH=http://ftp.eu.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/ + export PKG_PATH=https://cdn.openbsd.org/pub/OpenBSD/`uname -r`/packages/`uname -m`/ pkg_add -v python3 gcc pip install psutil @@ -93,7 +93,7 @@ Assuming Python 3.11 (the most recent at the time of writing): :: - export PKG_PATH="http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All" + export PKG_PATH="https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All" pkg_add -v pkgin pkgin install python311-* gcc12-* py311-setuptools-* py311-pip-* python3.11 -m pip install psutil diff --git a/psutil/arch/bsd/proc.c b/psutil/arch/bsd/proc.c index 5d353ff35..959b28ee0 100644 --- a/psutil/arch/bsd/proc.c +++ b/psutil/arch/bsd/proc.c @@ -442,8 +442,17 @@ psutil_proc_open_files(PyObject *self, PyObject *args) { errno = 0; freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { -#if !defined(PSUTIL_OPENBSD) +#if defined(PSUTIL_OPENBSD) + if ((pid == 0) && (errno == ESRCH)) { + psutil_debug( + "open_files() returned ESRCH for PID 0; forcing `return []`" + ); + PyErr_Clear(); + return py_retlist; + } +#else psutil_raise_for_pid(pid, "kinfo_getfile()"); #endif goto error; diff --git a/psutil/arch/openbsd/proc.c b/psutil/arch/openbsd/proc.c index 0881ccd55..bf4015be4 100644 --- a/psutil/arch/openbsd/proc.c +++ b/psutil/arch/openbsd/proc.c @@ -41,7 +41,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) { ret = sysctl((int*)mib, 6, proc, &size, NULL, 0); if (ret == -1) { - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_proc)"); return -1; } // sysctl stores 0 in the size if we can't find the process information. @@ -69,7 +69,7 @@ kinfo_getfile(pid_t pid, int* cnt) { /* get the size of what would be returned */ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_file) (1/2)"); return NULL; } if ((kf = malloc(len)) == NULL) { @@ -79,7 +79,7 @@ kinfo_getfile(pid_t pid, int* cnt) { mib[5] = (int)(len / sizeof(struct kinfo_file)); if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) { free(kf); - PyErr_SetFromErrno(PyExc_OSError); + psutil_PyErr_SetFromOSErrnoWithSyscall("sysctl(kinfo_file) (2/2)"); return NULL; } @@ -288,8 +288,19 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) { return NULL; freep = kinfo_getfile(pid, &cnt); - if (freep == NULL) + + if (freep == NULL) { +#if defined(PSUTIL_OPENBSD) + if ((pid == 0) && (errno == ESRCH)) { + psutil_debug( + "num_fds() returned ESRCH for PID 0; forcing `return 0`" + ); + PyErr_Clear(); + return Py_BuildValue("i", 0); + } +#endif return NULL; + } free(freep); return Py_BuildValue("i", cnt);