Skip to content

Commit

Permalink
fix #717: [Linux] Process.open_files fails if deleted files still vis…
Browse files Browse the repository at this point in the history
…ible.
  • Loading branch information
giampaolo committed Dec 15, 2015
1 parent d832788 commit 29334f1
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 19 deletions.
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues

- #714: [OpenBSD] virtual_memory().cached value was always set to 0.
- #715: don't crash at import time if cpu_times() fail for some reason.
- #717: [Linux] Process.open_files fails if deleted files still visible.


3.3.0 - 2015-11-25
Expand Down
15 changes: 15 additions & 0 deletions psutil/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ def isfile_strict(path):
return stat.S_ISREG(st.st_mode)


def path_exists_strict(path):
"""Same as os.path.exists() but does not swallow EACCES / EPERM
exceptions, see:
http://mail.python.org/pipermail/python-dev/2012-June/120787.html
"""
try:
os.stat(path)
except OSError as err:
if err.errno in (errno.EPERM, errno.EACCES):
raise
return False
else:
return True


def supports_ipv6():
"""Return True if IPv6 is supported on this platform."""
if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"):
Expand Down
39 changes: 20 additions & 19 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ._common import NIC_DUPLEX_FULL
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
from ._common import path_exists_strict
from ._common import supports_ipv6
from ._common import usage_percent
from ._compat import b
Expand Down Expand Up @@ -149,6 +150,21 @@ def get_procfs_path():
return sys.modules['psutil'].PROCFS_PATH


def readlink(path):
"""Wrapper around os.readlink()."""
path = os.readlink(path)
# readlink() might return paths containing null bytes causing
# problems when used with other fs-related functions (os.*,
# open(), ...), see:
# https://github.com/giampaolo/psutil/issues/717
path = path.replace('\x00', '')
# Certain paths have ' (deleted)' appended. Usually this is
# bogus as the file actually exists.
if path.endswith(' (deleted)') and not path_exists_strict(path):
path = path[:-10]
return path


# --- named tuples

@memoize
Expand Down Expand Up @@ -435,8 +451,7 @@ def get_proc_inodes(self, pid):
inodes = defaultdict(list)
for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)):
try:
inode = os.readlink("%s/%s/fd/%s" % (
self._procfs_path, pid, fd))
inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd))
except OSError as err:
# ENOENT == file which is gone in the meantime;
# os.stat('/proc/%s' % self.pid) will be done later
Expand Down Expand Up @@ -825,7 +840,7 @@ def name(self):

def exe(self):
try:
exe = os.readlink("%s/%s/exe" % (self._procfs_path, self.pid))
return readlink("%s/%s/exe" % (self._procfs_path, self.pid))
except OSError as err:
if err.errno in (errno.ENOENT, errno.ESRCH):
# no such file error; might be raised also if the
Expand All @@ -842,16 +857,6 @@ def exe(self):
raise AccessDenied(self.pid, self._name)
raise

# readlink() might return paths containing null bytes ('\x00').
# Certain names have ' (deleted)' appended. Usually this is
# bogus as the file actually exists. Either way that's not
# important as we don't want to discriminate executables which
# have been deleted.
exe = exe.split('\x00')[0]
if exe.endswith(' (deleted)') and not os.path.exists(exe):
exe = exe[:-10]
return exe

@wrap_exceptions
def cmdline(self):
with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f:
Expand Down Expand Up @@ -1026,11 +1031,7 @@ def memory_maps(self):

@wrap_exceptions_w_zombie
def cwd(self):
# readlink() might return paths containing null bytes causing
# problems when used with other fs-related functions (os.*,
# open(), ...)
path = os.readlink("%s/%s/cwd" % (self._procfs_path, self.pid))
return path.replace('\x00', '')
return readlink("%s/%s/cwd" % (self._procfs_path, self.pid))

@wrap_exceptions
def num_ctx_switches(self):
Expand Down Expand Up @@ -1208,7 +1209,7 @@ def open_files(self):
for fd in files:
file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd)
try:
file = os.readlink(file)
file = readlink(file)
except OSError as err:
# ENOENT == file which is gone in the meantime
if err.errno in (errno.ENOENT, errno.ESRCH):
Expand Down

0 comments on commit 29334f1

Please sign in to comment.