summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2022-10-19 02:28:14 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2022-10-19 02:28:14 +0200
commitaa9a4f6f9bfaca1dd1d22034d007bbf5d86b9670 (patch)
tree106256b2c758aa7253c6b23a67b0938f0795acc2
parent1da9f7928e79ecc127768e7a5bc4eb7f785f4513 (diff)
downloadpsutil-linux-zombie-proc.tar.gz
raise ZombieProcess instead of AccessDenied if process is a zombielinux-zombie-proc
-rw-r--r--HISTORY.rst11
-rw-r--r--psutil/__init__.py2
-rw-r--r--psutil/_pslinux.py28
-rwxr-xr-xpsutil/tests/test_process.py6
4 files changed, 44 insertions, 3 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index e9833990..b742cd8d 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,5 +1,16 @@
*Bug tracker at https://github.com/giampaolo/psutil/issues*
+5.9.4 (IN DEVELOPMENT)
+======================
+
+XXXX-XX-XX
+
+**Bug fixes**
+
+- XXXX_, [Linux]: when process is a zombie, certain methods like
+ `Process.open_files()`_, `Process.environ()`_ and `Process.io_counters
+ ()`_ erroneously raise ``AccessDenied`` instead of ``ZombieProcess``.
+
5.9.3
=====
diff --git a/psutil/__init__.py b/psutil/__init__.py
index ca2d9273..56742791 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -211,7 +211,7 @@ if hasattr(_psplatform.Process, "rlimit"):
AF_LINK = _psplatform.AF_LINK
__author__ = "Giampaolo Rodola'"
-__version__ = "5.9.3"
+__version__ = "5.9.4"
version_info = tuple([int(num) for num in __version__.split('.')])
_timer = getattr(time, 'monotonic', time.time)
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 9dc9643a..c16db892 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -1635,6 +1635,20 @@ def ppid_map():
return ret
+def is_zombie(pid):
+ try:
+ data = bcat("%s/%s/stat" % (get_procfs_path(), pid))
+ except EnvironmentError:
+ return False
+ else:
+ # Process name is between parentheses. It can contain spaces and
+ # other parentheses. This is taken into account by looking for
+ # the first occurrence of "(" and the last occurrence of ")".
+ rpar = data.rfind(b')')
+ fields = data[rpar + 2:].split()
+ return fields[0] == "Z"
+
+
def wrap_exceptions(fun):
"""Decorator which translates bare OSError and IOError exceptions
into NoSuchProcess and AccessDenied.
@@ -1644,9 +1658,19 @@ def wrap_exceptions(fun):
try:
return fun(self, *args, **kwargs)
except PermissionError:
- raise AccessDenied(self.pid, self._name)
+ # Linux is peculiar in that accessing certain pseudo files
+ # like /proc/pid/fd/*, /proc/pid/environ and /proc/pid/io
+ # results in EPERM if process is a zombie. In such cases it
+ # appears more correct to raise ZP instead of AD.
+ if is_zombie(self.pid):
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ raise AccessDenied(self.pid, self._name)
except ProcessLookupError:
- raise NoSuchProcess(self.pid, self._name)
+ if is_zombie(self.pid):
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ raise NoSuchProcess(self.pid, self._name)
except FileNotFoundError:
if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)):
raise NoSuchProcess(self.pid, self._name)
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index 26869e98..bd31ac66 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -1324,6 +1324,12 @@ class TestProcess(PsutilTestCase):
for fun, name in ns.iter(ns.all):
succeed_or_zombie_p_exc(fun)
+ if LINUX:
+ # TODO: broken
+ # self.assertRaises(psutil.ZombieProcess, zproc.exe)
+ self.assertRaises(psutil.ZombieProcess, zproc.cwd)
+ self.assertRaises(psutil.ZombieProcess, zproc.memory_full_info)
+
assert psutil.pid_exists(zproc.pid)
self.assertIn(zproc.pid, psutil.pids())
self.assertIn(zproc.pid, [x.pid for x in psutil.process_iter()])