Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/psutil/_psosx.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
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 | |
5 """macOS platform implementation.""" | |
6 | |
7 import contextlib | |
8 import errno | |
9 import functools | |
10 import os | |
11 from collections import namedtuple | |
12 | |
13 from . import _common | |
14 from . import _psposix | |
15 from . import _psutil_osx as cext | |
16 from . import _psutil_posix as cext_posix | |
17 from ._common import AccessDenied | |
18 from ._common import conn_tmap | |
19 from ._common import conn_to_ntuple | |
20 from ._common import isfile_strict | |
21 from ._common import memoize_when_activated | |
22 from ._common import NoSuchProcess | |
23 from ._common import parse_environ_block | |
24 from ._common import usage_percent | |
25 from ._common import ZombieProcess | |
26 from ._compat import PermissionError | |
27 from ._compat import ProcessLookupError | |
28 | |
29 | |
30 __extra__all__ = [] | |
31 | |
32 | |
33 # ===================================================================== | |
34 # --- globals | |
35 # ===================================================================== | |
36 | |
37 | |
38 PAGESIZE = os.sysconf("SC_PAGE_SIZE") | |
39 AF_LINK = cext_posix.AF_LINK | |
40 | |
41 TCP_STATUSES = { | |
42 cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, | |
43 cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, | |
44 cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV, | |
45 cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, | |
46 cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, | |
47 cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, | |
48 cext.TCPS_CLOSED: _common.CONN_CLOSE, | |
49 cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, | |
50 cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, | |
51 cext.TCPS_LISTEN: _common.CONN_LISTEN, | |
52 cext.TCPS_CLOSING: _common.CONN_CLOSING, | |
53 cext.PSUTIL_CONN_NONE: _common.CONN_NONE, | |
54 } | |
55 | |
56 PROC_STATUSES = { | |
57 cext.SIDL: _common.STATUS_IDLE, | |
58 cext.SRUN: _common.STATUS_RUNNING, | |
59 cext.SSLEEP: _common.STATUS_SLEEPING, | |
60 cext.SSTOP: _common.STATUS_STOPPED, | |
61 cext.SZOMB: _common.STATUS_ZOMBIE, | |
62 } | |
63 | |
64 kinfo_proc_map = dict( | |
65 ppid=0, | |
66 ruid=1, | |
67 euid=2, | |
68 suid=3, | |
69 rgid=4, | |
70 egid=5, | |
71 sgid=6, | |
72 ttynr=7, | |
73 ctime=8, | |
74 status=9, | |
75 name=10, | |
76 ) | |
77 | |
78 pidtaskinfo_map = dict( | |
79 cpuutime=0, | |
80 cpustime=1, | |
81 rss=2, | |
82 vms=3, | |
83 pfaults=4, | |
84 pageins=5, | |
85 numthreads=6, | |
86 volctxsw=7, | |
87 ) | |
88 | |
89 | |
90 # ===================================================================== | |
91 # --- named tuples | |
92 # ===================================================================== | |
93 | |
94 | |
95 # psutil.cpu_times() | |
96 scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle']) | |
97 # psutil.virtual_memory() | |
98 svmem = namedtuple( | |
99 'svmem', ['total', 'available', 'percent', 'used', 'free', | |
100 'active', 'inactive', 'wired']) | |
101 # psutil.Process.memory_info() | |
102 pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) | |
103 # psutil.Process.memory_full_info() | |
104 pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) | |
105 | |
106 | |
107 # ===================================================================== | |
108 # --- memory | |
109 # ===================================================================== | |
110 | |
111 | |
112 def virtual_memory(): | |
113 """System virtual memory as a namedtuple.""" | |
114 total, active, inactive, wired, free, speculative = cext.virtual_mem() | |
115 # This is how Zabbix calculate avail and used mem: | |
116 # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/ | |
117 # osx/memory.c | |
118 # Also see: https://github.com/giampaolo/psutil/issues/1277 | |
119 avail = inactive + free | |
120 used = active + wired | |
121 # This is NOT how Zabbix calculates free mem but it matches "free" | |
122 # cmdline utility. | |
123 free -= speculative | |
124 percent = usage_percent((total - avail), total, round_=1) | |
125 return svmem(total, avail, percent, used, free, | |
126 active, inactive, wired) | |
127 | |
128 | |
129 def swap_memory(): | |
130 """Swap system memory as a (total, used, free, sin, sout) tuple.""" | |
131 total, used, free, sin, sout = cext.swap_mem() | |
132 percent = usage_percent(used, total, round_=1) | |
133 return _common.sswap(total, used, free, percent, sin, sout) | |
134 | |
135 | |
136 # ===================================================================== | |
137 # --- CPU | |
138 # ===================================================================== | |
139 | |
140 | |
141 def cpu_times(): | |
142 """Return system CPU times as a namedtuple.""" | |
143 user, nice, system, idle = cext.cpu_times() | |
144 return scputimes(user, nice, system, idle) | |
145 | |
146 | |
147 def per_cpu_times(): | |
148 """Return system CPU times as a named tuple""" | |
149 ret = [] | |
150 for cpu_t in cext.per_cpu_times(): | |
151 user, nice, system, idle = cpu_t | |
152 item = scputimes(user, nice, system, idle) | |
153 ret.append(item) | |
154 return ret | |
155 | |
156 | |
157 def cpu_count_logical(): | |
158 """Return the number of logical CPUs in the system.""" | |
159 return cext.cpu_count_logical() | |
160 | |
161 | |
162 def cpu_count_physical(): | |
163 """Return the number of physical CPUs in the system.""" | |
164 return cext.cpu_count_phys() | |
165 | |
166 | |
167 def cpu_stats(): | |
168 ctx_switches, interrupts, soft_interrupts, syscalls, traps = \ | |
169 cext.cpu_stats() | |
170 return _common.scpustats( | |
171 ctx_switches, interrupts, soft_interrupts, syscalls) | |
172 | |
173 | |
174 def cpu_freq(): | |
175 """Return CPU frequency. | |
176 On macOS per-cpu frequency is not supported. | |
177 Also, the returned frequency never changes, see: | |
178 https://arstechnica.com/civis/viewtopic.php?f=19&t=465002 | |
179 """ | |
180 curr, min_, max_ = cext.cpu_freq() | |
181 return [_common.scpufreq(curr, min_, max_)] | |
182 | |
183 | |
184 # ===================================================================== | |
185 # --- disks | |
186 # ===================================================================== | |
187 | |
188 | |
189 disk_usage = _psposix.disk_usage | |
190 disk_io_counters = cext.disk_io_counters | |
191 | |
192 | |
193 def disk_partitions(all=False): | |
194 """Return mounted disk partitions as a list of namedtuples.""" | |
195 retlist = [] | |
196 partitions = cext.disk_partitions() | |
197 for partition in partitions: | |
198 device, mountpoint, fstype, opts = partition | |
199 if device == 'none': | |
200 device = '' | |
201 if not all: | |
202 if not os.path.isabs(device) or not os.path.exists(device): | |
203 continue | |
204 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) | |
205 retlist.append(ntuple) | |
206 return retlist | |
207 | |
208 | |
209 # ===================================================================== | |
210 # --- sensors | |
211 # ===================================================================== | |
212 | |
213 | |
214 def sensors_battery(): | |
215 """Return battery information.""" | |
216 try: | |
217 percent, minsleft, power_plugged = cext.sensors_battery() | |
218 except NotImplementedError: | |
219 # no power source - return None according to interface | |
220 return None | |
221 power_plugged = power_plugged == 1 | |
222 if power_plugged: | |
223 secsleft = _common.POWER_TIME_UNLIMITED | |
224 elif minsleft == -1: | |
225 secsleft = _common.POWER_TIME_UNKNOWN | |
226 else: | |
227 secsleft = minsleft * 60 | |
228 return _common.sbattery(percent, secsleft, power_plugged) | |
229 | |
230 | |
231 # ===================================================================== | |
232 # --- network | |
233 # ===================================================================== | |
234 | |
235 | |
236 net_io_counters = cext.net_io_counters | |
237 net_if_addrs = cext_posix.net_if_addrs | |
238 | |
239 | |
240 def net_connections(kind='inet'): | |
241 """System-wide network connections.""" | |
242 # Note: on macOS this will fail with AccessDenied unless | |
243 # the process is owned by root. | |
244 ret = [] | |
245 for pid in pids(): | |
246 try: | |
247 cons = Process(pid).connections(kind) | |
248 except NoSuchProcess: | |
249 continue | |
250 else: | |
251 if cons: | |
252 for c in cons: | |
253 c = list(c) + [pid] | |
254 ret.append(_common.sconn(*c)) | |
255 return ret | |
256 | |
257 | |
258 def net_if_stats(): | |
259 """Get NIC stats (isup, duplex, speed, mtu).""" | |
260 names = net_io_counters().keys() | |
261 ret = {} | |
262 for name in names: | |
263 try: | |
264 mtu = cext_posix.net_if_mtu(name) | |
265 isup = cext_posix.net_if_flags(name) | |
266 duplex, speed = cext_posix.net_if_duplex_speed(name) | |
267 except OSError as err: | |
268 # https://github.com/giampaolo/psutil/issues/1279 | |
269 if err.errno != errno.ENODEV: | |
270 raise | |
271 else: | |
272 if hasattr(_common, 'NicDuplex'): | |
273 duplex = _common.NicDuplex(duplex) | |
274 ret[name] = _common.snicstats(isup, duplex, speed, mtu) | |
275 return ret | |
276 | |
277 | |
278 # ===================================================================== | |
279 # --- other system functions | |
280 # ===================================================================== | |
281 | |
282 | |
283 def boot_time(): | |
284 """The system boot time expressed in seconds since the epoch.""" | |
285 return cext.boot_time() | |
286 | |
287 | |
288 def users(): | |
289 """Return currently connected users as a list of namedtuples.""" | |
290 retlist = [] | |
291 rawlist = cext.users() | |
292 for item in rawlist: | |
293 user, tty, hostname, tstamp, pid = item | |
294 if tty == '~': | |
295 continue # reboot or shutdown | |
296 if not tstamp: | |
297 continue | |
298 nt = _common.suser(user, tty or None, hostname or None, tstamp, pid) | |
299 retlist.append(nt) | |
300 return retlist | |
301 | |
302 | |
303 # ===================================================================== | |
304 # --- processes | |
305 # ===================================================================== | |
306 | |
307 | |
308 def pids(): | |
309 ls = cext.pids() | |
310 if 0 not in ls: | |
311 # On certain macOS versions pids() C doesn't return PID 0 but | |
312 # "ps" does and the process is querable via sysctl(): | |
313 # https://travis-ci.org/giampaolo/psutil/jobs/309619941 | |
314 try: | |
315 Process(0).create_time() | |
316 ls.insert(0, 0) | |
317 except NoSuchProcess: | |
318 pass | |
319 except AccessDenied: | |
320 ls.insert(0, 0) | |
321 return ls | |
322 | |
323 | |
324 pid_exists = _psposix.pid_exists | |
325 | |
326 | |
327 def is_zombie(pid): | |
328 try: | |
329 st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']] | |
330 return st == cext.SZOMB | |
331 except Exception: | |
332 return False | |
333 | |
334 | |
335 def wrap_exceptions(fun): | |
336 """Decorator which translates bare OSError exceptions into | |
337 NoSuchProcess and AccessDenied. | |
338 """ | |
339 @functools.wraps(fun) | |
340 def wrapper(self, *args, **kwargs): | |
341 try: | |
342 return fun(self, *args, **kwargs) | |
343 except ProcessLookupError: | |
344 if is_zombie(self.pid): | |
345 raise ZombieProcess(self.pid, self._name, self._ppid) | |
346 else: | |
347 raise NoSuchProcess(self.pid, self._name) | |
348 except PermissionError: | |
349 raise AccessDenied(self.pid, self._name) | |
350 except cext.ZombieProcessError: | |
351 raise ZombieProcess(self.pid, self._name, self._ppid) | |
352 return wrapper | |
353 | |
354 | |
355 @contextlib.contextmanager | |
356 def catch_zombie(proc): | |
357 """There are some poor C APIs which incorrectly raise ESRCH when | |
358 the process is still alive or it's a zombie, or even RuntimeError | |
359 (those who don't set errno). This is here in order to solve: | |
360 https://github.com/giampaolo/psutil/issues/1044 | |
361 """ | |
362 try: | |
363 yield | |
364 except (OSError, RuntimeError) as err: | |
365 if isinstance(err, RuntimeError) or err.errno == errno.ESRCH: | |
366 try: | |
367 # status() is not supposed to lie and correctly detect | |
368 # zombies so if it raises ESRCH it's true. | |
369 status = proc.status() | |
370 except NoSuchProcess: | |
371 raise err | |
372 else: | |
373 if status == _common.STATUS_ZOMBIE: | |
374 raise ZombieProcess(proc.pid, proc._name, proc._ppid) | |
375 else: | |
376 raise AccessDenied(proc.pid, proc._name) | |
377 else: | |
378 raise | |
379 | |
380 | |
381 class Process(object): | |
382 """Wrapper class around underlying C implementation.""" | |
383 | |
384 __slots__ = ["pid", "_name", "_ppid", "_cache"] | |
385 | |
386 def __init__(self, pid): | |
387 self.pid = pid | |
388 self._name = None | |
389 self._ppid = None | |
390 | |
391 @wrap_exceptions | |
392 @memoize_when_activated | |
393 def _get_kinfo_proc(self): | |
394 # Note: should work with all PIDs without permission issues. | |
395 ret = cext.proc_kinfo_oneshot(self.pid) | |
396 assert len(ret) == len(kinfo_proc_map) | |
397 return ret | |
398 | |
399 @wrap_exceptions | |
400 @memoize_when_activated | |
401 def _get_pidtaskinfo(self): | |
402 # Note: should work for PIDs owned by user only. | |
403 with catch_zombie(self): | |
404 ret = cext.proc_pidtaskinfo_oneshot(self.pid) | |
405 assert len(ret) == len(pidtaskinfo_map) | |
406 return ret | |
407 | |
408 def oneshot_enter(self): | |
409 self._get_kinfo_proc.cache_activate(self) | |
410 self._get_pidtaskinfo.cache_activate(self) | |
411 | |
412 def oneshot_exit(self): | |
413 self._get_kinfo_proc.cache_deactivate(self) | |
414 self._get_pidtaskinfo.cache_deactivate(self) | |
415 | |
416 @wrap_exceptions | |
417 def name(self): | |
418 name = self._get_kinfo_proc()[kinfo_proc_map['name']] | |
419 return name if name is not None else cext.proc_name(self.pid) | |
420 | |
421 @wrap_exceptions | |
422 def exe(self): | |
423 with catch_zombie(self): | |
424 return cext.proc_exe(self.pid) | |
425 | |
426 @wrap_exceptions | |
427 def cmdline(self): | |
428 with catch_zombie(self): | |
429 return cext.proc_cmdline(self.pid) | |
430 | |
431 @wrap_exceptions | |
432 def environ(self): | |
433 with catch_zombie(self): | |
434 return parse_environ_block(cext.proc_environ(self.pid)) | |
435 | |
436 @wrap_exceptions | |
437 def ppid(self): | |
438 self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']] | |
439 return self._ppid | |
440 | |
441 @wrap_exceptions | |
442 def cwd(self): | |
443 with catch_zombie(self): | |
444 return cext.proc_cwd(self.pid) | |
445 | |
446 @wrap_exceptions | |
447 def uids(self): | |
448 rawtuple = self._get_kinfo_proc() | |
449 return _common.puids( | |
450 rawtuple[kinfo_proc_map['ruid']], | |
451 rawtuple[kinfo_proc_map['euid']], | |
452 rawtuple[kinfo_proc_map['suid']]) | |
453 | |
454 @wrap_exceptions | |
455 def gids(self): | |
456 rawtuple = self._get_kinfo_proc() | |
457 return _common.puids( | |
458 rawtuple[kinfo_proc_map['rgid']], | |
459 rawtuple[kinfo_proc_map['egid']], | |
460 rawtuple[kinfo_proc_map['sgid']]) | |
461 | |
462 @wrap_exceptions | |
463 def terminal(self): | |
464 tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']] | |
465 tmap = _psposix.get_terminal_map() | |
466 try: | |
467 return tmap[tty_nr] | |
468 except KeyError: | |
469 return None | |
470 | |
471 @wrap_exceptions | |
472 def memory_info(self): | |
473 rawtuple = self._get_pidtaskinfo() | |
474 return pmem( | |
475 rawtuple[pidtaskinfo_map['rss']], | |
476 rawtuple[pidtaskinfo_map['vms']], | |
477 rawtuple[pidtaskinfo_map['pfaults']], | |
478 rawtuple[pidtaskinfo_map['pageins']], | |
479 ) | |
480 | |
481 @wrap_exceptions | |
482 def memory_full_info(self): | |
483 basic_mem = self.memory_info() | |
484 uss = cext.proc_memory_uss(self.pid) | |
485 return pfullmem(*basic_mem + (uss, )) | |
486 | |
487 @wrap_exceptions | |
488 def cpu_times(self): | |
489 rawtuple = self._get_pidtaskinfo() | |
490 return _common.pcputimes( | |
491 rawtuple[pidtaskinfo_map['cpuutime']], | |
492 rawtuple[pidtaskinfo_map['cpustime']], | |
493 # children user / system times are not retrievable (set to 0) | |
494 0.0, 0.0) | |
495 | |
496 @wrap_exceptions | |
497 def create_time(self): | |
498 return self._get_kinfo_proc()[kinfo_proc_map['ctime']] | |
499 | |
500 @wrap_exceptions | |
501 def num_ctx_switches(self): | |
502 # Unvoluntary value seems not to be available; | |
503 # getrusage() numbers seems to confirm this theory. | |
504 # We set it to 0. | |
505 vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']] | |
506 return _common.pctxsw(vol, 0) | |
507 | |
508 @wrap_exceptions | |
509 def num_threads(self): | |
510 return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']] | |
511 | |
512 @wrap_exceptions | |
513 def open_files(self): | |
514 if self.pid == 0: | |
515 return [] | |
516 files = [] | |
517 with catch_zombie(self): | |
518 rawlist = cext.proc_open_files(self.pid) | |
519 for path, fd in rawlist: | |
520 if isfile_strict(path): | |
521 ntuple = _common.popenfile(path, fd) | |
522 files.append(ntuple) | |
523 return files | |
524 | |
525 @wrap_exceptions | |
526 def connections(self, kind='inet'): | |
527 if kind not in conn_tmap: | |
528 raise ValueError("invalid %r kind argument; choose between %s" | |
529 % (kind, ', '.join([repr(x) for x in conn_tmap]))) | |
530 families, types = conn_tmap[kind] | |
531 with catch_zombie(self): | |
532 rawlist = cext.proc_connections(self.pid, families, types) | |
533 ret = [] | |
534 for item in rawlist: | |
535 fd, fam, type, laddr, raddr, status = item | |
536 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, | |
537 TCP_STATUSES) | |
538 ret.append(nt) | |
539 return ret | |
540 | |
541 @wrap_exceptions | |
542 def num_fds(self): | |
543 if self.pid == 0: | |
544 return 0 | |
545 with catch_zombie(self): | |
546 return cext.proc_num_fds(self.pid) | |
547 | |
548 @wrap_exceptions | |
549 def wait(self, timeout=None): | |
550 return _psposix.wait_pid(self.pid, timeout, self._name) | |
551 | |
552 @wrap_exceptions | |
553 def nice_get(self): | |
554 with catch_zombie(self): | |
555 return cext_posix.getpriority(self.pid) | |
556 | |
557 @wrap_exceptions | |
558 def nice_set(self, value): | |
559 with catch_zombie(self): | |
560 return cext_posix.setpriority(self.pid, value) | |
561 | |
562 @wrap_exceptions | |
563 def status(self): | |
564 code = self._get_kinfo_proc()[kinfo_proc_map['status']] | |
565 # XXX is '?' legit? (we're not supposed to return it anyway) | |
566 return PROC_STATUSES.get(code, '?') | |
567 | |
568 @wrap_exceptions | |
569 def threads(self): | |
570 rawlist = cext.proc_threads(self.pid) | |
571 retlist = [] | |
572 for thread_id, utime, stime in rawlist: | |
573 ntuple = _common.pthread(thread_id, utime, stime) | |
574 retlist.append(ntuple) | |
575 return retlist |