Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

84 рядки
2.6 KiB

  1. import io
  2. import os
  3. import re
  4. import sys
  5. from ._core import Process
  6. # FreeBSD: https://www.freebsd.org/cgi/man.cgi?query=procfs
  7. # NetBSD: https://man.netbsd.org/NetBSD-9.3-STABLE/mount_procfs.8
  8. # DragonFlyBSD: https://www.dragonflybsd.org/cgi/web-man?command=procfs
  9. BSD_STAT_PPID = 2
  10. # See https://docs.kernel.org/filesystems/proc.html
  11. LINUX_STAT_PPID = 3
  12. STAT_PATTERN = re.compile(r"\(.+\)|\S+")
  13. def detect_proc():
  14. """Detect /proc filesystem style.
  15. This checks the /proc/{pid} directory for possible formats. Returns one of
  16. the following as str:
  17. * `stat`: Linux-style, i.e. ``/proc/{pid}/stat``.
  18. * `status`: BSD-style, i.e. ``/proc/{pid}/status``.
  19. """
  20. pid = os.getpid()
  21. for name in ("stat", "status"):
  22. if os.path.exists(os.path.join("/proc", str(pid), name)):
  23. return name
  24. raise ProcFormatError("unsupported proc format")
  25. def _use_bsd_stat_format():
  26. try:
  27. return os.uname().sysname.lower() in ("freebsd", "netbsd", "dragonfly")
  28. except Exception:
  29. return False
  30. def _get_ppid(pid, name):
  31. path = os.path.join("/proc", str(pid), name)
  32. with io.open(path, encoding="ascii", errors="replace") as f:
  33. parts = STAT_PATTERN.findall(f.read())
  34. # We only care about TTY and PPID -- both are numbers.
  35. if _use_bsd_stat_format():
  36. return parts[BSD_STAT_PPID]
  37. return parts[LINUX_STAT_PPID]
  38. def _get_cmdline(pid):
  39. path = os.path.join("/proc", str(pid), "cmdline")
  40. encoding = sys.getfilesystemencoding() or "utf-8"
  41. with io.open(path, encoding=encoding, errors="replace") as f:
  42. # XXX: Command line arguments can be arbitrary byte sequences, not
  43. # necessarily decodable. For Shellingham's purpose, however, we don't
  44. # care. (pypa/pipenv#2820)
  45. # cmdline appends an extra NULL at the end, hence the [:-1].
  46. return tuple(f.read().split("\0")[:-1])
  47. class ProcFormatError(EnvironmentError):
  48. pass
  49. def iter_process_parents(pid, max_depth=10):
  50. """Try to look up the process tree via the /proc interface."""
  51. stat_name = detect_proc()
  52. # Inner generator function so we correctly throw an error eagerly if proc
  53. # is not supported, rather than on the first call to the iterator. This
  54. # allows the call site detects the correct implementation.
  55. def _iter_process_parents(pid, max_depth):
  56. for _ in range(max_depth):
  57. ppid = _get_ppid(pid, stat_name)
  58. args = _get_cmdline(pid)
  59. yield Process(args=args, pid=pid, ppid=ppid)
  60. if ppid == "0":
  61. break
  62. pid = ppid
  63. return _iter_process_parents(pid, max_depth)