_pssunos.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. """Sun OS Solaris platform implementation."""
  5. import errno
  6. import functools
  7. import os
  8. import socket
  9. import subprocess
  10. import sys
  11. from collections import namedtuple
  12. from socket import AF_INET
  13. from . import _common
  14. from . import _psposix
  15. from . import _psutil_posix as cext_posix
  16. from . import _psutil_sunos as cext
  17. from ._common import AccessDenied
  18. from ._common import AF_INET6
  19. from ._common import debug
  20. from ._common import get_procfs_path
  21. from ._common import isfile_strict
  22. from ._common import memoize_when_activated
  23. from ._common import NoSuchProcess
  24. from ._common import sockfam_to_enum
  25. from ._common import socktype_to_enum
  26. from ._common import usage_percent
  27. from ._common import ZombieProcess
  28. from ._compat import b
  29. from ._compat import FileNotFoundError
  30. from ._compat import PermissionError
  31. from ._compat import ProcessLookupError
  32. from ._compat import PY3
  33. __extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
  34. # =====================================================================
  35. # --- globals
  36. # =====================================================================
  37. PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
  38. AF_LINK = cext_posix.AF_LINK
  39. IS_64_BIT = sys.maxsize > 2**32
  40. CONN_IDLE = "IDLE"
  41. CONN_BOUND = "BOUND"
  42. PROC_STATUSES = {
  43. cext.SSLEEP: _common.STATUS_SLEEPING,
  44. cext.SRUN: _common.STATUS_RUNNING,
  45. cext.SZOMB: _common.STATUS_ZOMBIE,
  46. cext.SSTOP: _common.STATUS_STOPPED,
  47. cext.SIDL: _common.STATUS_IDLE,
  48. cext.SONPROC: _common.STATUS_RUNNING, # same as run
  49. cext.SWAIT: _common.STATUS_WAITING,
  50. }
  51. TCP_STATUSES = {
  52. cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
  53. cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
  54. cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
  55. cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
  56. cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
  57. cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
  58. cext.TCPS_CLOSED: _common.CONN_CLOSE,
  59. cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
  60. cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
  61. cext.TCPS_LISTEN: _common.CONN_LISTEN,
  62. cext.TCPS_CLOSING: _common.CONN_CLOSING,
  63. cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
  64. cext.TCPS_IDLE: CONN_IDLE, # sunos specific
  65. cext.TCPS_BOUND: CONN_BOUND, # sunos specific
  66. }
  67. proc_info_map = dict(
  68. ppid=0,
  69. rss=1,
  70. vms=2,
  71. create_time=3,
  72. nice=4,
  73. num_threads=5,
  74. status=6,
  75. ttynr=7,
  76. uid=8,
  77. euid=9,
  78. gid=10,
  79. egid=11)
  80. # =====================================================================
  81. # --- named tuples
  82. # =====================================================================
  83. # psutil.cpu_times()
  84. scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
  85. # psutil.cpu_times(percpu=True)
  86. pcputimes = namedtuple('pcputimes',
  87. ['user', 'system', 'children_user', 'children_system'])
  88. # psutil.virtual_memory()
  89. svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
  90. # psutil.Process.memory_info()
  91. pmem = namedtuple('pmem', ['rss', 'vms'])
  92. pfullmem = pmem
  93. # psutil.Process.memory_maps(grouped=True)
  94. pmmap_grouped = namedtuple('pmmap_grouped',
  95. ['path', 'rss', 'anonymous', 'locked'])
  96. # psutil.Process.memory_maps(grouped=False)
  97. pmmap_ext = namedtuple(
  98. 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
  99. # =====================================================================
  100. # --- memory
  101. # =====================================================================
  102. def virtual_memory():
  103. """Report virtual memory metrics."""
  104. # we could have done this with kstat, but IMHO this is good enough
  105. total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
  106. # note: there's no difference on Solaris
  107. free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
  108. used = total - free
  109. percent = usage_percent(used, total, round_=1)
  110. return svmem(total, avail, percent, used, free)
  111. def swap_memory():
  112. """Report swap memory metrics."""
  113. sin, sout = cext.swap_mem()
  114. # XXX
  115. # we are supposed to get total/free by doing so:
  116. # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
  117. # usr/src/cmd/swap/swap.c
  118. # ...nevertheless I can't manage to obtain the same numbers as 'swap'
  119. # cmdline utility, so let's parse its output (sigh!)
  120. p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
  121. os.environ['PATH'], 'swap', '-l'],
  122. stdout=subprocess.PIPE)
  123. stdout, stderr = p.communicate()
  124. if PY3:
  125. stdout = stdout.decode(sys.stdout.encoding)
  126. if p.returncode != 0:
  127. raise RuntimeError("'swap -l' failed (retcode=%s)" % p.returncode)
  128. lines = stdout.strip().split('\n')[1:]
  129. if not lines:
  130. raise RuntimeError('no swap device(s) configured')
  131. total = free = 0
  132. for line in lines:
  133. line = line.split()
  134. t, f = line[-2:]
  135. total += int(int(t) * 512)
  136. free += int(int(f) * 512)
  137. used = total - free
  138. percent = usage_percent(used, total, round_=1)
  139. return _common.sswap(total, used, free, percent,
  140. sin * PAGE_SIZE, sout * PAGE_SIZE)
  141. # =====================================================================
  142. # --- CPU
  143. # =====================================================================
  144. def cpu_times():
  145. """Return system-wide CPU times as a named tuple"""
  146. ret = cext.per_cpu_times()
  147. return scputimes(*[sum(x) for x in zip(*ret)])
  148. def per_cpu_times():
  149. """Return system per-CPU times as a list of named tuples"""
  150. ret = cext.per_cpu_times()
  151. return [scputimes(*x) for x in ret]
  152. def cpu_count_logical():
  153. """Return the number of logical CPUs in the system."""
  154. try:
  155. return os.sysconf("SC_NPROCESSORS_ONLN")
  156. except ValueError:
  157. # mimic os.cpu_count() behavior
  158. return None
  159. def cpu_count_physical():
  160. """Return the number of physical CPUs in the system."""
  161. return cext.cpu_count_phys()
  162. def cpu_stats():
  163. """Return various CPU stats as a named tuple."""
  164. ctx_switches, interrupts, syscalls, traps = cext.cpu_stats()
  165. soft_interrupts = 0
  166. return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
  167. syscalls)
  168. # =====================================================================
  169. # --- disks
  170. # =====================================================================
  171. disk_io_counters = cext.disk_io_counters
  172. disk_usage = _psposix.disk_usage
  173. def disk_partitions(all=False):
  174. """Return system disk partitions."""
  175. # TODO - the filtering logic should be better checked so that
  176. # it tries to reflect 'df' as much as possible
  177. retlist = []
  178. partitions = cext.disk_partitions()
  179. for partition in partitions:
  180. device, mountpoint, fstype, opts = partition
  181. if device == 'none':
  182. device = ''
  183. if not all:
  184. # Differently from, say, Linux, we don't have a list of
  185. # common fs types so the best we can do, AFAIK, is to
  186. # filter by filesystem having a total size > 0.
  187. try:
  188. if not disk_usage(mountpoint).total:
  189. continue
  190. except OSError as err:
  191. # https://github.com/giampaolo/psutil/issues/1674
  192. debug("skipping %r: %r" % (mountpoint, err))
  193. continue
  194. ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
  195. retlist.append(ntuple)
  196. return retlist
  197. # =====================================================================
  198. # --- network
  199. # =====================================================================
  200. net_io_counters = cext.net_io_counters
  201. net_if_addrs = cext_posix.net_if_addrs
  202. def net_connections(kind, _pid=-1):
  203. """Return socket connections. If pid == -1 return system-wide
  204. connections (as opposed to connections opened by one process only).
  205. Only INET sockets are returned (UNIX are not).
  206. """
  207. cmap = _common.conn_tmap.copy()
  208. if _pid == -1:
  209. cmap.pop('unix', 0)
  210. if kind not in cmap:
  211. raise ValueError("invalid %r kind argument; choose between %s"
  212. % (kind, ', '.join([repr(x) for x in cmap])))
  213. families, types = _common.conn_tmap[kind]
  214. rawlist = cext.net_connections(_pid)
  215. ret = set()
  216. for item in rawlist:
  217. fd, fam, type_, laddr, raddr, status, pid = item
  218. if fam not in families:
  219. continue
  220. if type_ not in types:
  221. continue
  222. # TODO: refactor and use _common.conn_to_ntuple.
  223. if fam in (AF_INET, AF_INET6):
  224. if laddr:
  225. laddr = _common.addr(*laddr)
  226. if raddr:
  227. raddr = _common.addr(*raddr)
  228. status = TCP_STATUSES[status]
  229. fam = sockfam_to_enum(fam)
  230. type_ = socktype_to_enum(type_)
  231. if _pid == -1:
  232. nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
  233. else:
  234. nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
  235. ret.add(nt)
  236. return list(ret)
  237. def net_if_stats():
  238. """Get NIC stats (isup, duplex, speed, mtu)."""
  239. ret = cext.net_if_stats()
  240. for name, items in ret.items():
  241. isup, duplex, speed, mtu = items
  242. if hasattr(_common, 'NicDuplex'):
  243. duplex = _common.NicDuplex(duplex)
  244. ret[name] = _common.snicstats(isup, duplex, speed, mtu)
  245. return ret
  246. # =====================================================================
  247. # --- other system functions
  248. # =====================================================================
  249. def boot_time():
  250. """The system boot time expressed in seconds since the epoch."""
  251. return cext.boot_time()
  252. def users():
  253. """Return currently connected users as a list of namedtuples."""
  254. retlist = []
  255. rawlist = cext.users()
  256. localhost = (':0.0', ':0')
  257. for item in rawlist:
  258. user, tty, hostname, tstamp, user_process, pid = item
  259. # note: the underlying C function includes entries about
  260. # system boot, run level and others. We might want
  261. # to use them in the future.
  262. if not user_process:
  263. continue
  264. if hostname in localhost:
  265. hostname = 'localhost'
  266. nt = _common.suser(user, tty, hostname, tstamp, pid)
  267. retlist.append(nt)
  268. return retlist
  269. # =====================================================================
  270. # --- processes
  271. # =====================================================================
  272. def pids():
  273. """Returns a list of PIDs currently running on the system."""
  274. return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()]
  275. def pid_exists(pid):
  276. """Check for the existence of a unix pid."""
  277. return _psposix.pid_exists(pid)
  278. def wrap_exceptions(fun):
  279. """Call callable into a try/except clause and translate ENOENT,
  280. EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
  281. """
  282. @functools.wraps(fun)
  283. def wrapper(self, *args, **kwargs):
  284. try:
  285. return fun(self, *args, **kwargs)
  286. except (FileNotFoundError, ProcessLookupError):
  287. # ENOENT (no such file or directory) gets raised on open().
  288. # ESRCH (no such process) can get raised on read() if
  289. # process is gone in meantime.
  290. if not pid_exists(self.pid):
  291. raise NoSuchProcess(self.pid, self._name)
  292. else:
  293. raise ZombieProcess(self.pid, self._name, self._ppid)
  294. except PermissionError:
  295. raise AccessDenied(self.pid, self._name)
  296. except OSError:
  297. if self.pid == 0:
  298. if 0 in pids():
  299. raise AccessDenied(self.pid, self._name)
  300. else:
  301. raise
  302. raise
  303. return wrapper
  304. class Process(object):
  305. """Wrapper class around underlying C implementation."""
  306. __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
  307. def __init__(self, pid):
  308. self.pid = pid
  309. self._name = None
  310. self._ppid = None
  311. self._procfs_path = get_procfs_path()
  312. def _assert_alive(self):
  313. """Raise NSP if the process disappeared on us."""
  314. # For those C function who do not raise NSP, possibly returning
  315. # incorrect or incomplete result.
  316. os.stat('%s/%s' % (self._procfs_path, self.pid))
  317. def oneshot_enter(self):
  318. self._proc_name_and_args.cache_activate(self)
  319. self._proc_basic_info.cache_activate(self)
  320. self._proc_cred.cache_activate(self)
  321. def oneshot_exit(self):
  322. self._proc_name_and_args.cache_deactivate(self)
  323. self._proc_basic_info.cache_deactivate(self)
  324. self._proc_cred.cache_deactivate(self)
  325. @wrap_exceptions
  326. @memoize_when_activated
  327. def _proc_name_and_args(self):
  328. return cext.proc_name_and_args(self.pid, self._procfs_path)
  329. @wrap_exceptions
  330. @memoize_when_activated
  331. def _proc_basic_info(self):
  332. if self.pid == 0 and not \
  333. os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
  334. raise AccessDenied(self.pid)
  335. ret = cext.proc_basic_info(self.pid, self._procfs_path)
  336. assert len(ret) == len(proc_info_map)
  337. return ret
  338. @wrap_exceptions
  339. @memoize_when_activated
  340. def _proc_cred(self):
  341. return cext.proc_cred(self.pid, self._procfs_path)
  342. @wrap_exceptions
  343. def name(self):
  344. # note: max len == 15
  345. return self._proc_name_and_args()[0]
  346. @wrap_exceptions
  347. def exe(self):
  348. try:
  349. return os.readlink(
  350. "%s/%s/path/a.out" % (self._procfs_path, self.pid))
  351. except OSError:
  352. pass # continue and guess the exe name from the cmdline
  353. # Will be guessed later from cmdline but we want to explicitly
  354. # invoke cmdline here in order to get an AccessDenied
  355. # exception if the user has not enough privileges.
  356. self.cmdline()
  357. return ""
  358. @wrap_exceptions
  359. def cmdline(self):
  360. return self._proc_name_and_args()[1].split(' ')
  361. @wrap_exceptions
  362. def environ(self):
  363. return cext.proc_environ(self.pid, self._procfs_path)
  364. @wrap_exceptions
  365. def create_time(self):
  366. return self._proc_basic_info()[proc_info_map['create_time']]
  367. @wrap_exceptions
  368. def num_threads(self):
  369. return self._proc_basic_info()[proc_info_map['num_threads']]
  370. @wrap_exceptions
  371. def nice_get(self):
  372. # Note #1: getpriority(3) doesn't work for realtime processes.
  373. # Psinfo is what ps uses, see:
  374. # https://github.com/giampaolo/psutil/issues/1194
  375. return self._proc_basic_info()[proc_info_map['nice']]
  376. @wrap_exceptions
  377. def nice_set(self, value):
  378. if self.pid in (2, 3):
  379. # Special case PIDs: internally setpriority(3) return ESRCH
  380. # (no such process), no matter what.
  381. # The process actually exists though, as it has a name,
  382. # creation time, etc.
  383. raise AccessDenied(self.pid, self._name)
  384. return cext_posix.setpriority(self.pid, value)
  385. @wrap_exceptions
  386. def ppid(self):
  387. self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
  388. return self._ppid
  389. @wrap_exceptions
  390. def uids(self):
  391. try:
  392. real, effective, saved, _, _, _ = self._proc_cred()
  393. except AccessDenied:
  394. real = self._proc_basic_info()[proc_info_map['uid']]
  395. effective = self._proc_basic_info()[proc_info_map['euid']]
  396. saved = None
  397. return _common.puids(real, effective, saved)
  398. @wrap_exceptions
  399. def gids(self):
  400. try:
  401. _, _, _, real, effective, saved = self._proc_cred()
  402. except AccessDenied:
  403. real = self._proc_basic_info()[proc_info_map['gid']]
  404. effective = self._proc_basic_info()[proc_info_map['egid']]
  405. saved = None
  406. return _common.puids(real, effective, saved)
  407. @wrap_exceptions
  408. def cpu_times(self):
  409. try:
  410. times = cext.proc_cpu_times(self.pid, self._procfs_path)
  411. except OSError as err:
  412. if err.errno == errno.EOVERFLOW and not IS_64_BIT:
  413. # We may get here if we attempt to query a 64bit process
  414. # with a 32bit python.
  415. # Error originates from read() and also tools like "cat"
  416. # fail in the same way (!).
  417. # Since there simply is no way to determine CPU times we
  418. # return 0.0 as a fallback. See:
  419. # https://github.com/giampaolo/psutil/issues/857
  420. times = (0.0, 0.0, 0.0, 0.0)
  421. else:
  422. raise
  423. return _common.pcputimes(*times)
  424. @wrap_exceptions
  425. def cpu_num(self):
  426. return cext.proc_cpu_num(self.pid, self._procfs_path)
  427. @wrap_exceptions
  428. def terminal(self):
  429. procfs_path = self._procfs_path
  430. hit_enoent = False
  431. tty = wrap_exceptions(
  432. self._proc_basic_info()[proc_info_map['ttynr']])
  433. if tty != cext.PRNODEV:
  434. for x in (0, 1, 2, 255):
  435. try:
  436. return os.readlink(
  437. '%s/%d/path/%d' % (procfs_path, self.pid, x))
  438. except FileNotFoundError:
  439. hit_enoent = True
  440. continue
  441. if hit_enoent:
  442. self._assert_alive()
  443. @wrap_exceptions
  444. def cwd(self):
  445. # /proc/PID/path/cwd may not be resolved by readlink() even if
  446. # it exists (ls shows it). If that's the case and the process
  447. # is still alive return None (we can return None also on BSD).
  448. # Reference: http://goo.gl/55XgO
  449. procfs_path = self._procfs_path
  450. try:
  451. return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
  452. except FileNotFoundError:
  453. os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
  454. return None
  455. @wrap_exceptions
  456. def memory_info(self):
  457. ret = self._proc_basic_info()
  458. rss = ret[proc_info_map['rss']] * 1024
  459. vms = ret[proc_info_map['vms']] * 1024
  460. return pmem(rss, vms)
  461. memory_full_info = memory_info
  462. @wrap_exceptions
  463. def status(self):
  464. code = self._proc_basic_info()[proc_info_map['status']]
  465. # XXX is '?' legit? (we're not supposed to return it anyway)
  466. return PROC_STATUSES.get(code, '?')
  467. @wrap_exceptions
  468. def threads(self):
  469. procfs_path = self._procfs_path
  470. ret = []
  471. tids = os.listdir('%s/%d/lwp' % (procfs_path, self.pid))
  472. hit_enoent = False
  473. for tid in tids:
  474. tid = int(tid)
  475. try:
  476. utime, stime = cext.query_process_thread(
  477. self.pid, tid, procfs_path)
  478. except EnvironmentError as err:
  479. if err.errno == errno.EOVERFLOW and not IS_64_BIT:
  480. # We may get here if we attempt to query a 64bit process
  481. # with a 32bit python.
  482. # Error originates from read() and also tools like "cat"
  483. # fail in the same way (!).
  484. # Since there simply is no way to determine CPU times we
  485. # return 0.0 as a fallback. See:
  486. # https://github.com/giampaolo/psutil/issues/857
  487. continue
  488. # ENOENT == thread gone in meantime
  489. if err.errno == errno.ENOENT:
  490. hit_enoent = True
  491. continue
  492. raise
  493. else:
  494. nt = _common.pthread(tid, utime, stime)
  495. ret.append(nt)
  496. if hit_enoent:
  497. self._assert_alive()
  498. return ret
  499. @wrap_exceptions
  500. def open_files(self):
  501. retlist = []
  502. hit_enoent = False
  503. procfs_path = self._procfs_path
  504. pathdir = '%s/%d/path' % (procfs_path, self.pid)
  505. for fd in os.listdir('%s/%d/fd' % (procfs_path, self.pid)):
  506. path = os.path.join(pathdir, fd)
  507. if os.path.islink(path):
  508. try:
  509. file = os.readlink(path)
  510. except FileNotFoundError:
  511. hit_enoent = True
  512. continue
  513. else:
  514. if isfile_strict(file):
  515. retlist.append(_common.popenfile(file, int(fd)))
  516. if hit_enoent:
  517. self._assert_alive()
  518. return retlist
  519. def _get_unix_sockets(self, pid):
  520. """Get UNIX sockets used by process by parsing 'pfiles' output."""
  521. # TODO: rewrite this in C (...but the damn netstat source code
  522. # does not include this part! Argh!!)
  523. cmd = "pfiles %s" % pid
  524. p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
  525. stderr=subprocess.PIPE)
  526. stdout, stderr = p.communicate()
  527. if PY3:
  528. stdout, stderr = [x.decode(sys.stdout.encoding)
  529. for x in (stdout, stderr)]
  530. if p.returncode != 0:
  531. if 'permission denied' in stderr.lower():
  532. raise AccessDenied(self.pid, self._name)
  533. if 'no such process' in stderr.lower():
  534. raise NoSuchProcess(self.pid, self._name)
  535. raise RuntimeError("%r command error\n%s" % (cmd, stderr))
  536. lines = stdout.split('\n')[2:]
  537. for i, line in enumerate(lines):
  538. line = line.lstrip()
  539. if line.startswith('sockname: AF_UNIX'):
  540. path = line.split(' ', 2)[2]
  541. type = lines[i - 2].strip()
  542. if type == 'SOCK_STREAM':
  543. type = socket.SOCK_STREAM
  544. elif type == 'SOCK_DGRAM':
  545. type = socket.SOCK_DGRAM
  546. else:
  547. type = -1
  548. yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
  549. @wrap_exceptions
  550. def connections(self, kind='inet'):
  551. ret = net_connections(kind, _pid=self.pid)
  552. # The underlying C implementation retrieves all OS connections
  553. # and filters them by PID. At this point we can't tell whether
  554. # an empty list means there were no connections for process or
  555. # process is no longer active so we force NSP in case the PID
  556. # is no longer there.
  557. if not ret:
  558. # will raise NSP if process is gone
  559. os.stat('%s/%s' % (self._procfs_path, self.pid))
  560. # UNIX sockets
  561. if kind in ('all', 'unix'):
  562. ret.extend([_common.pconn(*conn) for conn in
  563. self._get_unix_sockets(self.pid)])
  564. return ret
  565. nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
  566. nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')
  567. @wrap_exceptions
  568. def memory_maps(self):
  569. def toaddr(start, end):
  570. return '%s-%s' % (hex(start)[2:].strip('L'),
  571. hex(end)[2:].strip('L'))
  572. procfs_path = self._procfs_path
  573. retlist = []
  574. try:
  575. rawlist = cext.proc_memory_maps(self.pid, procfs_path)
  576. except OSError as err:
  577. if err.errno == errno.EOVERFLOW and not IS_64_BIT:
  578. # We may get here if we attempt to query a 64bit process
  579. # with a 32bit python.
  580. # Error originates from read() and also tools like "cat"
  581. # fail in the same way (!).
  582. # Since there simply is no way to determine CPU times we
  583. # return 0.0 as a fallback. See:
  584. # https://github.com/giampaolo/psutil/issues/857
  585. return []
  586. else:
  587. raise
  588. hit_enoent = False
  589. for item in rawlist:
  590. addr, addrsize, perm, name, rss, anon, locked = item
  591. addr = toaddr(addr, addrsize)
  592. if not name.startswith('['):
  593. try:
  594. name = os.readlink(
  595. '%s/%s/path/%s' % (procfs_path, self.pid, name))
  596. except OSError as err:
  597. if err.errno == errno.ENOENT:
  598. # sometimes the link may not be resolved by
  599. # readlink() even if it exists (ls shows it).
  600. # If that's the case we just return the
  601. # unresolved link path.
  602. # This seems an incosistency with /proc similar
  603. # to: http://goo.gl/55XgO
  604. name = '%s/%s/path/%s' % (procfs_path, self.pid, name)
  605. hit_enoent = True
  606. else:
  607. raise
  608. retlist.append((addr, perm, name, rss, anon, locked))
  609. if hit_enoent:
  610. self._assert_alive()
  611. return retlist
  612. @wrap_exceptions
  613. def num_fds(self):
  614. return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
  615. @wrap_exceptions
  616. def num_ctx_switches(self):
  617. return _common.pctxsw(
  618. *cext.proc_num_ctx_switches(self.pid, self._procfs_path))
  619. @wrap_exceptions
  620. def wait(self, timeout=None):
  621. return _psposix.wait_pid(self.pid, timeout, self._name)