aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-03-05 14:54:25 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-03-05 15:05:44 +0300
commitf6c02fde7bd26b2e3f57a32972f9aa1e47144be8 (patch)
tree0c89e1977d3d3c394b11dfea2932e40c8445837a /contrib/python
parent274118e9de20923d783e4ec247dfbc8a63a86c8e (diff)
downloadydb-f6c02fde7bd26b2e3f57a32972f9aa1e47144be8.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/hypothesis/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/core.py13
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/observability.py3
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/stateful.py55
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/version.py2
-rw-r--r--contrib/python/hypothesis/py3/ya.make2
-rw-r--r--contrib/python/psutil/py3/.dist-info/METADATA102
-rw-r--r--contrib/python/psutil/py3/LICENSE2
-rw-r--r--contrib/python/psutil/py3/README.rst121
-rw-r--r--contrib/python/psutil/py3/psutil/__init__.py523
-rw-r--r--contrib/python/psutil/py3/psutil/_common.py345
-rw-r--r--contrib/python/psutil/py3/psutil/_compat.py119
-rw-r--r--contrib/python/psutil/py3/psutil/_psaix.py128
-rw-r--r--contrib/python/psutil/py3/psutil/_psbsd.py322
-rw-r--r--contrib/python/psutil/py3/psutil/_pslinux.py872
-rw-r--r--contrib/python/psutil/py3/psutil/_psosx.py126
-rw-r--r--contrib/python/psutil/py3/psutil/_psposix.py56
-rw-r--r--contrib/python/psutil/py3/psutil/_pssunos.py135
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_common.c131
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_common.h40
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_linux.c506
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_osx.c1850
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_posix.c231
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_windows.c1699
-rw-r--r--contrib/python/psutil/py3/psutil/_pswindows.py197
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/disk.c75
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/disk.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/mem.c30
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/mem.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/net.c120
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/net.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/proc.c201
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/proc.h25
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/users.c71
-rw-r--r--contrib/python/psutil/py3/psutil/arch/linux/users.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/cpu.c326
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/cpu.h14
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/disk.c377
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/disk.h11
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/mem.c113
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/mem.h10
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/net.c101
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/net.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/proc.c1267
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/proc.h21
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/process_info.c384
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/process_info.h17
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/sensors.c102
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/sensors.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/sys.c88
-rw-r--r--contrib/python/psutil/py3/psutil/arch/osx/sys.h10
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/cpu.c15
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/cpu.h2
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/disk.c34
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/disk.h2
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/mem.c94
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/mem.h11
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h2
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc.c1251
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc.h34
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_handles.c (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_handles.c)2
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_handles.h (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_handles.h)0
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_info.c (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_info.c)94
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_info.h (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_info.h)9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_utils.c (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_utils.c)4
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/proc_utils.h (renamed from contrib/python/psutil/py3/psutil/arch/windows/process_utils.h)0
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/sensors.c32
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/sensors.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/services.c1
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/socks.c2
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/sys.c178
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/sys.h10
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/wmi.c29
-rw-r--r--contrib/python/psutil/py3/ya.make27
75 files changed, 7146 insertions, 5699 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA
index 26ae8b8481..d883f6dec6 100644
--- a/contrib/python/hypothesis/py3/.dist-info/METADATA
+++ b/contrib/python/hypothesis/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: hypothesis
-Version: 6.98.8
+Version: 6.98.9
Summary: A library for property-based testing
Home-page: https://hypothesis.works
Author: David R. MacIver and Zac Hatfield-Dodds
diff --git a/contrib/python/hypothesis/py3/hypothesis/core.py b/contrib/python/hypothesis/py3/hypothesis/core.py
index e000da174f..536d7f0156 100644
--- a/contrib/python/hypothesis/py3/hypothesis/core.py
+++ b/contrib/python/hypothesis/py3/hypothesis/core.py
@@ -834,8 +834,9 @@ class StateForActualGivenExecution:
in_drawtime = math.fsum(data.draw_times.values()) - arg_drawtime
runtime = datetime.timedelta(seconds=finish - start - in_drawtime)
self._timing_features = {
- "execute_test": finish - start - in_drawtime,
+ "execute:test": finish - start - in_drawtime,
**data.draw_times,
+ **data._stateful_run_times,
}
if (current_deadline := self.settings.deadline) is not None:
@@ -927,6 +928,9 @@ class StateForActualGivenExecution:
msg, format_arg = data._sampled_from_all_strategies_elements_message
add_note(e, msg.format(format_arg))
raise
+ finally:
+ if parts := getattr(data, "_stateful_repr_parts", None):
+ self._string_repr = "\n".join(parts)
# self.test_runner can include the execute_example method, or setup/teardown
# _example, so it's important to get the PRNG and build context in place first.
@@ -942,7 +946,11 @@ class StateForActualGivenExecution:
if expected_failure is not None:
exception, traceback = expected_failure
if isinstance(exception, DeadlineExceeded) and (
- runtime_secs := self._timing_features.get("execute_test")
+ runtime_secs := math.fsum(
+ v
+ for k, v in self._timing_features.items()
+ if k.startswith("execute:")
+ )
):
report(
"Unreliable test timings! On an initial run, this "
@@ -1068,6 +1076,7 @@ class StateForActualGivenExecution:
arguments={**self._jsonable_arguments, **data._observability_args},
timing=self._timing_features,
coverage=tractable_coverage_report(trace) or None,
+ phase=phase,
)
deliver_json_blob(tc)
self._timing_features = {}
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
index cea40823be..0016f2fa1a 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
@@ -18,6 +18,7 @@ from typing import (
TYPE_CHECKING,
Any,
Callable,
+ DefaultDict,
Dict,
FrozenSet,
Iterable,
@@ -1468,6 +1469,7 @@ class ConjectureData:
self.forced_indices: "Set[int]" = set()
self.interesting_origin: Optional[InterestingOrigin] = None
self.draw_times: "Dict[str, float]" = {}
+ self._stateful_run_times: "DefaultDict[str, float]" = defaultdict(float)
self.max_depth = 0
self.has_discards = False
self.provider = PrimitiveProvider(self)
@@ -1752,7 +1754,7 @@ class ConjectureData:
try:
return strategy.do_draw(self)
finally:
- key = observe_as or f"unlabeled_{len(self.draw_times)}"
+ key = observe_as or f"generate:unlabeled_{len(self.draw_times)}"
self.draw_times[key] = time.perf_counter() - start_time
finally:
self.stop_example()
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
index 98753985f1..aff19d805c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
@@ -41,9 +41,12 @@ def make_testcase(
arguments: Optional[dict] = None,
timing: Dict[str, float],
coverage: Optional[Dict[str, List[int]]] = None,
+ phase: Optional[str] = None,
) -> dict:
if data.interesting_origin:
status_reason = str(data.interesting_origin)
+ elif phase == "shrink" and data.status == Status.OVERRUN:
+ status_reason = "exceeded size of current best example"
else:
status_reason = str(data.events.pop("invalid because", ""))
diff --git a/contrib/python/hypothesis/py3/hypothesis/stateful.py b/contrib/python/hypothesis/py3/hypothesis/stateful.py
index 2ae5815161..bf7271fc4c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/stateful.py
+++ b/contrib/python/hypothesis/py3/hypothesis/stateful.py
@@ -20,6 +20,7 @@ import inspect
from copy import copy
from functools import lru_cache
from io import StringIO
+from time import perf_counter
from typing import (
Any,
Callable,
@@ -48,6 +49,7 @@ from hypothesis.core import TestFunc, given
from hypothesis.errors import InvalidArgument, InvalidDefinition
from hypothesis.internal.conjecture import utils as cu
from hypothesis.internal.healthcheck import fail_health_check
+from hypothesis.internal.observability import TESTCASE_CALLBACKS
from hypothesis.internal.reflection import (
function_digest,
get_pretty_function_description,
@@ -121,10 +123,17 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
print_steps = (
current_build_context().is_final or current_verbosity() >= Verbosity.debug
)
- try:
+ cd._stateful_repr_parts = []
+
+ def output(s):
if print_steps:
- report(f"state = {machine.__class__.__name__}()")
- machine.check_invariants(settings)
+ report(s)
+ if TESTCASE_CALLBACKS:
+ cd._stateful_repr_parts.append(s)
+
+ try:
+ output(f"state = {machine.__class__.__name__}()")
+ machine.check_invariants(settings, output, cd._stateful_run_times)
max_steps = settings.stateful_step_count
steps_run = 0
@@ -141,6 +150,8 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
must_stop = True
elif steps_run <= _min_steps:
must_stop = False
+
+ start_draw = perf_counter()
if cd.draw_boolean(p=2**-16, forced=must_stop):
break
steps_run += 1
@@ -156,12 +167,15 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
machine._initialize_rules_to_run.remove(rule)
else:
rule, data = cd.draw(machine._rules_strategy)
+ draw_label = f"generate:rule:{rule.function.__name__}"
+ cd.draw_times.setdefault(draw_label, 0.0)
+ cd.draw_times[draw_label] += perf_counter() - start_draw
# Pretty-print the values this rule was called with *before* calling
# _add_result_to_targets, to avoid printing arguments which are also
# a return value using the variable name they are assigned to.
# See https://github.com/HypothesisWorks/hypothesis/issues/2341
- if print_steps:
+ if print_steps or TESTCASE_CALLBACKS:
data_to_print = {
k: machine._pretty_print(v) for k, v in data.items()
}
@@ -173,7 +187,12 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
for k, v in list(data.items()):
if isinstance(v, VarReference):
data[k] = machine.names_to_values[v.name]
+
+ label = f"execute:rule:{rule.function.__name__}"
+ start = perf_counter()
result = rule.function(machine, **data)
+ cd._stateful_run_times[label] += perf_counter() - start
+
if rule.targets:
if isinstance(result, MultipleResults):
for single_result in result.values:
@@ -190,16 +209,15 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
HealthCheck.return_value,
)
finally:
- if print_steps:
+ if print_steps or TESTCASE_CALLBACKS:
# 'result' is only used if the step has target bundles.
# If it does, and the result is a 'MultipleResult',
# then 'print_step' prints a multi-variable assignment.
- machine._print_step(rule, data_to_print, result)
- machine.check_invariants(settings)
+ output(machine._repr_step(rule, data_to_print, result))
+ machine.check_invariants(settings, output, cd._stateful_run_times)
cd.stop_example()
finally:
- if print_steps:
- report("state.teardown()")
+ output("state.teardown()")
machine.teardown()
# Use a machine digest to identify stateful tests in the example database
@@ -338,7 +356,7 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta):
cls._invariants_per_class[cls] = target
return cls._invariants_per_class[cls]
- def _print_step(self, rule, data, result):
+ def _repr_step(self, rule, data, result):
self.step_count = getattr(self, "step_count", 0) + 1
output_assignment = ""
if rule.targets:
@@ -350,13 +368,8 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta):
output_assignment = ", ".join(output_names) + " = "
else:
output_assignment = self._last_names(1)[0] + " = "
- report(
- "{}state.{}({})".format(
- output_assignment,
- rule.function.__name__,
- ", ".join("%s=%s" % kv for kv in data.items()),
- )
- )
+ args = ", ".join("%s=%s" % kv for kv in data.items())
+ return f"{output_assignment}state.{rule.function.__name__}({args})"
def _add_result_to_targets(self, targets, result):
name = self._new_name()
@@ -367,18 +380,22 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta):
for target in targets:
self.bundles.setdefault(target, []).append(VarReference(name))
- def check_invariants(self, settings):
+ def check_invariants(self, settings, output, runtimes):
for invar in self.invariants():
if self._initialize_rules_to_run and not invar.check_during_init:
continue
if not all(precond(self) for precond in invar.preconditions):
continue
+ name = invar.function.__name__
if (
current_build_context().is_final
or settings.verbosity >= Verbosity.debug
+ or TESTCASE_CALLBACKS
):
- report(f"state.{invar.function.__name__}()")
+ output(f"state.{name}()")
+ start = perf_counter()
result = invar.function(self)
+ runtimes[f"execute:invariant:{name}"] += perf_counter() - start
if result is not None:
fail_health_check(
settings,
diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py
index 6aafd5421c..f54f8dc42f 100644
--- a/contrib/python/hypothesis/py3/hypothesis/version.py
+++ b/contrib/python/hypothesis/py3/hypothesis/version.py
@@ -8,5 +8,5 @@
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
-__version_info__ = (6, 98, 8)
+__version_info__ = (6, 98, 9)
__version__ = ".".join(map(str, __version_info__))
diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make
index 1678e159c9..89269430da 100644
--- a/contrib/python/hypothesis/py3/ya.make
+++ b/contrib/python/hypothesis/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(6.98.8)
+VERSION(6.98.9)
LICENSE(MPL-2.0)
diff --git a/contrib/python/psutil/py3/.dist-info/METADATA b/contrib/python/psutil/py3/.dist-info/METADATA
index e0d5c4d09b..1815b243bc 100644
--- a/contrib/python/psutil/py3/.dist-info/METADATA
+++ b/contrib/python/psutil/py3/.dist-info/METADATA
@@ -1,11 +1,11 @@
Metadata-Version: 2.1
Name: psutil
-Version: 5.8.0
+Version: 5.9.8
Summary: Cross-platform lib for process and system monitoring in Python.
Home-page: https://github.com/giampaolo/psutil
Author: Giampaolo Rodola
Author-email: g.rodola@gmail.com
-License: BSD
+License: BSD-3-Clause
Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit,smem,performance,metrics,agent,observability
Platform: Platform Independent
Classifier: Development Status :: 5 - Production/Stable
@@ -35,7 +35,6 @@ Classifier: Operating System :: POSIX :: SunOS/Solaris
Classifier: Operating System :: POSIX
Classifier: Programming Language :: C
Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -53,19 +52,19 @@ Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Operating System
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
-Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*
Description-Content-Type: text/x-rst
+License-File: LICENSE
Provides-Extra: test
Requires-Dist: ipaddress ; (python_version < "3.0") and extra == 'test'
Requires-Dist: mock ; (python_version < "3.0") and extra == 'test'
-Requires-Dist: unittest2 ; (python_version < "3.0") and extra == 'test'
Requires-Dist: enum34 ; (python_version <= "3.4") and extra == 'test'
Requires-Dist: pywin32 ; (sys_platform == "win32") and extra == 'test'
Requires-Dist: wmi ; (sys_platform == "win32") and extra == 'test'
-| |downloads| |stars| |forks| |contributors| |coverage| |quality|
+| |downloads| |stars| |forks| |contributors| |coverage|
| |version| |py-versions| |packages| |license|
-| |github-actions| |appveyor| |doc| |twitter| |tidelift|
+| |github-actions-wheels| |github-actions-bsd| |appveyor| |doc| |twitter| |tidelift|
.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
:target: https://pepy.tech/project/psutil
@@ -83,24 +82,24 @@ Requires-Dist: wmi ; (sys_platform == "win32") and extra == 'test'
:target: https://github.com/giampaolo/psutil/graphs/contributors
:alt: Contributors
-.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg
- :target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=giampaolo/psutil&amp;utm_campaign=Badge_Grade
- :alt: Code quality
+.. |github-actions-wheels| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/build.yml?label=Linux%2C%20macOS%2C%20Windows
+ :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Abuild
+ :alt: Linux, macOS, Windows
-.. |github-actions| image:: https://img.shields.io/github/workflow/status/giampaolo/psutil/CI?label=Linux%2C%20macOS%2C%20FreeBSD
- :target: https://github.com/giampaolo/psutil/actions?query=workflow%3ACI
- :alt: Linux, macOS, Windows tests
+.. |github-actions-bsd| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/bsd.yml?label=FreeBSD,%20NetBSD,%20OpenBSD
+ :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests
+ :alt: FreeBSD, NetBSD, OpenBSD
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
+.. |appveyor| image:: https://img.shields.io/appveyor/build/giampaolo/psutil/master.svg?maxAge=3600&label=Windows%20(py2)
:target: https://ci.appveyor.com/project/giampaolo/psutil
- :alt: Windows tests (Appveyor)
+ :alt: Windows (Appveyor)
.. |coverage| image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
:alt: Test coverage (coverall.io)
.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest
- :target: http://psutil.readthedocs.io/en/latest/?badge=latest
+ :target: https://psutil.readthedocs.io/en/latest/
:alt: Documentation Status
.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
@@ -108,7 +107,6 @@ Requires-Dist: wmi ; (sys_platform == "win32") and extra == 'test'
:alt: Latest version
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg
- :target: https://pypi.org/project/psutil
:alt: Supported Python versions
.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg
@@ -161,7 +159,7 @@ psutil currently supports the following platforms:
- **Sun Solaris**
- **AIX**
-Supported Python versions are **2.6**, **2.7**, **3.4+** and
+Supported Python versions are **2.7**, **3.6+** and
`PyPy <http://pypy.org/>`__.
Funding
@@ -172,7 +170,7 @@ immensely from some funding.
Keeping up with bug reports and maintenance has become hardly sustainable for
me alone in terms of time.
If you're a company that's making significant use of psutil you can consider
-becoming a sponsor via `GitHub <https://github.com/sponsors/giampaolo>`__,
+becoming a sponsor via `GitHub Sponsors <https://github.com/sponsors/giampaolo>`__,
`Open Collective <https://opencollective.com/psutil>`__ or
`PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8>`__
and have your logo displayed in here and psutil `doc <https://psutil.readthedocs.io>`__.
@@ -199,7 +197,7 @@ CPU
>>> import psutil
>>>
>>> psutil.cpu_times()
- scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
+ scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, guest_nice=0.0)
>>>
>>> for x in range(3):
... psutil.cpu_percent(interval=1)
@@ -254,7 +252,7 @@ Disks
>>> psutil.disk_partitions()
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid', maxfile=255, maxpath=4096),
- sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw', maxfile=255, maxpath=4096)]
+ sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext', opts='rw', maxfile=255, maxpath=4096)]
>>>
>>> psutil.disk_usage('/')
sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
@@ -286,8 +284,8 @@ Network
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
>>>
>>> psutil.net_if_stats()
- {'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
- 'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
+ {'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running'),
+ 'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast')}
>>>
Sensors
@@ -340,39 +338,38 @@ Process management
>>> p = psutil.Process(7055)
>>> p
psutil.Process(pid=7055, name='python3', status='running', started='09:04:44')
+ >>> p.pid
+ 7055
>>> p.name()
- 'python'
+ 'python3'
>>> p.exe()
- '/usr/bin/python'
+ '/usr/bin/python3'
>>> p.cwd()
'/home/giampaolo'
>>> p.cmdline()
- ['/usr/bin/python', 'main.py']
+ ['/usr/bin/python3', 'main.py']
>>>
- >>> p.pid
- 7055
>>> p.ppid()
7054
- >>> p.children(recursive=True)
- [psutil.Process(pid=29835, name='python3', status='sleeping', started='11:45:38'),
- psutil.Process(pid=29836, name='python3', status='waking', started='11:43:39')]
- >>>
>>> p.parent()
psutil.Process(pid=4699, name='bash', status='sleeping', started='09:06:44')
>>> p.parents()
[psutil.Process(pid=4699, name='bash', started='09:06:44'),
psutil.Process(pid=4689, name='gnome-terminal-server', status='sleeping', started='0:06:44'),
psutil.Process(pid=1, name='systemd', status='sleeping', started='05:56:55')]
+ >>> p.children(recursive=True)
+ [psutil.Process(pid=29835, name='python3', status='sleeping', started='11:45:38'),
+ psutil.Process(pid=29836, name='python3', status='waking', started='11:43:39')]
>>>
>>> p.status()
'running'
- >>> p.username()
- 'giampaolo'
>>> p.create_time()
1267551141.5019531
>>> p.terminal()
'/dev/pts/0'
>>>
+ >>> p.username()
+ 'giampaolo'
>>> p.uids()
puids(real=1000, effective=1000, saved=1000)
>>> p.gids()
@@ -412,14 +409,14 @@ Process management
[pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'),
pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING')]
>>>
- >>> p.num_threads()
- 4
- >>> p.num_fds()
- 8
>>> p.threads()
[pthread(id=5234, user_time=22.5, system_time=9.2891),
pthread(id=5237, user_time=0.0707, system_time=1.1)]
>>>
+ >>> p.num_threads()
+ 4
+ >>> p.num_fds()
+ 8
>>> p.num_ctx_switches()
pctxsw(voluntary=78, involuntary=19)
>>>
@@ -486,23 +483,6 @@ Further process APIs
>>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)
>>>
-Popen wrapper:
-
-.. code-block:: python
-
- >>> import psutil
- >>> from subprocess import PIPE
- >>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
- >>> p.name()
- 'python'
- >>> p.username()
- 'giampaolo'
- >>> p.communicate()
- ('hello\n', None)
- >>> p.wait(timeout=2)
- 0
- >>>
-
Windows services
----------------
@@ -533,6 +513,7 @@ Here's some I find particularly interesting:
- https://github.com/google/grr
- https://github.com/facebook/osquery/
- https://github.com/nicolargo/glances
+- https://github.com/aristocratos/bpytop
- https://github.com/Jahaja/psdash
- https://github.com/ajenti/ajenti
- https://github.com/home-assistant/home-assistant/
@@ -545,16 +526,5 @@ Portings
- Rust: https://github.com/rust-psutil/rust-psutil
- Nim: https://github.com/johnscillieri/psutil-nim
-Security
-========
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _`Giampaolo Rodola`: https://gmpy.dev/about
-.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
-.. _Tidelift security contact: https://tidelift.com/security
-.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
-
diff --git a/contrib/python/psutil/py3/LICENSE b/contrib/python/psutil/py3/LICENSE
index 0bf4a7fc04..cff5eb74e1 100644
--- a/contrib/python/psutil/py3/LICENSE
+++ b/contrib/python/psutil/py3/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola'
+Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
diff --git a/contrib/python/psutil/py3/README.rst b/contrib/python/psutil/py3/README.rst
index a9334da283..12bf64254e 100644
--- a/contrib/python/psutil/py3/README.rst
+++ b/contrib/python/psutil/py3/README.rst
@@ -1,6 +1,6 @@
-| |downloads| |stars| |forks| |contributors| |coverage| |quality|
+| |downloads| |stars| |forks| |contributors| |coverage|
| |version| |py-versions| |packages| |license|
-| |github-actions| |appveyor| |doc| |twitter| |tidelift|
+| |github-actions-wheels| |github-actions-bsd| |appveyor| |doc| |twitter| |tidelift|
.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
:target: https://pepy.tech/project/psutil
@@ -18,24 +18,24 @@
:target: https://github.com/giampaolo/psutil/graphs/contributors
:alt: Contributors
-.. |quality| image:: https://img.shields.io/codacy/grade/ce63e7f7f69d44b5b59682196e6fbfca.svg
- :target: https://www.codacy.com/app/g-rodola/psutil?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=giampaolo/psutil&amp;utm_campaign=Badge_Grade
- :alt: Code quality
+.. |github-actions-wheels| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/build.yml?label=Linux%2C%20macOS%2C%20Windows
+ :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Abuild
+ :alt: Linux, macOS, Windows
-.. |github-actions| image:: https://img.shields.io/github/workflow/status/giampaolo/psutil/CI?label=Linux%2C%20macOS%2C%20FreeBSD
- :target: https://github.com/giampaolo/psutil/actions?query=workflow%3ACI
- :alt: Linux, macOS, Windows tests
+.. |github-actions-bsd| image:: https://img.shields.io/github/actions/workflow/status/giampaolo/psutil/.github/workflows/bsd.yml?label=FreeBSD,%20NetBSD,%20OpenBSD
+ :target: https://github.com/giampaolo/psutil/actions?query=workflow%3Absd-tests
+ :alt: FreeBSD, NetBSD, OpenBSD
-.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
+.. |appveyor| image:: https://img.shields.io/appveyor/build/giampaolo/psutil/master.svg?maxAge=3600&label=Windows%20(py2)
:target: https://ci.appveyor.com/project/giampaolo/psutil
- :alt: Windows tests (Appveyor)
+ :alt: Windows (Appveyor)
.. |coverage| image:: https://coveralls.io/repos/github/giampaolo/psutil/badge.svg?branch=master
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
:alt: Test coverage (coverall.io)
.. |doc| image:: https://readthedocs.org/projects/psutil/badge/?version=latest
- :target: http://psutil.readthedocs.io/en/latest/?badge=latest
+ :target: https://psutil.readthedocs.io/en/latest/
:alt: Documentation Status
.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
@@ -43,7 +43,6 @@
:alt: Latest version
.. |py-versions| image:: https://img.shields.io/pypi/pyversions/psutil.svg
- :target: https://pypi.org/project/psutil
:alt: Supported Python versions
.. |packages| image:: https://repology.org/badge/tiny-repos/python:psutil.svg
@@ -99,7 +98,7 @@ psutil currently supports the following platforms:
- **Sun Solaris**
- **AIX**
-Supported Python versions are **2.6**, **2.7**, **3.4+** and
+Supported Python versions are **2.7**, **3.6+** and
`PyPy <http://pypy.org/>`__.
Funding
@@ -110,7 +109,7 @@ immensely from some funding.
Keeping up with bug reports and maintenance has become hardly sustainable for
me alone in terms of time.
If you're a company that's making significant use of psutil you can consider
-becoming a sponsor via `GitHub <https://github.com/sponsors/giampaolo>`__,
+becoming a sponsor via `GitHub Sponsors <https://github.com/sponsors/giampaolo>`__,
`Open Collective <https://opencollective.com/psutil>`__ or
`PayPal <https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8>`__
and have your logo displayed in here and psutil `doc <https://psutil.readthedocs.io>`__.
@@ -122,7 +121,11 @@ Sponsors
<div>
<a href="https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme">
- <img src="https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.png" />
+ <img width="185" src="https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.svg" />
+ </a>
+ &nbsp;&nbsp
+ <a href="https://sansec.io/">
+ <img src="https://sansec.io/assets/images/logo.svg" />
</a>
</div>
<sup><a href="https://github.com/sponsors/giampaolo">add your logo</a></sup>
@@ -130,16 +133,32 @@ Sponsors
Supporters
==========
-None yet.
-
.. raw:: html
+ <div>
+ <a href="https://github.com/dbwiddis"><img height="40" width="40" title="Daniel Widdis" src="https://avatars1.githubusercontent.com/u/9291703?s=88&amp;v=4" /></a>
+ <a href="https://github.com/aristocratos"><img height="40" width="40" title="aristocratos" src="https://avatars3.githubusercontent.com/u/59659483?s=96&amp;v=4" /></a>
+ <a href="https://github.com/cybersecgeek"><img height="40" width="40" title="cybersecgeek" src="https://avatars.githubusercontent.com/u/12847926?v=4" /></a>
+ <a href="https://github.com/scoutapm-sponsorships"><img height="40" width="40" title="scoutapm-sponsorships" src="https://avatars.githubusercontent.com/u/71095532?v=4" /></a>
+ <a href="https://opencollective.com/chenyoo-hao"><img height="40" width="40" title="Chenyoo Hao" src="https://images.opencollective.com/chenyoo-hao/avatar/40.png" /></a>
+ <a href="https://opencollective.com/alexey-vazhnov"><img height="40" width="40" title="Alexey Vazhnov" src="https://images.opencollective.com/alexey-vazhnov/daed334/avatar/40.png" /></a>
+ <a href="https://github.com/indeedeng"><img height="40" width="40" title="indeedeng" src="https://avatars.githubusercontent.com/u/2905043?s=200&v=4" /></a>
+ <a href="https://github.com/PySimpleGUI"><img height="40" width="40" title="PySimpleGUI" src="https://avatars.githubusercontent.com/u/46163555?v=4" /></a>
+ <a href="https://github.com/u93"><img height="40" width="40" title="Eugenio E Breijo" src="https://avatars.githubusercontent.com/u/16807302?v=4" /></a>
+ <a href="https://github.com/guilt"><img height="40" width="40" title="Karthik Kumar Viswanathan" src="https://avatars.githubusercontent.com/u/195178?v=4" /></a>
+ <a href="https://github.com/eallrich"><img height="40" width="40" title="Evan Allrich" src="https://avatars.githubusercontent.com/u/17393?v=4" /></a>
+ <a href="https://github.com/robusta-dev"><img height="40" width="40" title="Robusta" src="https://avatars.githubusercontent.com/u/82757710?s=200&v=4" /></a>
+ <a href="https://github.com/JeremyGrosser"><img height="40" width="40" title="JeremyGrosser" src="https://avatars.githubusercontent.com/u/2151?v=4" /></a>
+ <a href="https://github.com/getsentry"><img height="40" width="40" title="getsentry" src="https://avatars.githubusercontent.com/u/1396951?s=200&v=4" /></a>
+
+ </div>
<sup><a href="https://github.com/sponsors/giampaolo">add your avatar</a></sup>
+
Contributing
============
-See `CONTRIBUTING.md <https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md>`__ guidelines.
+See `contributing guidelines <https://github.com/giampaolo/psutil/blob/master/CONTRIBUTING.md>`__.
Example usages
==============
@@ -154,7 +173,7 @@ CPU
>>> import psutil
>>>
>>> psutil.cpu_times()
- scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
+ scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, guest_nice=0.0)
>>>
>>> for x in range(3):
... psutil.cpu_percent(interval=1)
@@ -209,7 +228,7 @@ Disks
>>> psutil.disk_partitions()
[sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid', maxfile=255, maxpath=4096),
- sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw', maxfile=255, maxpath=4096)]
+ sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext', opts='rw', maxfile=255, maxpath=4096)]
>>>
>>> psutil.disk_usage('/')
sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
@@ -241,8 +260,8 @@ Network
snicaddr(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
>>>
>>> psutil.net_if_stats()
- {'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536),
- 'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500)}
+ {'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536, flags='up,loopback,running'),
+ 'wlan0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500, flags='up,broadcast,running,multicast')}
>>>
Sensors
@@ -295,39 +314,38 @@ Process management
>>> p = psutil.Process(7055)
>>> p
psutil.Process(pid=7055, name='python3', status='running', started='09:04:44')
+ >>> p.pid
+ 7055
>>> p.name()
- 'python'
+ 'python3'
>>> p.exe()
- '/usr/bin/python'
+ '/usr/bin/python3'
>>> p.cwd()
'/home/giampaolo'
>>> p.cmdline()
- ['/usr/bin/python', 'main.py']
+ ['/usr/bin/python3', 'main.py']
>>>
- >>> p.pid
- 7055
>>> p.ppid()
7054
- >>> p.children(recursive=True)
- [psutil.Process(pid=29835, name='python3', status='sleeping', started='11:45:38'),
- psutil.Process(pid=29836, name='python3', status='waking', started='11:43:39')]
- >>>
>>> p.parent()
psutil.Process(pid=4699, name='bash', status='sleeping', started='09:06:44')
>>> p.parents()
[psutil.Process(pid=4699, name='bash', started='09:06:44'),
psutil.Process(pid=4689, name='gnome-terminal-server', status='sleeping', started='0:06:44'),
psutil.Process(pid=1, name='systemd', status='sleeping', started='05:56:55')]
+ >>> p.children(recursive=True)
+ [psutil.Process(pid=29835, name='python3', status='sleeping', started='11:45:38'),
+ psutil.Process(pid=29836, name='python3', status='waking', started='11:43:39')]
>>>
>>> p.status()
'running'
- >>> p.username()
- 'giampaolo'
>>> p.create_time()
1267551141.5019531
>>> p.terminal()
'/dev/pts/0'
>>>
+ >>> p.username()
+ 'giampaolo'
>>> p.uids()
puids(real=1000, effective=1000, saved=1000)
>>> p.gids()
@@ -367,14 +385,14 @@ Process management
[pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=48776), raddr=addr(ip='93.186.135.91', port=80), status='ESTABLISHED'),
pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=addr(ip='10.0.0.1', port=43761), raddr=addr(ip='72.14.234.100', port=80), status='CLOSING')]
>>>
- >>> p.num_threads()
- 4
- >>> p.num_fds()
- 8
>>> p.threads()
[pthread(id=5234, user_time=22.5, system_time=9.2891),
pthread(id=5237, user_time=0.0707, system_time=1.1)]
>>>
+ >>> p.num_threads()
+ 4
+ >>> p.num_fds()
+ 8
>>> p.num_ctx_switches()
pctxsw(voluntary=78, involuntary=19)
>>>
@@ -441,23 +459,6 @@ Further process APIs
>>> gone, alive = psutil.wait_procs(procs_list, timeout=3, callback=on_terminate)
>>>
-Popen wrapper:
-
-.. code-block:: python
-
- >>> import psutil
- >>> from subprocess import PIPE
- >>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
- >>> p.name()
- 'python'
- >>> p.username()
- 'giampaolo'
- >>> p.communicate()
- ('hello\n', None)
- >>> p.wait(timeout=2)
- 0
- >>>
-
Windows services
----------------
@@ -488,6 +489,7 @@ Here's some I find particularly interesting:
- https://github.com/google/grr
- https://github.com/facebook/osquery/
- https://github.com/nicolargo/glances
+- https://github.com/aristocratos/bpytop
- https://github.com/Jahaja/psdash
- https://github.com/ajenti/ajenti
- https://github.com/home-assistant/home-assistant/
@@ -499,14 +501,3 @@ Portings
- C: https://github.com/hamon-in/cpslib
- Rust: https://github.com/rust-psutil/rust-psutil
- Nim: https://github.com/johnscillieri/psutil-nim
-
-Security
-========
-
-To report a security vulnerability, please use the `Tidelift security
-contact`_. Tidelift will coordinate the fix and disclosure.
-
-.. _`Giampaolo Rodola`: https://gmpy.dev/about
-.. _`donation`: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
-.. _Tidelift security contact: https://tidelift.com/security
-.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
diff --git a/contrib/python/psutil/py3/psutil/__init__.py b/contrib/python/psutil/py3/psutil/__init__.py
index acd42ac264..8138db41e1 100644
--- a/contrib/python/psutil/py3/psutil/__init__.py
+++ b/contrib/python/psutil/py3/psutil/__init__.py
@@ -17,10 +17,11 @@ sensors) in Python. Supported platforms:
- Sun Solaris
- AIX
-Works with Python versions from 2.6 to 3.4+.
+Works with Python versions 2.7 and 3.6+.
"""
from __future__ import division
+
import collections
import contextlib
import datetime
@@ -31,24 +32,16 @@ import subprocess
import sys
import threading
import time
+
+
try:
import pwd
except ImportError:
pwd = None
from . import _common
-from ._common import AccessDenied
-from ._common import Error
-from ._common import memoize_when_activated
-from ._common import NoSuchProcess
-from ._common import TimeoutExpired
-from ._common import wrap_numbers as _wrap_numbers
-from ._common import ZombieProcess
-from ._compat import long
-from ._compat import PermissionError
-from ._compat import ProcessLookupError
-from ._compat import PY3 as _PY3
-
+from ._common import AIX
+from ._common import BSD
from ._common import CONN_CLOSE
from ._common import CONN_CLOSE_WAIT
from ._common import CONN_CLOSING
@@ -61,9 +54,16 @@ from ._common import CONN_NONE
from ._common import CONN_SYN_RECV
from ._common import CONN_SYN_SENT
from ._common import CONN_TIME_WAIT
+from ._common import FREEBSD # NOQA
+from ._common import LINUX
+from ._common import MACOS
+from ._common import NETBSD # NOQA
from ._common import NIC_DUPLEX_FULL
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import OPENBSD # NOQA
+from ._common import OSX # deprecated alias
+from ._common import POSIX # NOQA
from ._common import POWER_TIME_UNKNOWN
from ._common import POWER_TIME_UNLIMITED
from ._common import STATUS_DEAD
@@ -78,18 +78,21 @@ from ._common import STATUS_TRACING_STOP
from ._common import STATUS_WAITING
from ._common import STATUS_WAKING
from ._common import STATUS_ZOMBIE
-
-from ._common import AIX
-from ._common import BSD
-from ._common import FREEBSD # NOQA
-from ._common import LINUX
-from ._common import MACOS
-from ._common import NETBSD # NOQA
-from ._common import OPENBSD # NOQA
-from ._common import OSX # deprecated alias
-from ._common import POSIX # NOQA
from ._common import SUNOS
from ._common import WINDOWS
+from ._common import AccessDenied
+from ._common import Error
+from ._common import NoSuchProcess
+from ._common import TimeoutExpired
+from ._common import ZombieProcess
+from ._common import memoize_when_activated
+from ._common import wrap_numbers as _wrap_numbers
+from ._compat import PY3 as _PY3
+from ._compat import PermissionError
+from ._compat import ProcessLookupError
+from ._compat import SubprocessTimeoutExpired as _SubprocessTimeoutExpired
+from ._compat import long
+
if LINUX:
# This is public API and it will be retrieved from _pslinux.py
@@ -97,7 +100,6 @@ if LINUX:
PROCFS_PATH = "/proc"
from . import _pslinux as _psplatform
-
from ._pslinux import IOPRIO_CLASS_BE # NOQA
from ._pslinux import IOPRIO_CLASS_IDLE # NOQA
from ._pslinux import IOPRIO_CLASS_NONE # NOQA
@@ -112,10 +114,10 @@ elif WINDOWS:
from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
from ._pswindows import CONN_DELETE_TCB # NOQA
- from ._pswindows import IOPRIO_VERYLOW # NOQA
+ from ._pswindows import IOPRIO_HIGH # NOQA
from ._pswindows import IOPRIO_LOW # NOQA
from ._pswindows import IOPRIO_NORMAL # NOQA
- from ._pswindows import IOPRIO_HIGH # NOQA
+ from ._pswindows import IOPRIO_VERYLOW # NOQA
elif MACOS:
from . import _psosx as _psplatform
@@ -143,6 +145,7 @@ else: # pragma: no cover
raise NotImplementedError('platform %s is not supported' % sys.platform)
+# fmt: off
__all__ = [
# exceptions
"Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
@@ -189,6 +192,7 @@ __all__ = [
# "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
"users", "boot_time", # others
]
+# fmt: on
__all__.extend(_psplatform.__extra__all__)
@@ -209,7 +213,7 @@ if hasattr(_psplatform.Process, "rlimit"):
AF_LINK = _psplatform.AF_LINK
__author__ = "Giampaolo Rodola'"
-__version__ = "5.8.0"
+__version__ = "5.9.8"
version_info = tuple([int(num) for num in __version__.split('.')])
_timer = getattr(time, 'monotonic', time.time)
@@ -223,18 +227,25 @@ _SENTINEL = object()
# was compiled for a different version of psutil.
# We want to prevent that by failing sooner rather than later.
# See: https://github.com/giampaolo/psutil/issues/564
-if (int(__version__.replace('.', '')) !=
- getattr(_psplatform.cext, 'version', None)):
- msg = "version conflict: %r C extension module was built for another " \
- "version of psutil" % getattr(_psplatform.cext, "__file__")
+if int(__version__.replace('.', '')) != getattr(
+ _psplatform.cext, 'version', None
+):
+ msg = "version conflict: %r C extension " % _psplatform.cext.__file__
+ msg += "module was built for another version of psutil"
if hasattr(_psplatform.cext, 'version'):
msg += " (%s instead of %s)" % (
- '.'.join([x for x in str(_psplatform.cext.version)]), __version__)
+ '.'.join([x for x in str(_psplatform.cext.version)]),
+ __version__,
+ )
else:
msg += " (different than %s)" % __version__
msg += "; you may try to 'pip uninstall psutil', manually remove %s" % (
- getattr(_psplatform.cext, "__file__",
- "the existing psutil install directory"))
+ getattr(
+ _psplatform.cext,
+ "__file__",
+ "the existing psutil install directory",
+ )
+ )
msg += " or clean the virtual env somehow, then reinstall"
raise ImportError(msg)
@@ -248,6 +259,7 @@ if hasattr(_psplatform, 'ppid_map'):
# Faster version (Windows and Linux).
_ppid_map = _psplatform.ppid_map
else: # pragma: no cover
+
def _ppid_map():
"""Return a {pid: ppid, ...} dict for all running processes in
one shot. Used to speed up Process.children().
@@ -261,26 +273,11 @@ else: # pragma: no cover
return ret
-def _assert_pid_not_reused(fun):
- """Decorator which raises NoSuchProcess in case a process is no
- longer running or its PID has been reused.
- """
- @functools.wraps(fun)
- def wrapper(self, *args, **kwargs):
- if not self.is_running():
- raise NoSuchProcess(self.pid, self._name)
- return fun(self, *args, **kwargs)
- return wrapper
-
-
def _pprint_secs(secs):
"""Format seconds in a human readable form."""
now = time.time()
secs_ago = int(now - secs)
- if secs_ago < 60 * 60 * 24:
- fmt = "%H:%M:%S"
- else:
- fmt = "%Y-%m-%d %H:%M:%S"
+ fmt = "%H:%M:%S" if secs_ago < 60 * 60 * 24 else "%Y-%m-%d %H:%M:%S"
return datetime.datetime.fromtimestamp(secs).strftime(fmt)
@@ -289,7 +286,7 @@ def _pprint_secs(secs):
# =====================================================================
-class Process(object):
+class Process(object): # noqa: UP004
"""Represents an OS process with the given PID.
If PID is omitted current process PID (os.getpid()) is used.
Raise NoSuchProcess if PID does not exist.
@@ -319,7 +316,7 @@ class Process(object):
- use is_running() before querying the process
- if you're continuously iterating over a set of Process
instances use process_iter() which pre-emptively checks
- process identity for every yielded instance
+ process identity for every yielded instance
"""
def __init__(self, pid=None):
@@ -330,15 +327,23 @@ class Process(object):
pid = os.getpid()
else:
if not _PY3 and not isinstance(pid, (int, long)):
- raise TypeError('pid must be an integer (got %r)' % pid)
+ msg = "pid must be an integer (got %r)" % pid
+ raise TypeError(msg)
if pid < 0:
- raise ValueError('pid must be a positive integer (got %s)'
- % pid)
+ msg = "pid must be a positive integer (got %s)" % pid
+ raise ValueError(msg)
+ try:
+ _psplatform.cext.check_pid_range(pid)
+ except OverflowError:
+ msg = "process PID out of range (got %s)" % pid
+ raise NoSuchProcess(pid, msg=msg)
+
self._pid = pid
self._name = None
self._exe = None
self._create_time = None
self._gone = False
+ self._pid_reused = False
self._hash = None
self._lock = threading.RLock()
# used for caching on Windows only (on POSIX ppid may change)
@@ -363,21 +368,18 @@ class Process(object):
pass
except NoSuchProcess:
if not _ignore_nsp:
- msg = 'no process found with pid %s' % pid
- raise NoSuchProcess(pid, None, msg)
+ msg = "process PID not found"
+ raise NoSuchProcess(pid, msg=msg)
else:
self._gone = True
- # This pair is supposed to indentify a Process instance
+ # This pair is supposed to identify a Process instance
# univocally over time (the PID alone is not enough as
# it might refer to a process whose PID has been reused).
# This will be used later in __eq__() and is_running().
self._ident = (self.pid, self._create_time)
def __str__(self):
- try:
- info = collections.OrderedDict()
- except AttributeError: # pragma: no cover
- info = {} # Python 2.6
+ info = collections.OrderedDict()
info["pid"] = self.pid
if self._name:
info['name'] = self._name
@@ -393,12 +395,13 @@ class Process(object):
pass
if self._exitcode not in (_SENTINEL, None):
info["exitcode"] = self._exitcode
- if self._create_time:
+ if self._create_time is not None:
info['started'] = _pprint_secs(self._create_time)
return "%s.%s(%s)" % (
self.__class__.__module__,
self.__class__.__name__,
- ", ".join(["%s=%r" % (k, v) for k, v in info.items()]))
+ ", ".join(["%s=%r" % (k, v) for k, v in info.items()]),
+ )
__repr__ = __str__
@@ -407,6 +410,20 @@ class Process(object):
# on PID and creation time.
if not isinstance(other, Process):
return NotImplemented
+ if OPENBSD or NETBSD: # pragma: no cover
+ # Zombie processes on Open/NetBSD have a creation time of
+ # 0.0. This covers the case when a process started normally
+ # (so it has a ctime), then it turned into a zombie. It's
+ # important to do this because is_running() depends on
+ # __eq__.
+ pid1, ctime1 = self._ident
+ pid2, ctime2 = other._ident
+ if pid1 == pid2:
+ if ctime1 and not ctime2:
+ try:
+ return self.status() == STATUS_ZOMBIE
+ except Error:
+ pass
return self._ident == other._ident
def __ne__(self, other):
@@ -417,6 +434,18 @@ class Process(object):
self._hash = hash(self._ident)
return self._hash
+ def _raise_if_pid_reused(self):
+ """Raises NoSuchProcess in case process PID has been reused."""
+ if not self.is_running() and self._pid_reused:
+ # We may directly raise NSP in here already if PID is just
+ # not running, but I prefer NSP to be raised naturally by
+ # the actual Process API call. This way unit tests will tell
+ # us if the API is broken (aka don't raise NSP when it
+ # should). We also remain consistent with all other "get"
+ # APIs which don't use _raise_if_pid_reused().
+ msg = "process no longer exists and its PID has been reused"
+ raise NoSuchProcess(self.pid, self._name, msg=msg)
+
@property
def pid(self):
"""The process PID."""
@@ -505,15 +534,18 @@ class Process(object):
valid_names = _as_dict_attrnames
if attrs is not None:
if not isinstance(attrs, (list, tuple, set, frozenset)):
- raise TypeError("invalid attrs type %s" % type(attrs))
+ msg = "invalid attrs type %s" % type(attrs)
+ raise TypeError(msg)
attrs = set(attrs)
invalid_names = attrs - valid_names
if invalid_names:
- raise ValueError("invalid attr name%s %s" % (
+ msg = "invalid attr name%s %s" % (
"s" if len(invalid_names) > 1 else "",
- ", ".join(map(repr, invalid_names))))
+ ", ".join(map(repr, invalid_names)),
+ )
+ raise ValueError(msg)
- retdict = dict()
+ retdict = {}
ls = attrs or valid_names
with self.oneshot():
for name in ls:
@@ -570,7 +602,7 @@ class Process(object):
It also checks if PID has been reused by another process in
which case return False.
"""
- if self._gone:
+ if self._gone or self._pid_reused:
return False
try:
# Checking if PID is alive is not enough as the PID might
@@ -578,7 +610,8 @@ class Process(object):
# verify process identity.
# Process identity / uniqueness over time is guaranteed by
# (PID + creation time) and that is verified in __eq__.
- return self == Process(self.pid)
+ self._pid_reused = self != Process(self.pid)
+ return not self._pid_reused
except ZombieProcess:
# We should never get here as it's already handled in
# Process.__init__; here just for extra safety.
@@ -601,6 +634,7 @@ class Process(object):
# XXX should we check creation time here rather than in
# Process.parent()?
+ self._raise_if_pid_reused()
if POSIX:
return self._proc.ppid()
else: # pragma: no cover
@@ -622,7 +656,12 @@ class Process(object):
# Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon".
try:
cmdline = self.cmdline()
- except AccessDenied:
+ except (AccessDenied, ZombieProcess):
+ # Just pass and return the truncated name: it's better
+ # than nothing. Note: there are actual cases where a
+ # zombie process can return a name() but not a
+ # cmdline(), see:
+ # https://github.com/giampaolo/psutil/issues/2239
pass
else:
if cmdline:
@@ -638,6 +677,7 @@ class Process(object):
May also be an empty string.
The return value is cached after first call.
"""
+
def guess_it(fallback):
# try to guess exe from cmdline[0] in absence of a native
# exe representation
@@ -647,9 +687,11 @@ class Process(object):
# Attempt to guess only in case of an absolute path.
# It is not safe otherwise as the process might have
# changed cwd.
- if (os.path.isabs(exe) and
- os.path.isfile(exe) and
- os.access(exe, os.X_OK)):
+ if (
+ os.path.isabs(exe)
+ and os.path.isfile(exe)
+ and os.access(exe, os.X_OK)
+ ):
return exe
if isinstance(fallback, AccessDenied):
raise fallback
@@ -690,8 +732,8 @@ class Process(object):
if POSIX:
if pwd is None:
# might happen if python was installed from sources
- raise ImportError(
- "requires pwd module shipped with standard python")
+ msg = "requires pwd module shipped with standard python"
+ raise ImportError(msg)
real_uid = self.uids().real
try:
return pwd.getpwuid(real_uid).pw_name
@@ -719,8 +761,7 @@ class Process(object):
if value is None:
return self._proc.nice_get()
else:
- if not self.is_running():
- raise NoSuchProcess(self.pid, self._name)
+ self._raise_if_pid_reused()
self._proc.nice_set(value)
if POSIX:
@@ -779,9 +820,11 @@ class Process(object):
"""
if ioclass is None:
if value is not None:
- raise ValueError("'ioclass' argument must be specified")
+ msg = "'ioclass' argument must be specified"
+ raise ValueError(msg)
return self._proc.ionice_get()
else:
+ self._raise_if_pid_reused()
return self._proc.ionice_set(ioclass, value)
# Linux / FreeBSD only
@@ -797,6 +840,8 @@ class Process(object):
See "man prlimit" for further info.
Available on Linux and FreeBSD only.
"""
+ if limits is not None:
+ self._raise_if_pid_reused()
return self._proc.rlimit(resource, limits)
# Windows, Linux and FreeBSD only
@@ -813,6 +858,7 @@ class Process(object):
if cpus is None:
return sorted(set(self._proc.cpu_affinity_get()))
else:
+ self._raise_if_pid_reused()
if not cpus:
if hasattr(self._proc, "_get_eligible_cpus"):
cpus = self._proc._get_eligible_cpus()
@@ -838,7 +884,8 @@ class Process(object):
def environ(self):
"""The environment variables of the process as a dict. Note: this
- might not reflect changes made after the process started. """
+ might not reflect changes made after the process started.
+ """
return self._proc.environ()
if WINDOWS:
@@ -869,7 +916,6 @@ class Process(object):
"""
return self._proc.threads()
- @_assert_pid_not_reused
def children(self, recursive=False):
"""Return the children of this process as a list of Process
instances, pre-emptively checking whether PID has been reused.
@@ -896,6 +942,7 @@ class Process(object):
process Y won't be listed as the reference to process A
is lost.
"""
+ self._raise_if_pid_reused()
ppid_map = _ppid_map()
ret = []
if not recursive:
@@ -976,7 +1023,8 @@ class Process(object):
"""
blocking = interval is not None and interval > 0.0
if interval is not None and interval < 0:
- raise ValueError("interval is not positive (got %r)" % interval)
+ msg = "interval is not positive (got %r)" % interval
+ raise ValueError(msg)
num_cpus = cpu_count() or 1
def timer():
@@ -1008,7 +1056,7 @@ class Process(object):
# This is the utilization split evenly between all CPUs.
# E.g. a busy loop process on a 2-CPU-cores system at this
# point is reported as 50% instead of 100%.
- overall_cpus_percent = ((delta_proc / delta_time) * 100)
+ overall_cpus_percent = (delta_proc / delta_time) * 100
except ZeroDivisionError:
# interval was too low
return 0.0
@@ -1047,7 +1095,7 @@ class Process(object):
"""Return a namedtuple with variable fields depending on the
platform, representing memory information about the process.
- The "portable" fields available on all plaforms are `rss` and `vms`.
+ The "portable" fields available on all platforms are `rss` and `vms`.
All numbers are expressed in bytes.
"""
@@ -1085,10 +1133,16 @@ class Process(object):
"""
valid_types = list(_psplatform.pfullmem._fields)
if memtype not in valid_types:
- raise ValueError("invalid memtype %r; valid types are %r" % (
- memtype, tuple(valid_types)))
- fun = self.memory_info if memtype in _psplatform.pmem._fields else \
- self.memory_full_info
+ msg = "invalid memtype %r; valid types are %r" % (
+ memtype,
+ tuple(valid_types),
+ )
+ raise ValueError(msg)
+ fun = (
+ self.memory_info
+ if memtype in _psplatform.pmem._fields
+ else self.memory_full_info
+ )
metrics = fun()
value = getattr(metrics, memtype)
@@ -1096,13 +1150,15 @@ class Process(object):
total_phymem = _TOTAL_PHYMEM or virtual_memory().total
if not total_phymem > 0:
# we should never get here
- raise ValueError(
- "can't calculate process memory percent because "
- "total physical system memory is not positive (%r)"
- % total_phymem)
+ msg = (
+ "can't calculate process memory percent because total physical"
+ " system memory is not positive (%r)" % (total_phymem)
+ )
+ raise ValueError(msg)
return (value / float(total_phymem)) * 100
if hasattr(_psplatform.Process, "memory_maps"):
+
def memory_maps(self, grouped=True):
"""Return process' mapped memory regions as a list of namedtuples
whose fields are variable depending on the platform.
@@ -1164,14 +1220,18 @@ class Process(object):
# --- signals
if POSIX:
+
def _send_signal(self, sig):
assert not self.pid < 0, self.pid
+ self._raise_if_pid_reused()
if self.pid == 0:
# see "man 2 kill"
- raise ValueError(
+ msg = (
"preventing sending signal to process with PID 0 as it "
"would affect every process in the process group of the "
- "calling process (os.getpid()) instead of PID 0")
+ "calling process (os.getpid()) instead of PID 0"
+ )
+ raise ValueError(msg)
try:
os.kill(self.pid, sig)
except ProcessLookupError:
@@ -1185,7 +1245,6 @@ class Process(object):
except PermissionError:
raise AccessDenied(self.pid, self._name)
- @_assert_pid_not_reused
def send_signal(self, sig):
"""Send a signal *sig* to process pre-emptively checking
whether PID has been reused (see signal module constants) .
@@ -1195,20 +1254,23 @@ class Process(object):
if POSIX:
self._send_signal(sig)
else: # pragma: no cover
+ self._raise_if_pid_reused()
+ if sig != signal.SIGTERM and not self.is_running():
+ msg = "process no longer exists"
+ raise NoSuchProcess(self.pid, self._name, msg=msg)
self._proc.send_signal(sig)
- @_assert_pid_not_reused
def suspend(self):
"""Suspend process execution with SIGSTOP pre-emptively checking
whether PID has been reused.
- On Windows this has the effect ot suspending all process threads.
+ On Windows this has the effect of suspending all process threads.
"""
if POSIX:
self._send_signal(signal.SIGSTOP)
else: # pragma: no cover
+ self._raise_if_pid_reused()
self._proc.suspend()
- @_assert_pid_not_reused
def resume(self):
"""Resume process execution with SIGCONT pre-emptively checking
whether PID has been reused.
@@ -1217,9 +1279,9 @@ class Process(object):
if POSIX:
self._send_signal(signal.SIGCONT)
else: # pragma: no cover
+ self._raise_if_pid_reused()
self._proc.resume()
- @_assert_pid_not_reused
def terminate(self):
"""Terminate the process with SIGTERM pre-emptively checking
whether PID has been reused.
@@ -1228,9 +1290,9 @@ class Process(object):
if POSIX:
self._send_signal(signal.SIGTERM)
else: # pragma: no cover
+ self._raise_if_pid_reused()
self._proc.kill()
- @_assert_pid_not_reused
def kill(self):
"""Kill the current process with SIGKILL pre-emptively checking
whether PID has been reused.
@@ -1238,6 +1300,7 @@ class Process(object):
if POSIX:
self._send_signal(signal.SIGKILL)
else: # pragma: no cover
+ self._raise_if_pid_reused()
self._proc.kill()
def wait(self, timeout=None):
@@ -1255,7 +1318,8 @@ class Process(object):
To wait for multiple Process(es) use psutil.wait_procs().
"""
if timeout is not None and not timeout >= 0:
- raise ValueError("timeout must be a positive integer")
+ msg = "timeout must be a positive integer"
+ raise ValueError(msg)
if self._exitcode is not _SENTINEL:
return self._exitcode
self._exitcode = self._proc.wait(timeout)
@@ -1263,11 +1327,13 @@ class Process(object):
# The valid attr names which can be processed by Process.as_dict().
+# fmt: off
_as_dict_attrnames = set(
[x for x in dir(Process) if not x.startswith('_') and x not in
- ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
+ {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit',
- 'memory_info_ex', 'oneshot']])
+ 'memory_info_ex', 'oneshot'}])
+# fmt: on
# =====================================================================
@@ -1298,7 +1364,7 @@ class Popen(Process):
>>> p.username()
'giampaolo'
>>> p.communicate()
- ('hi\n', None)
+ ('hi', None)
>>> p.terminate()
>>> p.wait(timeout=2)
0
@@ -1343,13 +1409,16 @@ class Popen(Process):
try:
return object.__getattribute__(self.__subproc, name)
except AttributeError:
- raise AttributeError("%s instance has no attribute '%s'"
- % (self.__class__.__name__, name))
+ msg = "%s instance has no attribute '%s'" % (
+ self.__class__.__name__,
+ name,
+ )
+ raise AttributeError(msg)
def wait(self, timeout=None):
if self.__subproc.returncode is not None:
return self.__subproc.returncode
- ret = super(Popen, self).wait(timeout)
+ ret = super(Popen, self).wait(timeout) # noqa
self.__subproc.returncode = ret
return ret
@@ -1386,7 +1455,6 @@ def pid_exists(pid):
_pmap = {}
-_lock = threading.Lock()
def process_iter(attrs=None, ad_value=None):
@@ -1410,58 +1478,60 @@ def process_iter(attrs=None, ad_value=None):
If *attrs* is an empty list it will retrieve all process info
(slow).
"""
+ global _pmap
+
def add(pid):
proc = Process(pid)
if attrs is not None:
proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value)
- with _lock:
- _pmap[proc.pid] = proc
+ pmap[proc.pid] = proc
return proc
def remove(pid):
- with _lock:
- _pmap.pop(pid, None)
+ pmap.pop(pid, None)
+ pmap = _pmap.copy()
a = set(pids())
- b = set(_pmap.keys())
+ b = set(pmap.keys())
new_pids = a - b
gone_pids = b - a
for pid in gone_pids:
remove(pid)
-
- with _lock:
- ls = sorted(list(_pmap.items()) +
- list(dict.fromkeys(new_pids).items()))
-
- for pid, proc in ls:
- try:
- if proc is None: # new process
- yield add(pid)
- else:
- # use is_running() to check whether PID has been reused by
- # another process in which case yield a new Process instance
- if proc.is_running():
- if attrs is not None:
- proc.info = proc.as_dict(
- attrs=attrs, ad_value=ad_value)
- yield proc
- else:
+ try:
+ ls = sorted(list(pmap.items()) + list(dict.fromkeys(new_pids).items()))
+ for pid, proc in ls:
+ try:
+ if proc is None: # new process
yield add(pid)
- except NoSuchProcess:
- remove(pid)
- except AccessDenied:
- # Process creation time can't be determined hence there's
- # no way to tell whether the pid of the cached process
- # has been reused. Just return the cached version.
- if proc is None and pid in _pmap:
- try:
- yield _pmap[pid]
- except KeyError:
- # If we get here it is likely that 2 threads were
- # using process_iter().
- pass
- else:
- raise
+ else:
+ # use is_running() to check whether PID has been
+ # reused by another process in which case yield a
+ # new Process instance
+ if proc.is_running():
+ if attrs is not None:
+ proc.info = proc.as_dict(
+ attrs=attrs, ad_value=ad_value
+ )
+ yield proc
+ else:
+ yield add(pid)
+ except NoSuchProcess:
+ remove(pid)
+ except AccessDenied:
+ # Process creation time can't be determined hence there's
+ # no way to tell whether the pid of the cached process
+ # has been reused. Just return the cached version.
+ if proc is None and pid in pmap:
+ try:
+ yield pmap[pid]
+ except KeyError:
+ # If we get here it is likely that 2 threads were
+ # using process_iter().
+ pass
+ else:
+ raise
+ finally:
+ _pmap = pmap
def wait_procs(procs, timeout=None, callback=None):
@@ -1500,11 +1570,14 @@ def wait_procs(procs, timeout=None, callback=None):
>>> for p in alive:
... p.kill()
"""
+
def check_gone(proc, timeout):
try:
returncode = proc.wait(timeout=timeout)
except TimeoutExpired:
pass
+ except _SubprocessTimeoutExpired:
+ pass
else:
if returncode is not None or not proc.is_running():
# Set new Process instance attribute.
@@ -1519,7 +1592,8 @@ def wait_procs(procs, timeout=None, callback=None):
gone = set()
alive = set(procs)
if callback is not None and not callable(callback):
- raise TypeError("callback %r is not a callable" % callable)
+ msg = "callback %r is not a callable" % callback
+ raise TypeError(msg)
if timeout is not None:
deadline = _timer() + timeout
@@ -1575,7 +1649,7 @@ def cpu_count(logical=True):
if logical:
ret = _psplatform.cpu_count_logical()
else:
- ret = _psplatform.cpu_count_physical()
+ ret = _psplatform.cpu_count_cores()
if ret is not None and ret < 1:
ret = None
return ret
@@ -1610,16 +1684,18 @@ def cpu_times(percpu=False):
try:
- _last_cpu_times = cpu_times()
-except Exception:
+ _last_cpu_times = {threading.current_thread().ident: cpu_times()}
+except Exception: # noqa: BLE001
# Don't want to crash at import time.
- _last_cpu_times = None
+ _last_cpu_times = {}
try:
- _last_per_cpu_times = cpu_times(percpu=True)
-except Exception:
+ _last_per_cpu_times = {
+ threading.current_thread().ident: cpu_times(percpu=True)
+ }
+except Exception: # noqa: BLE001
# Don't want to crash at import time.
- _last_per_cpu_times = None
+ _last_per_cpu_times = {}
def _cpu_tot_time(times):
@@ -1713,15 +1789,14 @@ def cpu_percent(interval=None, percpu=False):
2.9
>>>
"""
- global _last_cpu_times
- global _last_per_cpu_times
+ tid = threading.current_thread().ident
blocking = interval is not None and interval > 0.0
if interval is not None and interval < 0:
- raise ValueError("interval is not positive (got %r)" % interval)
+ msg = "interval is not positive (got %r)" % interval
+ raise ValueError(msg)
def calculate(t1, t2):
times_delta = _cpu_times_deltas(t1, t2)
-
all_delta = _cpu_tot_time(times_delta)
busy_delta = _cpu_busy_time(times_delta)
@@ -1738,14 +1813,9 @@ def cpu_percent(interval=None, percpu=False):
t1 = cpu_times()
time.sleep(interval)
else:
- t1 = _last_cpu_times
- if t1 is None:
- # Something bad happened at import time. We'll
- # get a meaningful result on the next call. See:
- # https://github.com/giampaolo/psutil/pull/715
- t1 = cpu_times()
- _last_cpu_times = cpu_times()
- return calculate(t1, _last_cpu_times)
+ t1 = _last_cpu_times.get(tid) or cpu_times()
+ _last_cpu_times[tid] = cpu_times()
+ return calculate(t1, _last_cpu_times[tid])
# per-cpu usage
else:
ret = []
@@ -1753,23 +1823,17 @@ def cpu_percent(interval=None, percpu=False):
tot1 = cpu_times(percpu=True)
time.sleep(interval)
else:
- tot1 = _last_per_cpu_times
- if tot1 is None:
- # Something bad happened at import time. We'll
- # get a meaningful result on the next call. See:
- # https://github.com/giampaolo/psutil/pull/715
- tot1 = cpu_times(percpu=True)
- _last_per_cpu_times = cpu_times(percpu=True)
- for t1, t2 in zip(tot1, _last_per_cpu_times):
+ tot1 = _last_per_cpu_times.get(tid) or cpu_times(percpu=True)
+ _last_per_cpu_times[tid] = cpu_times(percpu=True)
+ for t1, t2 in zip(tot1, _last_per_cpu_times[tid]):
ret.append(calculate(t1, t2))
return ret
-# Use separate global vars for cpu_times_percent() so that it's
-# independent from cpu_percent() and they can both be used within
-# the same program.
-_last_cpu_times_2 = _last_cpu_times
-_last_per_cpu_times_2 = _last_per_cpu_times
+# Use a separate dict for cpu_times_percent(), so it's independent from
+# cpu_percent() and they can both be used within the same program.
+_last_cpu_times_2 = _last_cpu_times.copy()
+_last_per_cpu_times_2 = _last_per_cpu_times.copy()
def cpu_times_percent(interval=None, percpu=False):
@@ -1785,11 +1849,11 @@ def cpu_times_percent(interval=None, percpu=False):
*interval* and *percpu* arguments have the same meaning as in
cpu_percent().
"""
- global _last_cpu_times_2
- global _last_per_cpu_times_2
+ tid = threading.current_thread().ident
blocking = interval is not None and interval > 0.0
if interval is not None and interval < 0:
- raise ValueError("interval is not positive (got %r)" % interval)
+ msg = "interval is not positive (got %r)" % interval
+ raise ValueError(msg)
def calculate(t1, t2):
nums = []
@@ -1814,14 +1878,9 @@ def cpu_times_percent(interval=None, percpu=False):
t1 = cpu_times()
time.sleep(interval)
else:
- t1 = _last_cpu_times_2
- if t1 is None:
- # Something bad happened at import time. We'll
- # get a meaningful result on the next call. See:
- # https://github.com/giampaolo/psutil/pull/715
- t1 = cpu_times()
- _last_cpu_times_2 = cpu_times()
- return calculate(t1, _last_cpu_times_2)
+ t1 = _last_cpu_times_2.get(tid) or cpu_times()
+ _last_cpu_times_2[tid] = cpu_times()
+ return calculate(t1, _last_cpu_times_2[tid])
# per-cpu usage
else:
ret = []
@@ -1829,14 +1888,9 @@ def cpu_times_percent(interval=None, percpu=False):
tot1 = cpu_times(percpu=True)
time.sleep(interval)
else:
- tot1 = _last_per_cpu_times_2
- if tot1 is None:
- # Something bad happened at import time. We'll
- # get a meaningful result on the next call. See:
- # https://github.com/giampaolo/psutil/pull/715
- tot1 = cpu_times(percpu=True)
- _last_per_cpu_times_2 = cpu_times(percpu=True)
- for t1, t2 in zip(tot1, _last_per_cpu_times_2):
+ tot1 = _last_per_cpu_times_2.get(tid) or cpu_times(percpu=True)
+ _last_per_cpu_times_2[tid] = cpu_times(percpu=True)
+ for t1, t2 in zip(tot1, _last_per_cpu_times_2[tid]):
ret.append(calculate(t1, t2))
return ret
@@ -1849,7 +1903,7 @@ def cpu_stats():
if hasattr(_psplatform, "cpu_freq"):
def cpu_freq(percpu=False):
- """Return CPU frequency as a nameduple including current,
+ """Return CPU frequency as a namedtuple including current,
min and max frequency expressed in Mhz.
If *percpu* is True and the system supports per-cpu frequency
@@ -2004,6 +2058,7 @@ def disk_partitions(all=False):
If *all* parameter is False return physical devices only and ignore
all others.
"""
+
def pathconf(path, name):
try:
return os.pathconf(path, name)
@@ -2016,7 +2071,8 @@ def disk_partitions(all=False):
for item in ret:
nt = item._replace(
maxfile=pathconf(item.mountpoint, 'PC_NAME_MAX'),
- maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX'))
+ maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX'),
+ )
new.append(nt)
return new
else:
@@ -2067,11 +2123,12 @@ def disk_io_counters(perdisk=False, nowrap=True):
rawdict[disk] = nt(*fields)
return rawdict
else:
- return nt(*[sum(x) for x in zip(*rawdict.values())])
+ return nt(*(sum(x) for x in zip(*rawdict.values())))
disk_io_counters.cache_clear = functools.partial(
- _wrap_numbers.cache_clear, 'psutil.disk_io_counters')
+ _wrap_numbers.cache_clear, 'psutil.disk_io_counters'
+)
disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
@@ -2103,7 +2160,7 @@ def net_io_counters(pernic=False, nowrap=True):
and wrap (restart from 0) and add "old value" to "new value" so that
the returned numbers will always be increasing or remain the same,
but never decrease.
- "disk_io_counters.cache_clear()" can be used to invalidate the
+ "net_io_counters.cache_clear()" can be used to invalidate the
cache.
"""
rawdict = _psplatform.net_io_counters()
@@ -2120,7 +2177,8 @@ def net_io_counters(pernic=False, nowrap=True):
net_io_counters.cache_clear = functools.partial(
- _wrap_numbers.cache_clear, 'psutil.net_io_counters')
+ _wrap_numbers.cache_clear, 'psutil.net_io_counters'
+)
net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
@@ -2171,7 +2229,7 @@ def net_if_addrs():
Note: you can have more than one address of the same family
associated with each interface.
"""
- has_enums = sys.version_info >= (3, 4)
+ has_enums = _PY3
if has_enums:
import socket
rawlist = _psplatform.net_if_addrs()
@@ -2184,8 +2242,10 @@ def net_if_addrs():
except ValueError:
if WINDOWS and fam == -1:
fam = _psplatform.AF_LINK
- elif (hasattr(_psplatform, "AF_LINK") and
- _psplatform.AF_LINK == fam):
+ elif (
+ hasattr(_psplatform, "AF_LINK")
+ and fam == _psplatform.AF_LINK
+ ):
# Linux defines AF_LINK as an alias for AF_PACKET.
# We re-set the family here so that repr(family)
# will show AF_LINK rather than AF_PACKET
@@ -2232,6 +2292,7 @@ if hasattr(_psplatform, "sensors_temperatures"):
All temperatures are expressed in celsius unless *fahrenheit*
is set to True.
"""
+
def convert(n):
if n is not None:
return (float(n) * 9 / 5) + 32 if fahrenheit else n
@@ -2252,7 +2313,8 @@ if hasattr(_psplatform, "sensors_temperatures"):
high = critical
ret[name].append(
- _common.shwtemp(label, current, high, critical))
+ _common.shwtemp(label, current, high, critical)
+ )
return dict(ret)
@@ -2338,16 +2400,28 @@ if WINDOWS:
# =====================================================================
+def _set_debug(value):
+ """Enable or disable PSUTIL_DEBUG option, which prints debugging
+ messages to stderr.
+ """
+ import psutil._common
+
+ psutil._common.PSUTIL_DEBUG = bool(value)
+ _psplatform.cext.set_debug(bool(value))
+
+
def test(): # pragma: no cover
from ._common import bytes2human
from ._compat import get_terminal_size
today_day = datetime.date.today()
+ # fmt: off
templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s %s"
attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times',
'create_time', 'memory_info', 'status', 'nice', 'username']
print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE", # NOQA
"STATUS", "START", "TIME", "CMDLINE"))
+ # fmt: on
for p in process_iter(attrs, ad_value=None):
if p.info['create_time']:
ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
@@ -2358,8 +2432,9 @@ def test(): # pragma: no cover
else:
ctime = ''
if p.info['cpu_times']:
- cputime = time.strftime("%M:%S",
- time.localtime(sum(p.info['cpu_times'])))
+ cputime = time.strftime(
+ "%M:%S", time.localtime(sum(p.info['cpu_times']))
+ )
else:
cputime = ''
@@ -2372,12 +2447,21 @@ def test(): # pragma: no cover
if user and WINDOWS and '\\' in user:
user = user.split('\\')[1]
user = user[:9]
- vms = bytes2human(p.info['memory_info'].vms) if \
- p.info['memory_info'] is not None else ''
- rss = bytes2human(p.info['memory_info'].rss) if \
- p.info['memory_info'] is not None else ''
- memp = round(p.info['memory_percent'], 1) if \
- p.info['memory_percent'] is not None else ''
+ vms = (
+ bytes2human(p.info['memory_info'].vms)
+ if p.info['memory_info'] is not None
+ else ''
+ )
+ rss = (
+ bytes2human(p.info['memory_info'].rss)
+ if p.info['memory_info'] is not None
+ else ''
+ )
+ memp = (
+ round(p.info['memory_percent'], 1)
+ if p.info['memory_percent'] is not None
+ else ''
+ )
nice = int(p.info['nice']) if p.info['nice'] else ''
if p.info['cmdline']:
cmdline = ' '.join(p.info['cmdline'])
@@ -2395,13 +2479,14 @@ def test(): # pragma: no cover
status,
ctime,
cputime,
- cmdline)
- print(line[:get_terminal_size()[0]]) # NOQA
+ cmdline,
+ )
+ print(line[: get_terminal_size()[0]]) # NOQA
del memoize_when_activated, division
if sys.version_info[0] < 3:
- del num, x
+ del num, x # noqa
if __name__ == "__main__":
test()
diff --git a/contrib/python/psutil/py3/psutil/_common.py b/contrib/python/psutil/py3/psutil/_common.py
index 771461d692..6989feafda 100644
--- a/contrib/python/psutil/py3/psutil/_common.py
+++ b/contrib/python/psutil/py3/psutil/_common.py
@@ -7,8 +7,10 @@
# Note: this module is imported by setup.py so it should not import
# psutil or third-party modules.
-from __future__ import division, print_function
+from __future__ import division
+from __future__ import print_function
+import collections
import contextlib
import errno
import functools
@@ -18,12 +20,12 @@ import stat
import sys
import threading
import warnings
-from collections import defaultdict
from collections import namedtuple
from socket import AF_INET
from socket import SOCK_DGRAM
from socket import SOCK_STREAM
+
try:
from socket import AF_INET6
except ImportError:
@@ -33,15 +35,19 @@ try:
except ImportError:
AF_UNIX = None
-if sys.version_info >= (3, 4):
+
+# can't take it from _common.py as this script is imported by setup.py
+PY3 = sys.version_info[0] >= 3
+if PY3:
import enum
else:
enum = None
-# can't take it from _common.py as this script is imported by setup.py
-PY3 = sys.version_info[0] == 3
+PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG'))
+_DEFAULT = object()
+# fmt: off
__all__ = [
# OS constants
'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
@@ -67,10 +73,12 @@ __all__ = [
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
'parse_environ_block', 'path_exists_strict', 'usage_percent',
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
+ 'open_text', 'open_binary', 'cat', 'bcat',
'bytes2human', 'conn_to_ntuple', 'debug',
# shell utils
'hilite', 'term_supports_colors', 'print_color',
]
+# fmt: on
# ===================================================================
@@ -83,7 +91,7 @@ WINDOWS = os.name == "nt"
LINUX = sys.platform.startswith("linux")
MACOS = sys.platform.startswith("darwin")
OSX = MACOS # deprecated alias
-FREEBSD = sys.platform.startswith("freebsd")
+FREEBSD = sys.platform.startswith(("freebsd", "midnightbsd"))
OPENBSD = sys.platform.startswith("openbsd")
NETBSD = sys.platform.startswith("netbsd")
BSD = FREEBSD or OPENBSD or NETBSD
@@ -132,6 +140,7 @@ if enum is None:
NIC_DUPLEX_HALF = 1
NIC_DUPLEX_UNKNOWN = 0
else:
+
class NicDuplex(enum.IntEnum):
NIC_DUPLEX_FULL = 2
NIC_DUPLEX_HALF = 1
@@ -144,6 +153,7 @@ if enum is None:
POWER_TIME_UNKNOWN = -1
POWER_TIME_UNLIMITED = -2
else:
+
class BatteryTime(enum.IntEnum):
POWER_TIME_UNKNOWN = -1
POWER_TIME_UNLIMITED = -2
@@ -168,6 +178,7 @@ else:
# --- for system functions
+# fmt: off
# psutil.swap_memory()
sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
'sout'])
@@ -194,7 +205,8 @@ sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
snicaddr = namedtuple('snicaddr',
['family', 'address', 'netmask', 'broadcast', 'ptp'])
# psutil.net_if_stats()
-snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
+snicstats = namedtuple('snicstats',
+ ['isup', 'duplex', 'speed', 'mtu', 'flags'])
# psutil.cpu_stats()
scpustats = namedtuple(
'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'])
@@ -207,12 +219,14 @@ shwtemp = namedtuple(
sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])
# psutil.sensors_fans()
sfan = namedtuple('sfan', ['label', 'current'])
+# fmt: on
# --- for Process methods
# psutil.Process.cpu_times()
-pcputimes = namedtuple('pcputimes',
- ['user', 'system', 'children_user', 'children_system'])
+pcputimes = namedtuple(
+ 'pcputimes', ['user', 'system', 'children_user', 'children_system']
+)
# psutil.Process.open_files()
popenfile = namedtuple('popenfile', ['path', 'fd'])
# psutil.Process.threads()
@@ -222,15 +236,17 @@ puids = namedtuple('puids', ['real', 'effective', 'saved'])
# psutil.Process.gids()
pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
# psutil.Process.io_counters()
-pio = namedtuple('pio', ['read_count', 'write_count',
- 'read_bytes', 'write_bytes'])
+pio = namedtuple(
+ 'pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes']
+)
# psutil.Process.ionice()
pionice = namedtuple('pionice', ['ioclass', 'value'])
# psutil.Process.ctx_switches()
pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
# psutil.Process.connections()
-pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
- 'status'])
+pconn = namedtuple(
+ 'pconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status']
+)
# psutil.connections() and psutil.Process.connections()
addr = namedtuple('addr', ['ip', 'port'])
@@ -259,9 +275,7 @@ if AF_INET6 is not None:
})
if AF_UNIX is not None:
- conn_tmap.update({
- "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
- })
+ conn_tmap.update({"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM])})
# =====================================================================
@@ -273,36 +287,49 @@ class Error(Exception):
"""Base exception class. All other psutil exceptions inherit
from this one.
"""
+
__module__ = 'psutil'
- def __init__(self, msg=""):
- Exception.__init__(self, msg)
- self.msg = msg
+ def _infodict(self, attrs):
+ info = collections.OrderedDict()
+ for name in attrs:
+ value = getattr(self, name, None)
+ if value: # noqa
+ info[name] = value
+ elif name == "pid" and value == 0:
+ info[name] = value
+ return info
+
+ def __str__(self):
+ # invoked on `raise Error`
+ info = self._infodict(("pid", "ppid", "name"))
+ if info:
+ details = "(%s)" % ", ".join(
+ ["%s=%r" % (k, v) for k, v in info.items()]
+ )
+ else:
+ details = None
+ return " ".join([x for x in (getattr(self, "msg", ""), details) if x])
def __repr__(self):
- ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
- return ret.strip()
-
- __str__ = __repr__
+ # invoked on `repr(Error)`
+ info = self._infodict(("pid", "ppid", "name", "seconds", "msg"))
+ details = ", ".join(["%s=%r" % (k, v) for k, v in info.items()])
+ return "psutil.%s(%s)" % (self.__class__.__name__, details)
class NoSuchProcess(Error):
"""Exception raised when a process with a certain PID doesn't
or no longer exists.
"""
+
__module__ = 'psutil'
def __init__(self, pid, name=None, msg=None):
- Error.__init__(self, msg)
+ Error.__init__(self)
self.pid = pid
self.name = name
- self.msg = msg
- if msg is None:
- if name:
- details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
- else:
- details = "(pid=%s)" % self.pid
- self.msg = "process no longer exists " + details
+ self.msg = msg or "process no longer exists"
class ZombieProcess(NoSuchProcess):
@@ -312,57 +339,40 @@ class ZombieProcess(NoSuchProcess):
On Linux all zombie processes are querable (hence this is never
raised). Windows doesn't have zombie processes.
"""
+
__module__ = 'psutil'
def __init__(self, pid, name=None, ppid=None, msg=None):
- NoSuchProcess.__init__(self, msg)
- self.pid = pid
+ NoSuchProcess.__init__(self, pid, name, msg)
self.ppid = ppid
- self.name = name
- self.msg = msg
- if msg is None:
- args = ["pid=%s" % pid]
- if name:
- args.append("name=%s" % repr(self.name))
- if ppid:
- args.append("ppid=%s" % self.ppid)
- details = "(%s)" % ", ".join(args)
- self.msg = "process still exists but it's a zombie " + details
+ self.msg = msg or "PID still exists but it's a zombie"
class AccessDenied(Error):
"""Exception raised when permission to perform an action is denied."""
+
__module__ = 'psutil'
def __init__(self, pid=None, name=None, msg=None):
- Error.__init__(self, msg)
+ Error.__init__(self)
self.pid = pid
self.name = name
- self.msg = msg
- if msg is None:
- if (pid is not None) and (name is not None):
- self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
- elif (pid is not None):
- self.msg = "(pid=%s)" % self.pid
- else:
- self.msg = ""
+ self.msg = msg or ""
class TimeoutExpired(Error):
"""Raised on Process.wait(timeout) if timeout expires and process
is still alive.
"""
+
__module__ = 'psutil'
def __init__(self, seconds, pid=None, name=None):
- Error.__init__(self, "timeout after %s seconds" % seconds)
+ Error.__init__(self)
self.seconds = seconds
self.pid = pid
self.name = name
- if (pid is not None) and (name is not None):
- self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
- elif (pid is not None):
- self.msg += " (pid=%s)" % self.pid
+ self.msg = "timeout after %s seconds" % seconds
# ===================================================================
@@ -370,6 +380,26 @@ class TimeoutExpired(Error):
# ===================================================================
+# This should be in _compat.py rather than here, but does not work well
+# with setup.py importing this module via a sys.path trick.
+if PY3:
+ if isinstance(__builtins__, dict): # cpython
+ exec_ = __builtins__["exec"]
+ else: # pypy
+ exec_ = getattr(__builtins__, "exec") # noqa
+
+ exec_("""def raise_from(value, from_value):
+ try:
+ raise value from from_value
+ finally:
+ value = None
+ """)
+else:
+
+ def raise_from(value, from_value):
+ raise value
+
+
def usage_percent(used, total, round_=None):
"""Calculate percentage usage of 'used' against 'total'."""
try:
@@ -395,14 +425,27 @@ def memoize(fun):
1
>>> foo.cache_clear()
>>>
+
+ It supports:
+ - functions
+ - classes (acts as a @singleton)
+ - staticmethods
+ - classmethods
+
+ It does NOT support:
+ - methods
"""
+
@functools.wraps(fun)
def wrapper(*args, **kwargs):
key = (args, frozenset(sorted(kwargs.items())))
try:
return cache[key]
except KeyError:
- ret = cache[key] = fun(*args, **kwargs)
+ try:
+ ret = cache[key] = fun(*args, **kwargs)
+ except Exception as err: # noqa: BLE001
+ raise raise_from(err, None)
return ret
def cache_clear():
@@ -440,6 +483,7 @@ def memoize_when_activated(fun):
>>> foo()
>>>
"""
+
@functools.wraps(fun)
def wrapper(self):
try:
@@ -447,16 +491,29 @@ def memoize_when_activated(fun):
ret = self._cache[fun]
except AttributeError:
# case 2: we never entered oneshot() ctx
- return fun(self)
+ try:
+ return fun(self)
+ except Exception as err: # noqa: BLE001
+ raise raise_from(err, None)
except KeyError:
# case 3: we entered oneshot() ctx but there's no cache
# for this entry yet
- ret = self._cache[fun] = fun(self)
+ try:
+ ret = fun(self)
+ except Exception as err: # noqa: BLE001
+ raise raise_from(err, None)
+ try:
+ self._cache[fun] = ret
+ except AttributeError:
+ # multi-threading race condition, see:
+ # https://github.com/giampaolo/psutil/issues/1948
+ pass
return ret
def cache_activate(proc):
"""Activate cache. Expects a Process instance. Cache will be
- stored as a "_cache" instance attribute."""
+ stored as a "_cache" instance attribute.
+ """
proc._cache = {}
def cache_deactivate(proc):
@@ -474,7 +531,7 @@ def memoize_when_activated(fun):
def isfile_strict(path):
"""Same as os.path.isfile() but does not swallow EACCES / EPERM
exceptions, see:
- http://mail.python.org/pipermail/python-dev/2012-June/120787.html
+ http://mail.python.org/pipermail/python-dev/2012-June/120787.html.
"""
try:
st = os.stat(path)
@@ -488,8 +545,8 @@ def isfile_strict(path):
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
+ exceptions. See:
+ http://mail.python.org/pipermail/python-dev/2012-June/120787.html.
"""
try:
os.stat(path)
@@ -533,7 +590,7 @@ def parse_environ_block(data):
equal_pos = data.find("=", pos, next_pos)
if equal_pos > pos:
key = data[pos:equal_pos]
- value = data[equal_pos + 1:next_pos]
+ value = data[equal_pos + 1 : next_pos]
# Windows expects environment variables to be uppercase only
if WINDOWS_:
key = key.upper()
@@ -592,9 +649,12 @@ def deprecated_method(replacement):
"""A decorator which can be used to mark a method as deprecated
'replcement' is the method name which will be called instead.
"""
+
def outer(fun):
msg = "%s() is deprecated and will be removed; use %s() instead" % (
- fun.__name__, replacement)
+ fun.__name__,
+ replacement,
+ )
if fun.__doc__ is None:
fun.__doc__ = msg
@@ -602,7 +662,9 @@ def deprecated_method(replacement):
def inner(self, *args, **kwargs):
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
return getattr(self, replacement)(*args, **kwargs)
+
return inner
+
return outer
@@ -622,8 +684,8 @@ class _WrapNumbers:
assert name not in self.reminders
assert name not in self.reminder_keys
self.cache[name] = input_dict
- self.reminders[name] = defaultdict(int)
- self.reminder_keys[name] = defaultdict(set)
+ self.reminders[name] = collections.defaultdict(int)
+ self.reminder_keys[name] = collections.defaultdict(set)
def _remove_dead_reminders(self, input_dict, name):
"""In case the number of keys changed between calls (e.g. a
@@ -638,7 +700,7 @@ class _WrapNumbers:
def run(self, input_dict, name):
"""Cache dict and sum numbers which overflow and wrap.
- Return an updated copy of `input_dict`
+ Return an updated copy of `input_dict`.
"""
if name not in self.cache:
# This was the first call.
@@ -649,7 +711,7 @@ class _WrapNumbers:
old_dict = self.cache[name]
new_dict = {}
- for key in input_dict.keys():
+ for key in input_dict:
input_tuple = input_dict[key]
try:
old_tuple = old_dict[key]
@@ -707,27 +769,79 @@ wrap_numbers.cache_clear = _wn.cache_clear
wrap_numbers.cache_info = _wn.cache_info
-def open_binary(fname, **kwargs):
- return open(fname, "rb", **kwargs)
+# The read buffer size for open() builtin. This (also) dictates how
+# much data we read(2) when iterating over file lines as in:
+# >>> with open(file) as f:
+# ... for line in f:
+# ... ...
+# Default per-line buffer size for binary files is 1K. For text files
+# is 8K. We use a bigger buffer (32K) in order to have more consistent
+# results when reading /proc pseudo files on Linux, see:
+# https://github.com/giampaolo/psutil/issues/2050
+# On Python 2 this also speeds up the reading of big files:
+# (namely /proc/{pid}/smaps and /proc/net/*):
+# https://github.com/giampaolo/psutil/issues/708
+FILE_READ_BUFFER_SIZE = 32 * 1024
-def open_text(fname, **kwargs):
+def open_binary(fname):
+ return open(fname, "rb", buffering=FILE_READ_BUFFER_SIZE)
+
+
+def open_text(fname):
"""On Python 3 opens a file in text mode by using fs encoding and
a proper en/decoding errors handler.
On Python 2 this is just an alias for open(name, 'rt').
"""
- if PY3:
- # See:
- # https://github.com/giampaolo/psutil/issues/675
- # https://github.com/giampaolo/psutil/pull/733
- kwargs.setdefault('encoding', ENCODING)
- kwargs.setdefault('errors', ENCODING_ERRS)
- return open(fname, "rt", **kwargs)
+ if not PY3:
+ return open(fname, buffering=FILE_READ_BUFFER_SIZE)
+
+ # See:
+ # https://github.com/giampaolo/psutil/issues/675
+ # https://github.com/giampaolo/psutil/pull/733
+ fobj = open(
+ fname,
+ buffering=FILE_READ_BUFFER_SIZE,
+ encoding=ENCODING,
+ errors=ENCODING_ERRS,
+ )
+ try:
+ # Dictates per-line read(2) buffer size. Defaults is 8k. See:
+ # https://github.com/giampaolo/psutil/issues/2050#issuecomment-1013387546
+ fobj._CHUNK_SIZE = FILE_READ_BUFFER_SIZE
+ except AttributeError:
+ pass
+ except Exception:
+ fobj.close()
+ raise
+
+ return fobj
+
+
+def cat(fname, fallback=_DEFAULT, _open=open_text):
+ """Read entire file content and return it as a string. File is
+ opened in text mode. If specified, `fallback` is the value
+ returned in case of error, either if the file does not exist or
+ it can't be read().
+ """
+ if fallback is _DEFAULT:
+ with _open(fname) as f:
+ return f.read()
+ else:
+ try:
+ with _open(fname) as f:
+ return f.read()
+ except (IOError, OSError):
+ return fallback
+
+
+def bcat(fname, fallback=_DEFAULT):
+ """Same as above but opens file in binary mode."""
+ return cat(fname, fallback=fallback, _open=open_binary)
def bytes2human(n, format="%(value).1f%(symbol)s"):
- """Used by various scripts. See:
- http://goo.gl/zeJZl
+ """Used by various scripts. See: http://goo.gl/zeJZl.
>>> bytes2human(10000)
'9.8K'
@@ -739,7 +853,7 @@ def bytes2human(n, format="%(value).1f%(symbol)s"):
for i, s in enumerate(symbols[1:]):
prefix[s] = 1 << (i + 1) * 10
for symbol in reversed(symbols[1:]):
- if n >= prefix[symbol]:
+ if abs(n) >= prefix[symbol]:
value = float(n) / prefix[symbol]
return format % locals()
return format % dict(symbol=symbols[0], value=n)
@@ -751,9 +865,12 @@ def get_procfs_path():
if PY3:
+
def decode(s):
return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
+
else:
+
def decode(s):
return s
@@ -769,10 +886,11 @@ def term_supports_colors(file=sys.stdout): # pragma: no cover
return True
try:
import curses
+
assert file.isatty()
curses.setupterm()
assert curses.tigetnum("colors") > 0
- except Exception:
+ except Exception: # noqa: BLE001
return False
else:
return True
@@ -783,14 +901,24 @@ def hilite(s, color=None, bold=False): # pragma: no cover
if not term_supports_colors():
return s
attr = []
- colors = dict(green='32', red='91', brown='33', yellow='93', blue='34',
- violet='35', lightblue='36', grey='37', darkgrey='30')
+ colors = dict(
+ blue='34',
+ brown='33',
+ darkgrey='30',
+ green='32',
+ grey='37',
+ lightblue='36',
+ red='91',
+ violet='35',
+ yellow='93',
+ )
colors[None] = '29'
try:
color = colors[color]
except KeyError:
- raise ValueError("invalid color %r; choose between %s" % (
- list(colors.keys())))
+ raise ValueError(
+ "invalid color %r; choose between %s" % (list(colors.keys()))
+ )
attr.append(color)
if bold:
attr.append('1')
@@ -798,7 +926,8 @@ def hilite(s, color=None, bold=False): # pragma: no cover
def print_color(
- s, color=None, bold=False, file=sys.stdout): # pragma: no cover
+ s, color=None, bold=False, file=sys.stdout
+): # pragma: no cover
"""Print a colorized version of string."""
if not term_supports_colors():
print(s, file=file) # NOQA
@@ -809,16 +938,19 @@ def print_color(
DEFAULT_COLOR = 7
GetStdHandle = ctypes.windll.Kernel32.GetStdHandle
- SetConsoleTextAttribute = \
+ SetConsoleTextAttribute = (
ctypes.windll.Kernel32.SetConsoleTextAttribute
+ )
colors = dict(green=2, red=4, brown=6, yellow=6)
colors[None] = DEFAULT_COLOR
try:
color = colors[color]
except KeyError:
- raise ValueError("invalid color %r; choose between %r" % (
- color, list(colors.keys())))
+ raise ValueError(
+ "invalid color %r; choose between %r"
+ % (color, list(colors.keys()))
+ )
if bold and color <= 7:
color += 8
@@ -827,20 +959,25 @@ def print_color(
handle = GetStdHandle(handle_id)
SetConsoleTextAttribute(handle, color)
try:
- print(s, file=file) # NOQA
+ print(s, file=file) # NOQA
finally:
SetConsoleTextAttribute(handle, DEFAULT_COLOR)
-if bool(os.getenv('PSUTIL_DEBUG', 0)):
- import inspect
+def debug(msg):
+ """If PSUTIL_DEBUG env var is set, print a debug message to stderr."""
+ if PSUTIL_DEBUG:
+ import inspect
- def debug(msg):
- """If PSUTIL_DEBUG env var is set, print a debug message to stderr."""
- fname, lineno, func_name, lines, index = inspect.getframeinfo(
- inspect.currentframe().f_back)
- print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), # NOQA
- file=sys.stderr)
-else:
- def debug(msg):
- pass
+ fname, lineno, _, lines, index = inspect.getframeinfo(
+ inspect.currentframe().f_back
+ )
+ if isinstance(msg, Exception):
+ if isinstance(msg, (OSError, IOError, EnvironmentError)):
+ # ...because str(exc) may contain info about the file name
+ msg = "ignoring %s" % msg
+ else:
+ msg = "ignoring %r" % msg
+ print( # noqa
+ "psutil-debug [%s:%s]> %s" % (fname, lineno, msg), file=sys.stderr
+ )
diff --git a/contrib/python/psutil/py3/psutil/_compat.py b/contrib/python/psutil/py3/psutil/_compat.py
index 17f38485e9..3db56c6019 100644
--- a/contrib/python/psutil/py3/psutil/_compat.py
+++ b/contrib/python/psutil/py3/psutil/_compat.py
@@ -8,12 +8,15 @@ Python 3 way of doing things).
"""
import collections
+import contextlib
import errno
import functools
import os
import sys
import types
+
+# fmt: off
__all__ = [
# constants
"PY3",
@@ -25,12 +28,16 @@ __all__ = [
"lru_cache",
# shutil module
"which", "get_terminal_size",
+ # contextlib module
+ "redirect_stderr",
# python 3 exceptions
"FileNotFoundError", "PermissionError", "ProcessLookupError",
- "InterruptedError", "ChildProcessError", "FileExistsError"]
+ "InterruptedError", "ChildProcessError", "FileExistsError",
+]
+# fmt: on
-PY3 = sys.version_info[0] == 3
+PY3 = sys.version_info[0] >= 3
_SENTINEL = object()
if PY3:
@@ -45,6 +52,7 @@ if PY3:
def b(s):
return s.encode("latin-1")
+
else:
long = long
range = xrange
@@ -79,7 +87,8 @@ else:
# Get the function's first positional argument.
type_or_obj = f.f_locals[f.f_code.co_varnames[0]]
except (IndexError, KeyError):
- raise RuntimeError('super() used in a function with no args')
+ msg = 'super() used in a function with no args'
+ raise RuntimeError(msg)
try:
# Get the MRO so we can crawl it.
mro = type_or_obj.__mro__
@@ -87,7 +96,8 @@ else:
try:
mro = type_or_obj.__class__.__mro__
except AttributeError:
- raise RuntimeError('super() used in a non-newstyle class')
+ msg = 'super() used in a non-newstyle class'
+ raise RuntimeError(msg)
for type_ in mro:
# Find the class that owns the currently-executing method.
for meth in type_.__dict__.values():
@@ -114,7 +124,8 @@ else:
continue
break # found
else:
- raise RuntimeError('super() called outside a method')
+ msg = 'super() called outside a method'
+ raise RuntimeError(msg)
# Dispatch to builtin super().
if type_or_obj is not _SENTINEL:
@@ -140,7 +151,6 @@ else:
def _instance_checking_exception(base_exception=Exception):
def wrapped(instance_checker):
class TemporaryClass(base_exception):
-
def __init__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], TemporaryClass):
unwrap_me = args[0]
@@ -148,7 +158,9 @@ else:
if not attr.startswith('__'):
setattr(self, attr, getattr(unwrap_me, attr))
else:
- super(TemporaryClass, self).__init__(*args, **kwargs)
+ super(TemporaryClass, self).__init__( # noqa
+ *args, **kwargs
+ )
class __metaclass__(type):
def __instancecheck__(cls, inst):
@@ -174,8 +186,7 @@ else:
@_instance_checking_exception(EnvironmentError)
def PermissionError(inst):
- return getattr(inst, 'errno', _SENTINEL) in (
- errno.EACCES, errno.EPERM)
+ return getattr(inst, 'errno', _SENTINEL) in (errno.EACCES, errno.EPERM)
@_instance_checking_exception(EnvironmentError)
def InterruptedError(inst):
@@ -195,9 +206,11 @@ else:
except FileExistsError:
pass
except OSError:
- raise RuntimeError(
+ msg = (
"broken or incompatible Python implementation, see: "
- "https://github.com/giampaolo/psutil/issues/1659")
+ "https://github.com/giampaolo/psutil/issues/1659"
+ )
+ raise RuntimeError(msg)
# --- stdlib additions
@@ -215,10 +228,11 @@ except ImportError:
from dummy_threading import RLock
_CacheInfo = collections.namedtuple(
- "CacheInfo", ["hits", "misses", "maxsize", "currsize"])
+ "CacheInfo", ["hits", "misses", "maxsize", "currsize"]
+ )
class _HashedSeq(list):
- __slots__ = 'hashvalue'
+ __slots__ = ('hashvalue',)
def __init__(self, tup, hash=hash):
self[:] = tup
@@ -227,10 +241,17 @@ except ImportError:
def __hash__(self):
return self.hashvalue
- def _make_key(args, kwds, typed,
- kwd_mark=(object(), ),
- fasttypes=set((int, str, frozenset, type(None))),
- sorted=sorted, tuple=tuple, type=type, len=len):
+ def _make_key(
+ args,
+ kwds,
+ typed,
+ kwd_mark=(_SENTINEL,),
+ fasttypes=set((int, str, frozenset, type(None))), # noqa
+ sorted=sorted,
+ tuple=tuple,
+ type=type,
+ len=len,
+ ):
key = args
if kwds:
sorted_items = sorted(kwds.items())
@@ -247,10 +268,11 @@ except ImportError:
def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator, see:
- http://docs.python.org/3/library/functools.html#functools.lru_cache
+ http://docs.python.org/3/library/functools.html#functools.lru_cache.
"""
+
def decorating_function(user_function):
- cache = dict()
+ cache = {}
stats = [0, 0]
HITS, MISSES = 0, 1
make_key = _make_key
@@ -262,11 +284,14 @@ except ImportError:
nonlocal_root = [root]
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
if maxsize == 0:
+
def wrapper(*args, **kwds):
result = user_function(*args, **kwds)
stats[MISSES] += 1
return result
+
elif maxsize is None:
+
def wrapper(*args, **kwds):
key = make_key(args, kwds, typed)
result = cache_get(key, root)
@@ -277,7 +302,9 @@ except ImportError:
cache[key] = result
stats[MISSES] += 1
return result
+
else:
+
def wrapper(*args, **kwds):
if kwds or typed:
key = make_key(args, kwds, typed)
@@ -287,7 +314,7 @@ except ImportError:
try:
link = cache_get(key)
if link is not None:
- root, = nonlocal_root
+ (root,) = nonlocal_root
link_prev, link_next, key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
@@ -302,7 +329,7 @@ except ImportError:
result = user_function(*args, **kwds)
lock.acquire()
try:
- root, = nonlocal_root
+ (root,) = nonlocal_root
if key in cache:
pass
elif _len(cache) >= maxsize:
@@ -324,16 +351,17 @@ except ImportError:
return result
def cache_info():
- """Report cache statistics"""
+ """Report cache statistics."""
lock.acquire()
try:
- return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
- len(cache))
+ return _CacheInfo(
+ stats[HITS], stats[MISSES], maxsize, len(cache)
+ )
finally:
lock.release()
def cache_clear():
- """Clear the cache and cache statistics"""
+ """Clear the cache and cache statistics."""
lock.acquire()
try:
cache.clear()
@@ -355,6 +383,7 @@ except ImportError:
try:
from shutil import which
except ImportError:
+
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
"""Given a command, mode, and a PATH string, return the path which
conforms to the given mode on the PATH, or None if there is no such
@@ -364,9 +393,13 @@ except ImportError:
of os.environ.get("PATH"), or can be overridden with a custom search
path.
"""
+
def _access_check(fn, mode):
- return (os.path.exists(fn) and os.access(fn, mode) and
- not os.path.isdir(fn))
+ return (
+ os.path.exists(fn)
+ and os.access(fn, mode)
+ and not os.path.isdir(fn)
+ )
if os.path.dirname(cmd):
if _access_check(cmd, mode):
@@ -407,18 +440,44 @@ except ImportError:
try:
from shutil import get_terminal_size
except ImportError:
+
def get_terminal_size(fallback=(80, 24)):
try:
import fcntl
- import termios
import struct
+ import termios
except ImportError:
return fallback
else:
try:
# This should work on Linux.
res = struct.unpack(
- 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234'))
+ 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')
+ )
return (res[1], res[0])
- except Exception:
+ except Exception: # noqa: BLE001
return fallback
+
+
+# python 3.3
+try:
+ from subprocess import TimeoutExpired as SubprocessTimeoutExpired
+except ImportError:
+
+ class SubprocessTimeoutExpired(Exception):
+ pass
+
+
+# python 3.5
+try:
+ from contextlib import redirect_stderr
+except ImportError:
+
+ @contextlib.contextmanager
+ def redirect_stderr(new_target):
+ original = sys.stderr
+ try:
+ sys.stderr = new_target
+ yield new_target
+ finally:
+ sys.stderr = original
diff --git a/contrib/python/psutil/py3/psutil/_psaix.py b/contrib/python/psutil/py3/psutil/_psaix.py
index 7160ecd63a..7310ab6c3d 100644
--- a/contrib/python/psutil/py3/psutil/_psaix.py
+++ b/contrib/python/psutil/py3/psutil/_psaix.py
@@ -18,20 +18,20 @@ from . import _common
from . import _psposix
from . import _psutil_aix as cext
from . import _psutil_posix as cext_posix
-from ._common import AccessDenied
-from ._common import conn_to_ntuple
-from ._common import get_procfs_path
-from ._common import memoize_when_activated
from ._common import NIC_DUPLEX_FULL
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import AccessDenied
from ._common import NoSuchProcess
-from ._common import usage_percent
from ._common import ZombieProcess
+from ._common import conn_to_ntuple
+from ._common import get_procfs_path
+from ._common import memoize_when_activated
+from ._common import usage_percent
+from ._compat import PY3
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
-from ._compat import PY3
__extra__all__ = ["PROCFS_PATH"]
@@ -53,7 +53,7 @@ PROC_STATUSES = {
cext.SIDL: _common.STATUS_IDLE,
cext.SZOMB: _common.STATUS_ZOMBIE,
cext.SACTIVE: _common.STATUS_RUNNING,
- cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this?
+ cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this?
cext.SSTOP: _common.STATUS_STOPPED,
}
@@ -80,7 +80,8 @@ proc_info_map = dict(
nice=4,
num_threads=5,
status=6,
- ttynr=7)
+ ttynr=7,
+)
# =====================================================================
@@ -123,13 +124,13 @@ def swap_memory():
def cpu_times():
- """Return system-wide CPU times as a named tuple"""
+ """Return system-wide CPU times as a named tuple."""
ret = cext.per_cpu_times()
return scputimes(*[sum(x) for x in zip(*ret)])
def per_cpu_times():
- """Return system per-CPU times as a list of named tuples"""
+ """Return system per-CPU times as a list of named tuples."""
ret = cext.per_cpu_times()
return [scputimes(*x) for x in ret]
@@ -143,14 +144,14 @@ def cpu_count_logical():
return None
-def cpu_count_physical():
- cmd = "lsdev -Cc processor"
- p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+def cpu_count_cores():
+ cmd = ["lsdev", "-Cc", "processor"]
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if PY3:
- stdout, stderr = [x.decode(sys.stdout.encoding)
- for x in (stdout, stderr)]
+ stdout, stderr = (
+ x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+ )
if p.returncode != 0:
raise RuntimeError("%r command error\n%s" % (cmd, stderr))
processors = stdout.strip().splitlines()
@@ -161,7 +162,8 @@ def cpu_stats():
"""Return various CPU stats as a named tuple."""
ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()
return _common.scpustats(
- ctx_switches, interrupts, soft_interrupts, syscalls)
+ ctx_switches, interrupts, soft_interrupts, syscalls
+ )
# =====================================================================
@@ -190,8 +192,9 @@ def disk_partitions(all=False):
if not disk_usage(mountpoint).total:
continue
maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
+ ntuple = _common.sdiskpart(
+ device, mountpoint, fstype, opts, maxfile, maxpath
+ )
retlist.append(ntuple)
return retlist
@@ -213,8 +216,10 @@ def net_connections(kind, _pid=-1):
"""
cmap = _common.conn_tmap
if kind not in cmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in cmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in cmap]))
+ )
families, types = _common.conn_tmap[kind]
rawlist = cext.net_connections(_pid)
ret = []
@@ -224,41 +229,56 @@ def net_connections(kind, _pid=-1):
continue
if type_ not in types:
continue
- nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status,
- TCP_STATUSES, pid=pid if _pid == -1 else None)
+ nt = conn_to_ntuple(
+ fd,
+ fam,
+ type_,
+ laddr,
+ raddr,
+ status,
+ TCP_STATUSES,
+ pid=pid if _pid == -1 else None,
+ )
ret.append(nt)
return ret
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
- duplex_map = {"Full": NIC_DUPLEX_FULL,
- "Half": NIC_DUPLEX_HALF}
+ duplex_map = {"Full": NIC_DUPLEX_FULL, "Half": NIC_DUPLEX_HALF}
names = set([x[0] for x in net_if_addrs()])
ret = {}
for name in names:
- isup, mtu = cext.net_if_stats(name)
+ mtu = cext_posix.net_if_mtu(name)
+ flags = cext_posix.net_if_flags(name)
# try to get speed and duplex
# TODO: rewrite this in C (entstat forks, so use truss -f to follow.
# looks like it is using an undocumented ioctl?)
duplex = ""
speed = 0
- p = subprocess.Popen(["/usr/bin/entstat", "-d", name],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p = subprocess.Popen(
+ ["/usr/bin/entstat", "-d", name],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
stdout, stderr = p.communicate()
if PY3:
- stdout, stderr = [x.decode(sys.stdout.encoding)
- for x in (stdout, stderr)]
+ stdout, stderr = (
+ x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+ )
if p.returncode == 0:
re_result = re.search(
- r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout)
+ r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout
+ )
if re_result is not None:
speed = int(re_result.group(1))
duplex = re_result.group(2)
+ output_flags = ','.join(flags)
+ isup = 'running' in flags
duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu, output_flags)
return ret
@@ -310,6 +330,7 @@ def wrap_exceptions(fun):
"""Call callable into a try/except clause and translate ENOENT,
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
@@ -324,10 +345,11 @@ def wrap_exceptions(fun):
raise ZombieProcess(self.pid, self._name, self._ppid)
except PermissionError:
raise AccessDenied(self.pid, self._name)
+
return wrapper
-class Process(object):
+class Process:
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
@@ -376,17 +398,20 @@ class Process(object):
if not os.path.isabs(exe):
# if cwd has changed, we're out of luck - this may be wrong!
exe = os.path.abspath(os.path.join(self.cwd(), exe))
- if (os.path.isabs(exe) and
- os.path.isfile(exe) and
- os.access(exe, os.X_OK)):
+ if (
+ os.path.isabs(exe)
+ and os.path.isfile(exe)
+ and os.access(exe, os.X_OK)
+ ):
return exe
# not found, move to search in PATH using basename only
exe = os.path.basename(exe)
# search for exe name PATH
for path in os.environ["PATH"].split(":"):
possible_exe = os.path.abspath(os.path.join(path, exe))
- if (os.path.isfile(possible_exe) and
- os.access(possible_exe, os.X_OK)):
+ if os.path.isfile(possible_exe) and os.access(
+ possible_exe, os.X_OK
+ ):
return possible_exe
return ''
@@ -407,6 +432,7 @@ class Process(object):
return self._proc_basic_info()[proc_info_map['num_threads']]
if HAS_THREADS:
+
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
@@ -462,14 +488,14 @@ class Process(object):
@wrap_exceptions
def cpu_times(self):
- cpu_times = cext.proc_cpu_times(self.pid, self._procfs_path)
- return _common.pcputimes(*cpu_times)
+ t = cext.proc_cpu_times(self.pid, self._procfs_path)
+ return _common.pcputimes(*t)
@wrap_exceptions
def terminal(self):
ttydev = self._proc_basic_info()[proc_info_map['ttynr']]
# convert from 64-bit dev_t to 32-bit dev_t and then map the device
- ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
+ ttydev = ((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF)
# try to match rdev of /dev/pts/* files ttydev
for dev in glob.glob("/dev/**/*"):
if os.stat(dev).st_rdev == ttydev:
@@ -484,7 +510,7 @@ class Process(object):
return result.rstrip('/')
except FileNotFoundError:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
- return None
+ return ""
@wrap_exceptions
def memory_info(self):
@@ -504,12 +530,16 @@ class Process(object):
def open_files(self):
# TODO rewrite without using procfiles (stat /proc/pid/fd/* and then
# find matching name of the inode)
- p = subprocess.Popen(["/usr/bin/procfiles", "-n", str(self.pid)],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ p = subprocess.Popen(
+ ["/usr/bin/procfiles", "-n", str(self.pid)],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
stdout, stderr = p.communicate()
if PY3:
- stdout, stderr = [x.decode(sys.stdout.encoding)
- for x in (stdout, stderr)]
+ stdout, stderr = (
+ x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+ )
if "no such process" in stderr.lower():
raise NoSuchProcess(self.pid, self._name)
procfiles = re.findall(r"(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout)
@@ -525,20 +555,20 @@ class Process(object):
@wrap_exceptions
def num_fds(self):
- if self.pid == 0: # no /proc/0/fd
+ if self.pid == 0: # no /proc/0/fd
return 0
return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
@wrap_exceptions
def num_ctx_switches(self):
- return _common.pctxsw(
- *cext.proc_num_ctx_switches(self.pid))
+ return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def wait(self, timeout=None):
return _psposix.wait_pid(self.pid, timeout, self._name)
if HAS_PROC_IO_COUNTERS:
+
@wrap_exceptions
def io_counters(self):
try:
diff --git a/contrib/python/psutil/py3/psutil/_psbsd.py b/contrib/python/psutil/py3/psutil/_psbsd.py
index 764463e980..da68f5efd5 100644
--- a/contrib/python/psutil/py3/psutil/_psbsd.py
+++ b/contrib/python/psutil/py3/psutil/_psbsd.py
@@ -8,25 +8,26 @@ import contextlib
import errno
import functools
import os
-import xml.etree.ElementTree as ET
-from collections import namedtuple
from collections import defaultdict
+from collections import namedtuple
+from xml.etree import ElementTree
from . import _common
from . import _psposix
from . import _psutil_bsd as cext
from . import _psutil_posix as cext_posix
+from ._common import FREEBSD
+from ._common import NETBSD
+from ._common import OPENBSD
from ._common import AccessDenied
+from ._common import NoSuchProcess
+from ._common import ZombieProcess
from ._common import conn_tmap
from ._common import conn_to_ntuple
-from ._common import FREEBSD
+from ._common import debug
from ._common import memoize
from ._common import memoize_when_activated
-from ._common import NETBSD
-from ._common import NoSuchProcess
-from ._common import OPENBSD
from ._common import usage_percent
-from ._common import ZombieProcess
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
@@ -140,6 +141,7 @@ kinfo_proc_map = dict(
# =====================================================================
+# fmt: off
# psutil.virtual_memory()
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
@@ -169,6 +171,7 @@ if FREEBSD:
else:
sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
'read_bytes', 'write_bytes'])
+# fmt: on
# =====================================================================
@@ -177,10 +180,9 @@ else:
def virtual_memory():
- """System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
- total, free, active, inactive, wired, cached, buffers, shared = mem
if NETBSD:
+ total, free, active, inactive, wired, cached = mem
# On NetBSD buffers and shared mem is determined via /proc.
# The C ext set them to 0.
with open('/proc/meminfo', 'rb') as f:
@@ -189,11 +191,39 @@ def virtual_memory():
buffers = int(line.split()[1]) * 1024
elif line.startswith(b'MemShared:'):
shared = int(line.split()[1]) * 1024
- avail = inactive + cached + free
- used = active + wired + cached
+ # Before avail was calculated as (inactive + cached + free),
+ # same as zabbix, but it turned out it could exceed total (see
+ # #2233), so zabbix seems to be wrong. Htop calculates it
+ # differently, and the used value seem more realistic, so let's
+ # match htop.
+ # https://github.com/htop-dev/htop/blob/e7f447b/netbsd/NetBSDProcessList.c#L162 # noqa
+ # https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/netbsd/memory.c#L135 # noqa
+ used = active + wired
+ avail = total - used
+ else:
+ total, free, active, inactive, wired, cached, buffers, shared = mem
+ # matches freebsd-memory CLI:
+ # * https://people.freebsd.org/~rse/dist/freebsd-memory
+ # * https://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt
+ # matches zabbix:
+ # * https://github.com/zabbix/zabbix/blob/af5e0f8/src/libs/zbxsysinfo/freebsd/memory.c#L143 # noqa
+ avail = inactive + cached + free
+ used = active + wired + cached
+
percent = usage_percent((total - avail), total, round_=1)
- return svmem(total, avail, percent, used, free,
- active, inactive, buffers, cached, shared, wired)
+ return svmem(
+ total,
+ avail,
+ percent,
+ used,
+ free,
+ active,
+ inactive,
+ buffers,
+ cached,
+ shared,
+ wired,
+ )
def swap_memory():
@@ -209,20 +239,22 @@ def swap_memory():
def cpu_times():
- """Return system per-CPU times as a namedtuple"""
+ """Return system per-CPU times as a namedtuple."""
user, nice, system, idle, irq = cext.cpu_times()
return scputimes(user, nice, system, idle, irq)
if HAS_PER_CPU_TIMES:
+
def per_cpu_times():
- """Return system CPU times as a namedtuple"""
+ """Return system CPU times as a namedtuple."""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle, irq = cpu_t
item = scputimes(user, nice, system, idle, irq)
ret.append(item)
return ret
+
else:
# XXX
# Ok, this is very dirty.
@@ -232,11 +264,12 @@ else:
# crash at psutil import time.
# Next calls will fail with NotImplementedError
def per_cpu_times():
- """Return system CPU times as a namedtuple"""
+ """Return system CPU times as a namedtuple."""
if cpu_count_logical() == 1:
return [cpu_times()]
if per_cpu_times.__called__:
- raise NotImplementedError("supported only starting from FreeBSD 8")
+ msg = "supported only starting from FreeBSD 8"
+ raise NotImplementedError(msg)
per_cpu_times.__called__ = True
return [cpu_times()]
@@ -249,33 +282,35 @@ def cpu_count_logical():
if OPENBSD or NETBSD:
- def cpu_count_physical():
+
+ def cpu_count_cores():
# OpenBSD and NetBSD do not implement this.
return 1 if cpu_count_logical() == 1 else None
+
else:
- def cpu_count_physical():
- """Return the number of physical CPUs in the system."""
+
+ def cpu_count_cores():
+ """Return the number of CPU cores in the system."""
# From the C module we'll get an XML string similar to this:
# http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
# We may get None in case "sysctl kern.sched.topology_spec"
# is not supported on this BSD version, in which case we'll mimic
# os.cpu_count() and return None.
ret = None
- s = cext.cpu_count_phys()
+ s = cext.cpu_topology()
if s is not None:
# get rid of padding chars appended at the end of the string
index = s.rfind("</groups>")
if index != -1:
- s = s[:index + 9]
- root = ET.fromstring(s)
+ s = s[: index + 9]
+ root = ElementTree.fromstring(s)
try:
ret = len(root.findall('group/children/group/cpu')) or None
finally:
# needed otherwise it will memleak
root.clear()
if not ret:
- # If logical CPUs are 1 it's obvious we'll have only 1
- # physical CPU.
+ # If logical CPUs == 1 it's obvious we' have only 1 core.
if cpu_count_logical() == 1:
return 1
return ret
@@ -297,8 +332,9 @@ def cpu_stats():
#
# Note: the C ext is returning some metrics we are not exposing:
# traps, faults and forks.
- ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
+ ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = (
cext.cpu_stats()
+ )
with open('/proc/stat', 'rb') as f:
for line in f:
if line.startswith(b'intr'):
@@ -306,11 +342,45 @@ def cpu_stats():
elif OPENBSD:
# Note: the C ext is returning some metrics we are not exposing:
# traps, faults and forks.
- ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \
+ ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = (
cext.cpu_stats()
+ )
return _common.scpustats(ctxsw, intrs, soft_intrs, syscalls)
+if FREEBSD:
+
+ def cpu_freq():
+ """Return frequency metrics for CPUs. As of Dec 2018 only
+ CPU 0 appears to be supported by FreeBSD and all other cores
+ match the frequency of CPU 0.
+ """
+ ret = []
+ num_cpus = cpu_count_logical()
+ for cpu in range(num_cpus):
+ try:
+ current, available_freq = cext.cpu_freq(cpu)
+ except NotImplementedError:
+ continue
+ if available_freq:
+ try:
+ min_freq = int(available_freq.split(" ")[-1].split("/")[0])
+ except (IndexError, ValueError):
+ min_freq = None
+ try:
+ max_freq = int(available_freq.split(" ")[0].split("/")[0])
+ except (IndexError, ValueError):
+ max_freq = None
+ ret.append(_common.scpufreq(current, min_freq, max_freq))
+ return ret
+
+elif OPENBSD:
+
+ def cpu_freq():
+ curr = float(cext.cpu_freq())
+ return [_common.scpufreq(curr, 0.0, 0.0)]
+
+
# =====================================================================
# --- disks
# =====================================================================
@@ -319,15 +389,16 @@ def cpu_stats():
def disk_partitions(all=False):
"""Return mounted disk partitions as a list of namedtuples.
'all' argument is ignored, see:
- https://github.com/giampaolo/psutil/issues/906
+ https://github.com/giampaolo/psutil/issues/906.
"""
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
+ ntuple = _common.sdiskpart(
+ device, mountpoint, fstype, opts, maxfile, maxpath
+ )
retlist.append(ntuple)
return retlist
@@ -352,7 +423,7 @@ def net_if_stats():
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_is_running(name)
+ flags = cext_posix.net_if_flags(name)
duplex, speed = cext_posix.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
@@ -361,42 +432,37 @@ def net_if_stats():
else:
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ output_flags = ','.join(flags)
+ isup = 'running' in flags
+ ret[name] = _common.snicstats(
+ isup, duplex, speed, mtu, output_flags
+ )
return ret
def net_connections(kind):
"""System-wide network connections."""
- if OPENBSD:
- ret = []
- for pid in pids():
- try:
- cons = Process(pid).connections(kind)
- except (NoSuchProcess, ZombieProcess):
- continue
- else:
- for conn in cons:
- conn = list(conn)
- conn.append(pid)
- ret.append(_common.sconn(*conn))
- return ret
-
if kind not in _common.conn_tmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in conn_tmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in conn_tmap]))
+ )
families, types = conn_tmap[kind]
ret = set()
- if NETBSD:
- rawlist = cext.net_connections(-1)
- else:
- rawlist = cext.net_connections()
+
+ if OPENBSD:
+ rawlist = cext.net_connections(-1, families, types)
+ elif NETBSD:
+ rawlist = cext.net_connections(-1, kind)
+ else: # FreeBSD
+ rawlist = cext.net_connections(families, types)
+
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
- # TODO: apply filter at C level
- if fam in families and type in types:
- nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
- TCP_STATUSES, pid)
- ret.add(nt)
+ nt = conn_to_ntuple(
+ fd, fam, type, laddr, raddr, status, TCP_STATUSES, pid
+ )
+ ret.add(nt)
return list(ret)
@@ -424,7 +490,7 @@ if FREEBSD:
return _common.sbattery(percent, secsleft, power_plugged)
def sensors_temperatures():
- "Return CPU cores temperatures if available, else an empty dict."
+ """Return CPU cores temperatures if available, else an empty dict."""
ret = defaultdict(list)
num_cpus = cpu_count_logical()
for cpu in range(num_cpus):
@@ -434,36 +500,13 @@ if FREEBSD:
high = None
name = "Core %s" % cpu
ret["coretemp"].append(
- _common.shwtemp(name, current, high, high))
+ _common.shwtemp(name, current, high, high)
+ )
except NotImplementedError:
pass
return ret
- def cpu_freq():
- """Return frequency metrics for CPUs. As of Dec 2018 only
- CPU 0 appears to be supported by FreeBSD and all other cores
- match the frequency of CPU 0.
- """
- ret = []
- num_cpus = cpu_count_logical()
- for cpu in range(num_cpus):
- try:
- current, available_freq = cext.cpu_frequency(cpu)
- except NotImplementedError:
- continue
- if available_freq:
- try:
- min_freq = int(available_freq.split(" ")[-1].split("/")[0])
- except(IndexError, ValueError):
- min_freq = None
- try:
- max_freq = int(available_freq.split(" ")[0].split("/")[0])
- except(IndexError, ValueError):
- max_freq = None
- ret.append(_common.scpufreq(current, min_freq, max_freq))
- return ret
-
# =====================================================================
# --- other system functions
@@ -519,6 +562,7 @@ def pids():
if OPENBSD or NETBSD:
+
def pid_exists(pid):
"""Return True if pid exists."""
exists = _psposix.pid_exists(pid)
@@ -528,6 +572,7 @@ if OPENBSD or NETBSD:
return pid in pids()
else:
return True
+
else:
pid_exists = _psposix.pid_exists
@@ -535,8 +580,8 @@ else:
def is_zombie(pid):
try:
st = cext.proc_oneshot_info(pid)[kinfo_proc_map['status']]
- return st == cext.SZOMB
- except Exception:
+ return PROC_STATUSES.get(st) == _common.STATUS_ZOMBIE
+ except OSError:
return False
@@ -544,6 +589,7 @@ def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
@@ -562,6 +608,7 @@ def wrap_exceptions(fun):
else:
raise
raise
+
return wrapper
@@ -582,7 +629,7 @@ def wrap_exceptions_procfs(inst):
raise AccessDenied(inst.pid, inst._name)
-class Process(object):
+class Process:
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid", "_cache"]
@@ -646,10 +693,10 @@ class Process(object):
if OPENBSD and self.pid == 0:
return [] # ...else it crashes
elif NETBSD:
- # XXX - most of the times the underlying sysctl() call on Net
- # and Open BSD returns a truncated string.
- # Also /proc/pid/cmdline behaves the same so it looks
- # like this is a kernel bug.
+ # XXX - most of the times the underlying sysctl() call on
+ # NetBSD and OpenBSD returns a truncated string. Also
+ # /proc/pid/cmdline behaves the same so it looks like this
+ # is a kernel bug.
try:
return cext.proc_cmdline(self.pid)
except OSError as err:
@@ -661,6 +708,7 @@ class Process(object):
else:
# XXX: this happens with unicode tests. It means the C
# routine is unable to decode invalid unicode chars.
+ debug("ignoring %r and returning an empty list" % err)
return []
else:
raise
@@ -691,7 +739,8 @@ class Process(object):
return _common.puids(
rawtuple[kinfo_proc_map['real_uid']],
rawtuple[kinfo_proc_map['effective_uid']],
- rawtuple[kinfo_proc_map['saved_uid']])
+ rawtuple[kinfo_proc_map['saved_uid']],
+ )
@wrap_exceptions
def gids(self):
@@ -699,7 +748,8 @@ class Process(object):
return _common.pgids(
rawtuple[kinfo_proc_map['real_gid']],
rawtuple[kinfo_proc_map['effective_gid']],
- rawtuple[kinfo_proc_map['saved_gid']])
+ rawtuple[kinfo_proc_map['saved_gid']],
+ )
@wrap_exceptions
def cpu_times(self):
@@ -708,9 +758,11 @@ class Process(object):
rawtuple[kinfo_proc_map['user_time']],
rawtuple[kinfo_proc_map['sys_time']],
rawtuple[kinfo_proc_map['ch_user_time']],
- rawtuple[kinfo_proc_map['ch_sys_time']])
+ rawtuple[kinfo_proc_map['ch_sys_time']],
+ )
if FREEBSD:
+
@wrap_exceptions
def cpu_num(self):
return self.oneshot()[kinfo_proc_map['cpunum']]
@@ -723,7 +775,8 @@ class Process(object):
rawtuple[kinfo_proc_map['vms']],
rawtuple[kinfo_proc_map['memtext']],
rawtuple[kinfo_proc_map['memdata']],
- rawtuple[kinfo_proc_map['memstack']])
+ rawtuple[kinfo_proc_map['memstack']],
+ )
memory_full_info = memory_info
@@ -744,7 +797,8 @@ class Process(object):
rawtuple = self.oneshot()
return _common.pctxsw(
rawtuple[kinfo_proc_map['ctx_switches_vol']],
- rawtuple[kinfo_proc_map['ctx_switches_unvol']])
+ rawtuple[kinfo_proc_map['ctx_switches_unvol']],
+ )
@wrap_exceptions
def threads(self):
@@ -761,35 +815,31 @@ class Process(object):
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in conn_tmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in conn_tmap]))
+ )
+ families, types = conn_tmap[kind]
+ ret = []
if NETBSD:
- families, types = conn_tmap[kind]
- ret = []
- rawlist = cext.net_connections(self.pid)
- for item in rawlist:
- fd, fam, type, laddr, raddr, status, pid = item
- assert pid == self.pid
- if fam in families and type in types:
- nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
- TCP_STATUSES)
- ret.append(nt)
- self._assert_alive()
- return list(ret)
+ rawlist = cext.net_connections(self.pid, kind)
+ elif OPENBSD:
+ rawlist = cext.net_connections(self.pid, families, types)
+ else:
+ rawlist = cext.proc_connections(self.pid, families, types)
- families, types = conn_tmap[kind]
- rawlist = cext.proc_connections(self.pid, families, types)
- ret = []
for item in rawlist:
- fd, fam, type, laddr, raddr, status = item
- nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
- TCP_STATUSES)
+ fd, fam, type, laddr, raddr, status = item[:6]
+ if FREEBSD:
+ if (fam not in families) or (type not in types):
+ continue
+ nt = conn_to_ntuple(
+ fd, fam, type, laddr, raddr, status, TCP_STATUSES
+ )
ret.append(nt)
- if OPENBSD:
- self._assert_alive()
-
+ self._assert_alive()
return ret
@wrap_exceptions
@@ -817,7 +867,8 @@ class Process(object):
rawtuple[kinfo_proc_map['read_io_count']],
rawtuple[kinfo_proc_map['write_io_count']],
-1,
- -1)
+ -1,
+ )
@wrap_exceptions
def cwd(self):
@@ -825,20 +876,22 @@ class Process(object):
# sometimes we get an empty string, in which case we turn
# it into None
if OPENBSD and self.pid == 0:
- return None # ...else it would raise EINVAL
+ return "" # ...else it would raise EINVAL
elif NETBSD or HAS_PROC_OPEN_FILES:
# FreeBSD < 8 does not support functions based on
# kinfo_getfile() and kinfo_getvmmap()
- return cext.proc_cwd(self.pid) or None
+ return cext.proc_cwd(self.pid)
else:
raise NotImplementedError(
- "supported only starting from FreeBSD 8" if
- FREEBSD else "")
+ "supported only starting from FreeBSD 8" if FREEBSD else ""
+ )
nt_mmap_grouped = namedtuple(
- 'mmap', 'path rss, private, ref_count, shadow_count')
+ 'mmap', 'path rss, private, ref_count, shadow_count'
+ )
nt_mmap_ext = namedtuple(
- 'mmap', 'addr, perms path rss, private, ref_count, shadow_count')
+ 'mmap', 'addr, perms path rss, private, ref_count, shadow_count'
+ )
def _not_implemented(self):
raise NotImplementedError
@@ -846,17 +899,20 @@ class Process(object):
# FreeBSD < 8 does not support functions based on kinfo_getfile()
# and kinfo_getvmmap()
if HAS_PROC_OPEN_FILES:
+
@wrap_exceptions
def open_files(self):
"""Return files opened by process as a list of namedtuples."""
rawlist = cext.proc_open_files(self.pid)
return [_common.popenfile(path, fd) for path, fd in rawlist]
+
else:
open_files = _not_implemented
# FreeBSD < 8 does not support functions based on kinfo_getfile()
# and kinfo_getvmmap()
if HAS_PROC_NUM_FDS:
+
@wrap_exceptions
def num_fds(self):
"""Return the number of file descriptors opened by this process."""
@@ -864,6 +920,7 @@ class Process(object):
if NETBSD:
self._assert_alive()
return ret
+
else:
num_fds = _not_implemented
@@ -883,8 +940,9 @@ class Process(object):
allcpus = tuple(range(len(per_cpu_times())))
for cpu in cpus:
if cpu not in allcpus:
- raise ValueError("invalid CPU #%i (choose between %s)"
- % (cpu, allcpus))
+ raise ValueError(
+ "invalid CPU #%i (choose between %s)" % (cpu, allcpus)
+ )
try:
cext.proc_cpu_affinity_set(self.pid, cpus)
except OSError as err:
@@ -896,8 +954,9 @@ class Process(object):
for cpu in cpus:
if cpu not in allcpus:
raise ValueError(
- "invalid CPU #%i (choose between %s)" % (
- cpu, allcpus))
+ "invalid CPU #%i (choose between %s)"
+ % (cpu, allcpus)
+ )
raise
@wrap_exceptions
@@ -911,7 +970,8 @@ class Process(object):
else:
if len(limits) != 2:
raise ValueError(
- "second argument must be a (soft, hard) tuple, "
- "got %s" % repr(limits))
+ "second argument must be a (soft, hard) tuple, got %s"
+ % repr(limits)
+ )
soft, hard = limits
return cext.proc_setrlimit(self.pid, resource, soft, hard)
diff --git a/contrib/python/psutil/py3/psutil/_pslinux.py b/contrib/python/psutil/py3/psutil/_pslinux.py
index c1dc52dd5f..bab70b5d20 100644
--- a/contrib/python/psutil/py3/psutil/_pslinux.py
+++ b/contrib/python/psutil/py3/psutil/_pslinux.py
@@ -16,7 +16,6 @@ import re
import socket
import struct
import sys
-import traceback
import warnings
from collections import defaultdict
from collections import namedtuple
@@ -25,37 +24,41 @@ from . import _common
from . import _psposix
from . import _psutil_linux as cext
from . import _psutil_posix as cext_posix
+from ._common import NIC_DUPLEX_FULL
+from ._common import NIC_DUPLEX_HALF
+from ._common import NIC_DUPLEX_UNKNOWN
from ._common import AccessDenied
+from ._common import NoSuchProcess
+from ._common import ZombieProcess
+from ._common import bcat
+from ._common import cat
from ._common import debug
from ._common import decode
from ._common import get_procfs_path
from ._common import isfile_strict
from ._common import memoize
from ._common import memoize_when_activated
-from ._common import NIC_DUPLEX_FULL
-from ._common import NIC_DUPLEX_HALF
-from ._common import NIC_DUPLEX_UNKNOWN
-from ._common import NoSuchProcess
from ._common import open_binary
from ._common import open_text
from ._common import parse_environ_block
from ._common import path_exists_strict
from ._common import supports_ipv6
from ._common import usage_percent
-from ._common import ZombieProcess
-from ._compat import b
-from ._compat import basestring
+from ._compat import PY3
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
-from ._compat import PY3
+from ._compat import b
+from ._compat import basestring
+
-if sys.version_info >= (3, 4):
+if PY3:
import enum
else:
enum = None
+# fmt: off
__extra__all__ = [
#
'PROCFS_PATH',
@@ -65,7 +68,9 @@ __extra__all__ = [
# connection status constants
"CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
"CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
- "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ]
+ "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING",
+]
+# fmt: on
# =====================================================================
@@ -74,19 +79,15 @@ __extra__all__ = [
POWER_SUPPLY_PATH = "/sys/class/power_supply"
-HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
+HAS_PROC_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
+HAS_PROC_SMAPS_ROLLUP = os.path.exists('/proc/%s/smaps_rollup' % os.getpid())
HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_ioprio_get")
HAS_CPU_AFFINITY = hasattr(cext, "proc_cpu_affinity_get")
-_DEFAULT = object()
# Number of clock ticks per second
CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
PAGESIZE = cext_posix.getpagesize()
BOOT_TIME = None # set later
-# Used when reading "big" files, namely /proc/{pid}/smaps and /proc/net/*.
-# On Python 2, using a buffer with open() for such files may result in a
-# speedup, see: https://github.com/giampaolo/psutil/issues/708
-BIGFILE_BUFFERING = -1 if PY3 else 8192
LITTLE_ENDIAN = sys.byteorder == 'little'
# "man iostat" states that sectors are equivalent with blocks and have
@@ -105,8 +106,9 @@ DISK_SECTOR_SIZE = 512
if enum is None:
AF_LINK = socket.AF_PACKET
else:
- AddressFamily = enum.IntEnum('AddressFamily',
- {'AF_LINK': int(socket.AF_PACKET)})
+ AddressFamily = enum.IntEnum(
+ 'AddressFamily', {'AF_LINK': int(socket.AF_PACKET)}
+ )
AF_LINK = AddressFamily.AF_LINK
# ioprio_* constants http://linux.die.net/man/2/ioprio_get
@@ -116,6 +118,7 @@ if enum is None:
IOPRIO_CLASS_BE = 2
IOPRIO_CLASS_IDLE = 3
else:
+
class IOPriority(enum.IntEnum):
IOPRIO_CLASS_NONE = 0
IOPRIO_CLASS_RT = 1
@@ -155,7 +158,7 @@ TCP_STATUSES = {
"08": _common.CONN_CLOSE_WAIT,
"09": _common.CONN_LAST_ACK,
"0A": _common.CONN_LISTEN,
- "0B": _common.CONN_CLOSING
+ "0B": _common.CONN_CLOSING,
}
@@ -164,6 +167,7 @@ TCP_STATUSES = {
# =====================================================================
+# fmt: off
# psutil.virtual_memory()
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
@@ -198,6 +202,7 @@ pio = namedtuple('pio', ['read_count', 'write_count',
pcputimes = namedtuple('pcputimes',
['user', 'system', 'children_user', 'children_system',
'iowait'])
+# fmt: on
# =====================================================================
@@ -244,7 +249,7 @@ def is_storage_device(name):
"nvme0n1p1"). If name is a virtual device (e.g. "loop1", "ram")
return True.
"""
- # Readapted from iostat source code, see:
+ # Re-adapted from iostat source code, see:
# https://github.com/sysstat/sysstat/blob/
# 97912938cd476645b267280069e83b1c8dc0e1c7/common.c#L208
# Some devices may have a slash in their name (e.g. cciss/c0d0...).
@@ -282,27 +287,11 @@ def set_scputimes_ntuple(procfs_path):
scputimes = namedtuple('scputimes', fields)
-def cat(fname, fallback=_DEFAULT, binary=True):
- """Return file content.
- fallback: the value returned in case the file does not exist or
- cannot be read
- binary: whether to open the file in binary or text mode.
- """
- try:
- with open_binary(fname) if binary else open_text(fname) as f:
- return f.read().strip()
- except (IOError, OSError):
- if fallback is not _DEFAULT:
- return fallback
- else:
- raise
-
-
try:
set_scputimes_ntuple("/proc")
-except Exception: # pragma: no cover
+except Exception as err: # noqa: BLE001
# Don't want to crash at import time.
- traceback.print_exc()
+ debug("ignoring exception on import: %r" % err)
scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0)
@@ -331,8 +320,10 @@ except ImportError:
def prlimit(pid, resource_, limits=None):
class StructRlimit(ctypes.Structure):
- _fields_ = [('rlim_cur', ctypes.c_longlong),
- ('rlim_max', ctypes.c_longlong)]
+ _fields_ = [
+ ('rlim_cur', ctypes.c_longlong),
+ ('rlim_max', ctypes.c_longlong),
+ ]
current = StructRlimit()
if limits is None:
@@ -344,17 +335,19 @@ except ImportError:
new.rlim_cur = limits[0]
new.rlim_max = limits[1]
ret = libc.prlimit(
- pid, resource_, ctypes.byref(new), ctypes.byref(current))
+ pid, resource_, ctypes.byref(new), ctypes.byref(current)
+ )
if ret != 0:
- errno = ctypes.get_errno()
- raise OSError(errno, os.strerror(errno))
+ errno_ = ctypes.get_errno()
+ raise OSError(errno_, os.strerror(errno_))
return (current.rlim_cur, current.rlim_max)
if prlimit is not None:
__extra__all__.extend(
- [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()])
+ [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()]
+ )
# =====================================================================
@@ -364,34 +357,46 @@ if prlimit is not None:
def calculate_avail_vmem(mems):
"""Fallback for kernels < 3.14 where /proc/meminfo does not provide
- "MemAvailable:" column, see:
- https://blog.famzah.net/2014/09/24/
+ "MemAvailable", see:
+ https://blog.famzah.net/2014/09/24/.
+
This code reimplements the algorithm outlined here:
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
- XXX: on recent kernels this calculation differs by ~1.5% than
- "MemAvailable:" as it's calculated slightly differently, see:
- https://gitlab.com/procps-ng/procps/issues/42
- https://github.com/famzah/linux-memavailable-procfs/issues/2
+ We use this function also when "MemAvailable" returns 0 (possibly a
+ kernel bug, see: https://github.com/giampaolo/psutil/issues/1915).
+ In that case this routine matches "free" CLI tool result ("available"
+ column).
+
+ XXX: on recent kernels this calculation may differ by ~1.5% compared
+ to "MemAvailable:", as it's calculated slightly differently.
It is still way more realistic than doing (free + cached) though.
+ See:
+ * https://gitlab.com/procps-ng/procps/issues/42
+ * https://github.com/famzah/linux-memavailable-procfs/issues/2
"""
- # Fallback for very old distros. According to
+ # Note about "fallback" value. According to:
# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
# commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
- # ...long ago "avail" was calculated as (free + cached).
- # We might fallback in such cases:
- # "Active(file)" not available: 2.6.28 / Dec 2008
- # "Inactive(file)" not available: 2.6.28 / Dec 2008
- # "SReclaimable:" not available: 2.6.19 / Nov 2006
- # /proc/zoneinfo not available: 2.6.13 / Aug 2005
+ # ...long ago "available" memory was calculated as (free + cached),
+ # We use fallback when one of these is missing from /proc/meminfo:
+ # "Active(file)": introduced in 2.6.28 / Dec 2008
+ # "Inactive(file)": introduced in 2.6.28 / Dec 2008
+ # "SReclaimable": introduced in 2.6.19 / Nov 2006
+ # /proc/zoneinfo: introduced in 2.6.13 / Aug 2005
free = mems[b'MemFree:']
fallback = free + mems.get(b"Cached:", 0)
try:
lru_active_file = mems[b'Active(file):']
lru_inactive_file = mems[b'Inactive(file):']
slab_reclaimable = mems[b'SReclaimable:']
- except KeyError:
+ except KeyError as err:
+ debug(
+ "%s is missing from /proc/meminfo; using an approximation for "
+ "calculating available memory"
+ % err.args[0]
+ )
return fallback
try:
f = open_binary('%s/zoneinfo' % get_procfs_path())
@@ -416,19 +421,11 @@ def calculate_avail_vmem(mems):
def virtual_memory():
"""Report virtual memory stats.
- This implementation matches "free" and "vmstat -s" cmdline
- utility values and procps-ng-3.3.12 source was used as a reference
- (2016-09-18):
+ This implementation mimicks procps-ng-3.3.12, aka "free" CLI tool:
https://gitlab.com/procps-ng/procps/blob/
- 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c
- For reference, procps-ng-3.3.10 is the version available on Ubuntu
- 16.04.
-
- Note about "available" memory: up until psutil 4.3 it was
- calculated as "avail = (free + buffers + cached)". Now
- "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as
- it's more accurate.
- That matches "available" column in newer versions of "free".
+ 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791
+ The returned values are supposed to match both "free" and "vmstat -s"
+ CLI tools.
"""
missing_fields = []
mems = {}
@@ -480,10 +477,11 @@ def virtual_memory():
inactive = mems[b"Inactive:"]
except KeyError:
try:
- inactive = \
- mems[b"Inact_dirty:"] + \
- mems[b"Inact_clean:"] + \
- mems[b"Inact_laundry:"]
+ inactive = (
+ mems[b"Inact_dirty:"]
+ + mems[b"Inact_clean:"]
+ + mems[b"Inact_laundry:"]
+ )
except KeyError:
inactive = 0
missing_fields.append('inactive')
@@ -510,17 +508,23 @@ def virtual_memory():
avail = mems[b'MemAvailable:']
except KeyError:
avail = calculate_avail_vmem(mems)
+ else:
+ if avail == 0:
+ # Yes, it can happen (probably a kernel bug):
+ # https://github.com/giampaolo/psutil/issues/1915
+ # In this case "free" CLI tool makes an estimate. We do the same,
+ # and it matches "free" CLI tool.
+ avail = calculate_avail_vmem(mems)
if avail < 0:
avail = 0
missing_fields.append('available')
-
- # If avail is greater than total or our calculation overflows,
- # that's symptomatic of running within a LCX container where such
- # values will be dramatically distorted over those of the host.
- # https://gitlab.com/procps-ng/procps/blob/
- # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
- if avail > total:
+ elif avail > total:
+ # If avail is greater than total or our calculation overflows,
+ # that's symptomatic of running within a LCX container where such
+ # values will be dramatically distorted over those of the host.
+ # https://gitlab.com/procps-ng/procps/blob/
+ # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
avail = free
percent = usage_percent((total - avail), total, round_=1)
@@ -529,11 +533,23 @@ def virtual_memory():
if missing_fields:
msg = "%s memory stats couldn't be determined and %s set to 0" % (
", ".join(missing_fields),
- "was" if len(missing_fields) == 1 else "were")
- warnings.warn(msg, RuntimeWarning)
-
- return svmem(total, avail, percent, used, free,
- active, inactive, buffers, cached, shared, slab)
+ "was" if len(missing_fields) == 1 else "were",
+ )
+ warnings.warn(msg, RuntimeWarning, stacklevel=2)
+
+ return svmem(
+ total,
+ avail,
+ percent,
+ used,
+ free,
+ active,
+ inactive,
+ buffers,
+ cached,
+ shared,
+ slab,
+ )
def swap_memory():
@@ -562,9 +578,11 @@ def swap_memory():
f = open_binary("%s/vmstat" % get_procfs_path())
except IOError as err:
# see https://github.com/giampaolo/psutil/issues/722
- msg = "'sin' and 'sout' swap memory stats couldn't " \
- "be determined and were set to 0 (%s)" % str(err)
- warnings.warn(msg, RuntimeWarning)
+ msg = (
+ "'sin' and 'sout' swap memory stats couldn't "
+ + "be determined and were set to 0 (%s)" % str(err)
+ )
+ warnings.warn(msg, RuntimeWarning, stacklevel=2)
sin = sout = 0
else:
with f:
@@ -582,9 +600,9 @@ def swap_memory():
# we might get here when dealing with exotic Linux
# flavors, see:
# https://github.com/giampaolo/psutil/issues/313
- msg = "'sin' and 'sout' swap memory stats couldn't " \
- "be determined and were set to 0"
- warnings.warn(msg, RuntimeWarning)
+ msg = "'sin' and 'sout' swap memory stats couldn't "
+ msg += "be determined and were set to 0"
+ warnings.warn(msg, RuntimeWarning, stacklevel=2)
sin = sout = 0
return _common.sswap(total, used, free, percent, sin, sout)
@@ -605,7 +623,7 @@ def cpu_times():
set_scputimes_ntuple(procfs_path)
with open_binary('%s/stat' % procfs_path) as f:
values = f.readline().split()
- fields = values[1:len(scputimes._fields) + 1]
+ fields = values[1 : len(scputimes._fields) + 1]
fields = [float(x) / CLOCK_TICKS for x in fields]
return scputimes(*fields)
@@ -623,7 +641,7 @@ def per_cpu_times():
for line in f:
if line.startswith(b'cpu'):
values = line.split()
- fields = values[1:len(scputimes._fields) + 1]
+ fields = values[1 : len(scputimes._fields) + 1]
fields = [float(x) / CLOCK_TICKS for x in fields]
entry = scputimes(*fields)
cpus.append(entry)
@@ -659,8 +677,8 @@ def cpu_count_logical():
return num
-def cpu_count_physical():
- """Return the number of physical cores in the system."""
+def cpu_count_cores():
+ """Return the number of CPU cores in the system."""
# Method #1
ls = set()
# These 2 files are the same but */core_cpus_list is newer while
@@ -686,8 +704,9 @@ def cpu_count_physical():
if not line:
# new section
try:
- mapping[current_info[b'physical id']] = \
- current_info[b'cpu cores']
+ mapping[current_info[b'physical id']] = current_info[
+ b'cpu cores'
+ ]
except KeyError:
pass
current_info = {}
@@ -714,66 +733,71 @@ def cpu_stats():
interrupts = int(line.split()[1])
elif line.startswith(b'softirq'):
soft_interrupts = int(line.split()[1])
- if ctx_switches is not None and soft_interrupts is not None \
- and interrupts is not None:
+ if (
+ ctx_switches is not None
+ and soft_interrupts is not None
+ and interrupts is not None
+ ):
break
syscalls = 0
return _common.scpustats(
- ctx_switches, interrupts, soft_interrupts, syscalls)
+ ctx_switches, interrupts, soft_interrupts, syscalls
+ )
+
+
+def _cpu_get_cpuinfo_freq():
+ """Return current CPU frequency from cpuinfo if available."""
+ ret = []
+ with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
+ for line in f:
+ if line.lower().startswith(b'cpu mhz'):
+ ret.append(float(line.split(b':', 1)[1]))
+ return ret
-if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \
- os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"):
+if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or os.path.exists(
+ "/sys/devices/system/cpu/cpu0/cpufreq"
+):
+
def cpu_freq():
"""Return frequency metrics for all CPUs.
Contrarily to other OSes, Linux updates these values in
real-time.
"""
- def get_path(num):
- for p in ("/sys/devices/system/cpu/cpufreq/policy%s" % num,
- "/sys/devices/system/cpu/cpu%s/cpufreq" % num):
- if os.path.exists(p):
- return p
-
+ cpuinfo_freqs = _cpu_get_cpuinfo_freq()
+ paths = glob.glob(
+ "/sys/devices/system/cpu/cpufreq/policy[0-9]*"
+ ) or glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq")
+ paths.sort(key=lambda x: int(re.search(r"[0-9]+", x).group()))
ret = []
- for n in range(cpu_count_logical()):
- path = get_path(n)
- if not path:
- continue
-
- pjoin = os.path.join
- curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None)
+ pjoin = os.path.join
+ for i, path in enumerate(paths):
+ if len(paths) == len(cpuinfo_freqs):
+ # take cached value from cpuinfo if available, see:
+ # https://github.com/giampaolo/psutil/issues/1851
+ curr = cpuinfo_freqs[i] * 1000
+ else:
+ curr = bcat(pjoin(path, "scaling_cur_freq"), fallback=None)
if curr is None:
# Likely an old RedHat, see:
# https://github.com/giampaolo/psutil/issues/1071
- curr = cat(pjoin(path, "cpuinfo_cur_freq"), fallback=None)
+ curr = bcat(pjoin(path, "cpuinfo_cur_freq"), fallback=None)
if curr is None:
- raise NotImplementedError(
- "can't find current frequency file")
+ msg = "can't find current frequency file"
+ raise NotImplementedError(msg)
curr = int(curr) / 1000
- max_ = int(cat(pjoin(path, "scaling_max_freq"))) / 1000
- min_ = int(cat(pjoin(path, "scaling_min_freq"))) / 1000
+ max_ = int(bcat(pjoin(path, "scaling_max_freq"))) / 1000
+ min_ = int(bcat(pjoin(path, "scaling_min_freq"))) / 1000
ret.append(_common.scpufreq(curr, min_, max_))
return ret
-elif os.path.exists("/proc/cpuinfo"):
+else:
+
def cpu_freq():
"""Alternate implementation using /proc/cpuinfo.
min and max frequencies are not available and are set to None.
"""
- ret = []
- with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
- for line in f:
- if line.lower().startswith(b'cpu mhz'):
- key, value = line.split(b':', 1)
- ret.append(_common.scpufreq(float(value), 0., 0.))
- return ret
-
-else:
- def cpu_freq():
- """Dummy implementation when none of the above files are present.
- """
- return []
+ return [_common.scpufreq(x, 0.0, 0.0) for x in _cpu_get_cpuinfo_freq()]
# =====================================================================
@@ -837,6 +861,10 @@ class Connections:
if err.errno == errno.EINVAL:
# not a link
continue
+ if err.errno == errno.ENAMETOOLONG:
+ # file name too long
+ debug(err)
+ continue
raise
else:
if inode.startswith('socket:['):
@@ -899,11 +927,13 @@ class Connections:
if LITTLE_ENDIAN:
ip = socket.inet_ntop(
socket.AF_INET6,
- struct.pack('>4I', *struct.unpack('<4I', ip)))
+ struct.pack('>4I', *struct.unpack('<4I', ip)),
+ )
else:
ip = socket.inet_ntop(
socket.AF_INET6,
- struct.pack('<4I', *struct.unpack('<4I', ip)))
+ struct.pack('<4I', *struct.unpack('<4I', ip)),
+ )
except ValueError:
# see: https://github.com/giampaolo/psutil/issues/623
if not supports_ipv6():
@@ -918,22 +948,24 @@ class Connections:
if file.endswith('6') and not os.path.exists(file):
# IPv6 not supported
return
- with open_text(file, buffering=BIGFILE_BUFFERING) as f:
+ with open_text(file) as f:
f.readline() # skip the first line
for lineno, line in enumerate(f, 1):
try:
- _, laddr, raddr, status, _, _, _, _, _, inode = \
+ _, laddr, raddr, status, _, _, _, _, _, inode = (
line.split()[:10]
+ )
except ValueError:
raise RuntimeError(
- "error while parsing %s; malformed line %s %r" % (
- file, lineno, line))
+ "error while parsing %s; malformed line %s %r"
+ % (file, lineno, line)
+ )
if inode in inodes:
# # We assume inet sockets are unique, so we error
# # out if there are multiple references to the
# # same inode. We won't do this for UNIX sockets.
# if len(inodes[inode]) > 1 and family != socket.AF_UNIX:
- # raise ValueError("ambiguos inode with multiple "
+ # raise ValueError("ambiguous inode with multiple "
# "PIDs references")
pid, fd = inodes[inode][0]
else:
@@ -955,7 +987,7 @@ class Connections:
@staticmethod
def process_unix(file, family, inodes, filter_pid=None):
"""Parse /proc/net/unix files."""
- with open_text(file, buffering=BIGFILE_BUFFERING) as f:
+ with open_text(file) as f:
f.readline() # skip the first line
for line in f:
tokens = line.split()
@@ -966,9 +998,10 @@ class Connections:
# see: https://github.com/giampaolo/psutil/issues/766
continue
raise RuntimeError(
- "error while parsing %s; malformed line %r" % (
- file, line))
- if inode in inodes:
+ "error while parsing %s; malformed line %r"
+ % (file, line)
+ )
+ if inode in inodes: # noqa
# With UNIX sockets we can have a single inode
# referencing many file descriptors.
pairs = inodes[inode]
@@ -978,10 +1011,7 @@ class Connections:
if filter_pid is not None and filter_pid != pid:
continue
else:
- if len(tokens) == 8:
- path = tokens[-1]
- else:
- path = ""
+ path = tokens[-1] if len(tokens) == 8 else ''
type_ = _common.socktype_to_enum(int(type_))
# XXX: determining the remote endpoint of a
# UNIX socket on Linux is not possible, see:
@@ -992,8 +1022,10 @@ class Connections:
def retrieve(self, kind, pid=None):
if kind not in self.tmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in self.tmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in self.tmap]))
+ )
self._procfs_path = get_procfs_path()
if pid is not None:
inodes = self.get_proc_inodes(pid)
@@ -1007,17 +1039,19 @@ class Connections:
path = "%s/net/%s" % (self._procfs_path, proto_name)
if family in (socket.AF_INET, socket.AF_INET6):
ls = self.process_inet(
- path, family, type_, inodes, filter_pid=pid)
+ path, family, type_, inodes, filter_pid=pid
+ )
else:
- ls = self.process_unix(
- path, family, inodes, filter_pid=pid)
+ ls = self.process_unix(path, family, inodes, filter_pid=pid)
for fd, family, type_, laddr, raddr, status, bound_pid in ls:
if pid:
- conn = _common.pconn(fd, family, type_, laddr, raddr,
- status)
+ conn = _common.pconn(
+ fd, family, type_, laddr, raddr, status
+ )
else:
- conn = _common.sconn(fd, family, type_, laddr, raddr,
- status, bound_pid)
+ conn = _common.sconn(
+ fd, family, type_, laddr, raddr, status, bound_pid
+ )
ret.add(conn)
return list(ret)
@@ -1041,50 +1075,68 @@ def net_io_counters():
colon = line.rfind(':')
assert colon > 0, repr(line)
name = line[:colon].strip()
- fields = line[colon + 1:].strip().split()
+ fields = line[colon + 1 :].strip().split()
# in
- (bytes_recv,
- packets_recv,
- errin,
- dropin,
- fifoin, # unused
- framein, # unused
- compressedin, # unused
- multicastin, # unused
- # out
- bytes_sent,
- packets_sent,
- errout,
- dropout,
- fifoout, # unused
- collisionsout, # unused
- carrierout, # unused
- compressedout) = map(int, fields)
-
- retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv,
- errin, errout, dropin, dropout)
+ (
+ bytes_recv,
+ packets_recv,
+ errin,
+ dropin,
+ fifoin, # unused
+ framein, # unused
+ compressedin, # unused
+ multicastin, # unused
+ # out
+ bytes_sent,
+ packets_sent,
+ errout,
+ dropout,
+ fifoout, # unused
+ collisionsout, # unused
+ carrierout, # unused
+ compressedout,
+ ) = map(int, fields)
+
+ retdict[name] = (
+ bytes_sent,
+ bytes_recv,
+ packets_sent,
+ packets_recv,
+ errin,
+ errout,
+ dropin,
+ dropout,
+ )
return retdict
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
- duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL,
- cext.DUPLEX_HALF: NIC_DUPLEX_HALF,
- cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN}
+ duplex_map = {
+ cext.DUPLEX_FULL: NIC_DUPLEX_FULL,
+ cext.DUPLEX_HALF: NIC_DUPLEX_HALF,
+ cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN,
+ }
names = net_io_counters().keys()
ret = {}
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_is_running(name)
+ flags = cext_posix.net_if_flags(name)
duplex, speed = cext.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
if err.errno != errno.ENODEV:
raise
+ else:
+ debug(err)
else:
- ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
+ output_flags = ','.join(flags)
+ isup = 'running' in flags
+ ret[name] = _common.snicstats(
+ isup, duplex_map[duplex], speed, mtu, output_flags
+ )
return ret
@@ -1100,6 +1152,7 @@ def disk_io_counters(perdisk=False):
"""Return disk I/O statistics for every disk installed on the
system as a dict of raw tuples.
"""
+
def read_procfs():
# OK, this is a bit confusing. The format of /proc/diskstats can
# have 3 variations.
@@ -1122,6 +1175,7 @@ def disk_io_counters(perdisk=False):
for line in lines:
fields = line.split()
flen = len(fields)
+ # fmt: off
if flen == 15:
# Linux 2.4
name = fields[3]
@@ -1142,6 +1196,7 @@ def disk_io_counters(perdisk=False):
raise ValueError("not sure how to interpret line %r" % line)
yield (name, reads, writes, rbytes, wbytes, rtime, wtime,
reads_merged, writes_merged, busy_time)
+ # fmt: on
def read_sysfs():
for block in os.listdir('/sys/block'):
@@ -1151,10 +1206,12 @@ def disk_io_counters(perdisk=False):
with open_text(os.path.join(root, 'stat')) as f:
fields = f.read().strip().split()
name = os.path.basename(root)
+ # fmt: off
(reads, reads_merged, rbytes, rtime, writes, writes_merged,
wbytes, wtime, _, busy_time) = map(int, fields[:10])
yield (name, reads, writes, rbytes, wbytes, rtime,
wtime, reads_merged, writes_merged, busy_time)
+ # fmt: on
if os.path.exists('%s/diskstats' % get_procfs_path()):
gen = read_procfs()
@@ -1163,10 +1220,13 @@ def disk_io_counters(perdisk=False):
else:
raise NotImplementedError(
"%s/diskstats nor /sys/block filesystem are available on this "
- "system" % get_procfs_path())
+ "system"
+ % get_procfs_path()
+ )
retdict = {}
for entry in gen:
+ # fmt: off
(name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged,
writes_merged, busy_time) = entry
if not perdisk and not is_storage_device(name):
@@ -1187,24 +1247,101 @@ def disk_io_counters(perdisk=False):
wbytes *= DISK_SECTOR_SIZE
retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime,
reads_merged, writes_merged, busy_time)
+ # fmt: on
return retdict
+class RootFsDeviceFinder:
+ """disk_partitions() may return partitions with device == "/dev/root"
+ or "rootfs". This container class uses different strategies to try to
+ obtain the real device path. Resources:
+ https://bootlin.com/blog/find-root-device/
+ https://www.systutorials.com/how-to-find-the-disk-where-root-is-on-in-bash-on-linux/.
+ """
+
+ __slots__ = ['major', 'minor']
+
+ def __init__(self):
+ dev = os.stat("/").st_dev
+ self.major = os.major(dev)
+ self.minor = os.minor(dev)
+
+ def ask_proc_partitions(self):
+ with open_text("%s/partitions" % get_procfs_path()) as f:
+ for line in f.readlines()[2:]:
+ fields = line.split()
+ if len(fields) < 4: # just for extra safety
+ continue
+ major = int(fields[0]) if fields[0].isdigit() else None
+ minor = int(fields[1]) if fields[1].isdigit() else None
+ name = fields[3]
+ if major == self.major and minor == self.minor:
+ if name: # just for extra safety
+ return "/dev/%s" % name
+
+ def ask_sys_dev_block(self):
+ path = "/sys/dev/block/%s:%s/uevent" % (self.major, self.minor)
+ with open_text(path) as f:
+ for line in f:
+ if line.startswith("DEVNAME="):
+ name = line.strip().rpartition("DEVNAME=")[2]
+ if name: # just for extra safety
+ return "/dev/%s" % name
+
+ def ask_sys_class_block(self):
+ needle = "%s:%s" % (self.major, self.minor)
+ files = glob.iglob("/sys/class/block/*/dev")
+ for file in files:
+ try:
+ f = open_text(file)
+ except FileNotFoundError: # race condition
+ continue
+ else:
+ with f:
+ data = f.read().strip()
+ if data == needle:
+ name = os.path.basename(os.path.dirname(file))
+ return "/dev/%s" % name
+
+ def find(self):
+ path = None
+ if path is None:
+ try:
+ path = self.ask_proc_partitions()
+ except (IOError, OSError) as err:
+ debug(err)
+ if path is None:
+ try:
+ path = self.ask_sys_dev_block()
+ except (IOError, OSError) as err:
+ debug(err)
+ if path is None:
+ try:
+ path = self.ask_sys_class_block()
+ except (IOError, OSError) as err:
+ debug(err)
+ # We use exists() because the "/dev/*" part of the path is hard
+ # coded, so we want to be sure.
+ if path is not None and os.path.exists(path):
+ return path
+
+
def disk_partitions(all=False):
"""Return mounted disk partitions as a list of namedtuples."""
fstypes = set()
procfs_path = get_procfs_path()
- with open_text("%s/filesystems" % procfs_path) as f:
- for line in f:
- line = line.strip()
- if not line.startswith("nodev"):
- fstypes.add(line.strip())
- else:
- # ignore all lines starting with "nodev" except "nodev zfs"
- fstype = line.split("\t")[1]
- if fstype == "zfs":
- fstypes.add("zfs")
+ if not all:
+ with open_text("%s/filesystems" % procfs_path) as f:
+ for line in f:
+ line = line.strip()
+ if not line.startswith("nodev"):
+ fstypes.add(line.strip())
+ else:
+ # ignore all lines starting with "nodev" except "nodev zfs"
+ fstype = line.split("\t")[1]
+ if fstype == "zfs":
+ fstypes.add("zfs")
# See: https://github.com/giampaolo/psutil/issues/1307
if procfs_path == "/proc" and os.path.isfile('/etc/mtab'):
@@ -1218,12 +1355,15 @@ def disk_partitions(all=False):
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
+ if device in ("/dev/root", "rootfs"):
+ device = RootFsDeviceFinder().find() or device
if not all:
if device == '' or fstype not in fstypes:
continue
maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
+ ntuple = _common.sdiskpart(
+ device, mountpoint, fstype, opts, maxfile, maxpath
+ )
retlist.append(ntuple)
return retlist
@@ -1260,7 +1400,8 @@ def sensors_temperatures():
# https://github.com/giampaolo/psutil/issues/1708
# https://github.com/giampaolo/psutil/pull/1648
basenames2 = glob.glob(
- '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*')
+ '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*'
+ )
repl = re.compile('/sys/devices/platform/coretemp.*/hwmon/')
for name in basenames2:
altname = repl.sub('/sys/class/hwmon/', name)
@@ -1270,9 +1411,9 @@ def sensors_temperatures():
for base in basenames:
try:
path = base + '_input'
- current = float(cat(path)) / 1000.0
+ current = float(bcat(path)) / 1000.0
path = os.path.join(os.path.dirname(base), 'name')
- unit_name = cat(path, binary=False)
+ unit_name = cat(path).strip()
except (IOError, OSError, ValueError):
# A lot of things can go wrong here, so let's just skip the
# whole entry. Sure thing is Linux's /sys/class/hwmon really
@@ -1284,9 +1425,9 @@ def sensors_temperatures():
# https://github.com/giampaolo/psutil/issues/1323
continue
- high = cat(base + '_max', fallback=None)
- critical = cat(base + '_crit', fallback=None)
- label = cat(base + '_label', fallback='', binary=False)
+ high = bcat(base + '_max', fallback=None)
+ critical = bcat(base + '_crit', fallback=None)
+ label = cat(base + '_label', fallback='').strip()
if high is not None:
try:
@@ -1309,27 +1450,31 @@ def sensors_temperatures():
for base in basenames:
try:
path = os.path.join(base, 'temp')
- current = float(cat(path)) / 1000.0
+ current = float(bcat(path)) / 1000.0
path = os.path.join(base, 'type')
- unit_name = cat(path, binary=False)
+ unit_name = cat(path).strip()
except (IOError, OSError, ValueError) as err:
- debug("ignoring %r for file %r" % (err, path))
+ debug(err)
continue
trip_paths = glob.glob(base + '/trip_point*')
- trip_points = set(['_'.join(
- os.path.basename(p).split('_')[0:3]) for p in trip_paths])
+ trip_points = set([
+ '_'.join(os.path.basename(p).split('_')[0:3])
+ for p in trip_paths
+ ])
critical = None
high = None
for trip_point in trip_points:
path = os.path.join(base, trip_point + "_type")
- trip_type = cat(path, fallback='', binary=False)
+ trip_type = cat(path, fallback='').strip()
if trip_type == 'critical':
- critical = cat(os.path.join(base, trip_point + "_temp"),
- fallback=None)
+ critical = bcat(
+ os.path.join(base, trip_point + "_temp"), fallback=None
+ )
elif trip_type == 'high':
- high = cat(os.path.join(base, trip_point + "_temp"),
- fallback=None)
+ high = bcat(
+ os.path.join(base, trip_point + "_temp"), fallback=None
+ )
if high is not None:
try:
@@ -1367,13 +1512,12 @@ def sensors_fans():
basenames = sorted(set([x.split('_')[0] for x in basenames]))
for base in basenames:
try:
- current = int(cat(base + '_input'))
+ current = int(bcat(base + '_input'))
except (IOError, OSError) as err:
- warnings.warn("ignoring %r" % err, RuntimeWarning)
+ debug(err)
continue
- unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
- binary=False)
- label = cat(base + '_label', fallback='', binary=False)
+ unit_name = cat(os.path.join(os.path.dirname(base), 'name')).strip()
+ label = cat(base + '_label', fallback='').strip()
ret[unit_name].append(_common.sfan(label, current))
return dict(ret)
@@ -1384,22 +1528,28 @@ def sensors_battery():
Implementation note: it appears /sys/class/power_supply/BAT0/
directory structure may vary and provide files with the same
meaning but under different names, see:
- https://github.com/giampaolo/psutil/issues/966
+ https://github.com/giampaolo/psutil/issues/966.
"""
null = object()
- def multi_cat(*paths):
+ def multi_bcat(*paths):
"""Attempt to read the content of multiple files which may
not exist. If none of them exist return None.
"""
for path in paths:
- ret = cat(path, fallback=null)
+ ret = bcat(path, fallback=null)
if ret != null:
- return int(ret) if ret.isdigit() else ret
+ try:
+ return int(ret)
+ except ValueError:
+ return ret.strip()
return None
- bats = [x for x in os.listdir(POWER_SUPPLY_PATH) if x.startswith('BAT') or
- 'battery' in x.lower()]
+ bats = [
+ x
+ for x in os.listdir(POWER_SUPPLY_PATH)
+ if x.startswith('BAT') or 'battery' in x.lower()
+ ]
if not bats:
return None
# Get the first available battery. Usually this is "BAT0", except
@@ -1408,16 +1558,10 @@ def sensors_battery():
root = os.path.join(POWER_SUPPLY_PATH, sorted(bats)[0])
# Base metrics.
- energy_now = multi_cat(
- root + "/energy_now",
- root + "/charge_now")
- power_now = multi_cat(
- root + "/power_now",
- root + "/current_now")
- energy_full = multi_cat(
- root + "/energy_full",
- root + "/charge_full")
- time_to_empty = multi_cat(root + "/time_to_empty_now")
+ energy_now = multi_bcat(root + "/energy_now", root + "/charge_now")
+ power_now = multi_bcat(root + "/power_now", root + "/current_now")
+ energy_full = multi_bcat(root + "/energy_full", root + "/charge_full")
+ time_to_empty = multi_bcat(root + "/time_to_empty_now")
# Percent. If we have energy_full the percentage will be more
# accurate compared to reading /capacity file (float vs. int).
@@ -1435,13 +1579,14 @@ def sensors_battery():
# Note: AC0 is not always available and sometimes (e.g. CentOS7)
# it's called "AC".
power_plugged = None
- online = multi_cat(
+ online = multi_bcat(
os.path.join(POWER_SUPPLY_PATH, "AC0/online"),
- os.path.join(POWER_SUPPLY_PATH, "AC/online"))
+ os.path.join(POWER_SUPPLY_PATH, "AC/online"),
+ )
if online is not None:
power_plugged = online == 1
else:
- status = cat(root + "/status", fallback="", binary=False).lower()
+ status = cat(root + "/status", fallback="").strip().lower()
if status == "discharging":
power_plugged = False
elif status in ("charging", "full"):
@@ -1478,14 +1623,7 @@ def users():
retlist = []
rawlist = cext.users()
for item in rawlist:
- user, tty, hostname, tstamp, user_process, pid = item
- # note: the underlying C function includes entries about
- # system boot, run level and others. We might want
- # to use them in the future.
- if not user_process:
- continue
- if hostname in (':0.0', ':0'):
- hostname = 'localhost'
+ user, tty, hostname, tstamp, pid = item
nt = _common.suser(user, tty or None, hostname, tstamp, pid)
retlist.append(nt)
return retlist
@@ -1501,8 +1639,7 @@ def boot_time():
ret = float(line.strip().split()[1])
BOOT_TIME = ret
return ret
- raise RuntimeError(
- "line 'btime' not found in %s" % path)
+ raise RuntimeError("line 'btime' not found in %s" % path)
# =====================================================================
@@ -1563,7 +1700,7 @@ def ppid_map():
pass
else:
rpar = data.rfind(b')')
- dset = data[rpar + 2:].split()
+ dset = data[rpar + 2 :].split()
ppid = int(dset[1])
ret[pid] = ppid
return ret
@@ -1573,6 +1710,7 @@ def wrap_exceptions(fun):
"""Decorator which translates bare OSError and IOError exceptions
into NoSuchProcess and AccessDenied.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
@@ -1580,17 +1718,18 @@ def wrap_exceptions(fun):
except PermissionError:
raise AccessDenied(self.pid, self._name)
except ProcessLookupError:
+ self._raise_if_zombie()
raise NoSuchProcess(self.pid, self._name)
except FileNotFoundError:
+ self._raise_if_zombie()
if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)):
raise NoSuchProcess(self.pid, self._name)
- # Note: zombies will keep existing under /proc until they're
- # gone so there's no way to distinguish them in here.
raise
+
return wrapper
-class Process(object):
+class Process:
"""Linux process implementation."""
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
@@ -1601,7 +1740,27 @@ class Process(object):
self._ppid = None
self._procfs_path = get_procfs_path()
- def _assert_alive(self):
+ def _is_zombie(self):
+ # Note: most of the times Linux is able to return info about the
+ # process even if it's a zombie, and /proc/{pid} will exist.
+ # There are some exceptions though, like exe(), cmdline() and
+ # memory_maps(). In these cases /proc/{pid}/{file} exists but
+ # it's empty. Instead of returning a "null" value we'll raise an
+ # exception.
+ try:
+ data = bcat("%s/%s/stat" % (self._procfs_path, self.pid))
+ except (IOError, OSError):
+ return False
+ else:
+ rpar = data.rfind(b')')
+ status = data[rpar + 2 : rpar + 3]
+ return status == b"Z"
+
+ def _raise_if_zombie(self):
+ if self._is_zombie():
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+
+ def _raise_if_not_alive(self):
"""Raise NSP if the process disappeared on us."""
# For those C function who do not raise NSP, possibly returning
# incorrect or incomplete result.
@@ -1613,19 +1772,18 @@ class Process(object):
"""Parse /proc/{pid}/stat file and return a dict with various
process info.
Using "man proc" as a reference: where "man proc" refers to
- position N always substract 3 (e.g ppid position 4 in
+ position N always subtract 3 (e.g ppid position 4 in
'man proc' == position 1 in here).
The return value is cached in case oneshot() ctx manager is
in use.
"""
- with open_binary("%s/%s/stat" % (self._procfs_path, self.pid)) as f:
- data = f.read()
+ data = bcat("%s/%s/stat" % (self._procfs_path, self.pid))
# 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 occurence of ")".
+ # the first occurrence of "(" and the last occurrence of ")".
rpar = data.rfind(b')')
- name = data[data.find(b'(') + 1:rpar]
- fields = data[rpar + 2:].split()
+ name = data[data.find(b'(') + 1 : rpar]
+ fields = data[rpar + 2 :].split()
ret = {}
ret['name'] = name
@@ -1655,8 +1813,7 @@ class Process(object):
@wrap_exceptions
@memoize_when_activated
def _read_smaps_file(self):
- with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid),
- buffering=BIGFILE_BUFFERING) as f:
+ with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid)) as f:
return f.read().strip()
def oneshot_enter(self):
@@ -1677,22 +1834,18 @@ class Process(object):
# XXX - gets changed later and probably needs refactoring
return name
+ @wrap_exceptions
def exe(self):
try:
return readlink("%s/%s/exe" % (self._procfs_path, self.pid))
except (FileNotFoundError, ProcessLookupError):
+ self._raise_if_zombie()
# no such file error; might be raised also if the
# path actually exists for system processes with
# low pids (about 0-20)
if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)):
return ""
- else:
- if not pid_exists(self.pid):
- raise NoSuchProcess(self.pid, self._name)
- else:
- raise ZombieProcess(self.pid, self._name, self._ppid)
- except PermissionError:
- raise AccessDenied(self.pid, self._name)
+ raise
@wrap_exceptions
def cmdline(self):
@@ -1700,6 +1853,7 @@ class Process(object):
data = f.read()
if not data:
# may happen in case of zombie process
+ self._raise_if_zombie()
return []
# 'man proc' states that args are separated by null bytes '\0'
# and last char is supposed to be a null byte. Nevertheless
@@ -1736,6 +1890,7 @@ class Process(object):
# May not be available on old kernels.
if os.path.exists('/proc/%s/io' % os.getpid()):
+
@wrap_exceptions
def io_counters(self):
fname = "%s/%s/io" % (self._procfs_path, self.pid)
@@ -1764,8 +1919,10 @@ class Process(object):
fields[b'wchar'], # write chars
)
except KeyError as err:
- raise ValueError("%r field was not found in %s; found fields "
- "are %r" % (err[0], fname, fields))
+ raise ValueError(
+ "%r field was not found in %s; found fields are %r"
+ % (err.args[0], fname, fields)
+ )
@wrap_exceptions
def cpu_times(self):
@@ -1811,22 +1968,47 @@ class Process(object):
# | dirty | dirty pages (unused in Linux 2.6) | dt | |
# ============================================================
with open_binary("%s/%s/statm" % (self._procfs_path, self.pid)) as f:
- vms, rss, shared, text, lib, data, dirty = \
- [int(x) * PAGESIZE for x in f.readline().split()[:7]]
+ vms, rss, shared, text, lib, data, dirty = (
+ int(x) * PAGESIZE for x in f.readline().split()[:7]
+ )
return pmem(rss, vms, shared, text, lib, data, dirty)
- # /proc/pid/smaps does not exist on kernels < 2.6.14 or if
- # CONFIG_MMU kernel configuration option is not enabled.
- if HAS_SMAPS:
+ if HAS_PROC_SMAPS_ROLLUP or HAS_PROC_SMAPS:
+
+ def _parse_smaps_rollup(self):
+ # /proc/pid/smaps_rollup was added to Linux in 2017. Faster
+ # than /proc/pid/smaps. It reports higher PSS than */smaps
+ # (from 1k up to 200k higher; tested against all processes).
+ # IMPORTANT: /proc/pid/smaps_rollup is weird, because it
+ # raises ESRCH / ENOENT for many PIDs, even if they're alive
+ # (also as root). In that case we'll use /proc/pid/smaps as
+ # fallback, which is slower but has a +50% success rate
+ # compared to /proc/pid/smaps_rollup.
+ uss = pss = swap = 0
+ with open_binary(
+ "{}/{}/smaps_rollup".format(self._procfs_path, self.pid)
+ ) as f:
+ for line in f:
+ if line.startswith(b"Private_"):
+ # Private_Clean, Private_Dirty, Private_Hugetlb
+ uss += int(line.split()[1]) * 1024
+ elif line.startswith(b"Pss:"):
+ pss = int(line.split()[1]) * 1024
+ elif line.startswith(b"Swap:"):
+ swap = int(line.split()[1]) * 1024
+ return (uss, pss, swap)
@wrap_exceptions
- def memory_full_info(
- self,
- # Gets Private_Clean, Private_Dirty, Private_Hugetlb.
- _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"),
- _pss_re=re.compile(br"\nPss\:\s+(\d+)"),
- _swap_re=re.compile(br"\nSwap\:\s+(\d+)")):
- basic_mem = self.memory_info()
+ def _parse_smaps(
+ self,
+ # Gets Private_Clean, Private_Dirty, Private_Hugetlb.
+ _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"),
+ _pss_re=re.compile(br"\nPss\:\s+(\d+)"),
+ _swap_re=re.compile(br"\nSwap\:\s+(\d+)"),
+ ):
+ # /proc/pid/smaps does not exist on kernels < 2.6.14 or if
+ # CONFIG_MMU kernel configuration option is not enabled.
+
# Note: using 3 regexes is faster than reading the file
# line by line.
# XXX: on Python 3 the 2 regexes are 30% slower than on
@@ -1845,22 +2027,35 @@ class Process(object):
uss = sum(map(int, _private_re.findall(smaps_data))) * 1024
pss = sum(map(int, _pss_re.findall(smaps_data))) * 1024
swap = sum(map(int, _swap_re.findall(smaps_data))) * 1024
+ return (uss, pss, swap)
+
+ @wrap_exceptions
+ def memory_full_info(self):
+ if HAS_PROC_SMAPS_ROLLUP: # faster
+ try:
+ uss, pss, swap = self._parse_smaps_rollup()
+ except (ProcessLookupError, FileNotFoundError):
+ uss, pss, swap = self._parse_smaps()
+ else:
+ uss, pss, swap = self._parse_smaps()
+ basic_mem = self.memory_info()
return pfullmem(*basic_mem + (uss, pss, swap))
else:
memory_full_info = memory_info
- if HAS_SMAPS:
+ if HAS_PROC_SMAPS:
@wrap_exceptions
def memory_maps(self):
"""Return process's mapped memory regions as a list of named
tuples. Fields are explained in 'man proc'; here is an updated
- (Apr 2012) version: http://goo.gl/fmebo
+ (Apr 2012) version: http://goo.gl/fmebo.
/proc/{PID}/smaps does not exist on kernels < 2.6.14 or if
CONFIG_MMU kernel configuration option is not enabled.
"""
+
def get_blocks(lines, current_block):
data = {}
for line in lines:
@@ -1877,13 +2072,17 @@ class Process(object):
# see issue #369
continue
else:
- raise ValueError("don't know how to inte"
- "rpret line %r" % line)
+ raise ValueError(
+ "don't know how to interpret line %r"
+ % line
+ )
yield (current_block.pop(), data)
data = self._read_smaps_file()
- # Note: smaps file can be empty for certain processes.
+ # Note: smaps file can be empty for certain processes or for
+ # zombies.
if not data:
+ self._raise_if_zombie()
return []
lines = data.split(b'\n')
ls = []
@@ -1894,19 +2093,21 @@ class Process(object):
try:
addr, perms, offset, dev, inode, path = hfields
except ValueError:
- addr, perms, offset, dev, inode, path = \
- hfields + ['']
+ addr, perms, offset, dev, inode, path = hfields + ['']
if not path:
path = '[anon]'
else:
if PY3:
path = decode(path)
path = path.strip()
- if (path.endswith(' (deleted)') and not
- path_exists_strict(path)):
+ if path.endswith(' (deleted)') and not path_exists_strict(
+ path
+ ):
path = path[:-10]
ls.append((
- decode(addr), decode(perms), path,
+ decode(addr),
+ decode(perms),
+ path,
data.get(b'Rss:', 0),
data.get(b'Size:', 0),
data.get(b'Pss:', 0),
@@ -1916,32 +2117,26 @@ class Process(object):
data.get(b'Private_Dirty:', 0),
data.get(b'Referenced:', 0),
data.get(b'Anonymous:', 0),
- data.get(b'Swap:', 0)
+ data.get(b'Swap:', 0),
))
return ls
@wrap_exceptions
def cwd(self):
- try:
- return readlink("%s/%s/cwd" % (self._procfs_path, self.pid))
- except (FileNotFoundError, ProcessLookupError):
- # https://github.com/giampaolo/psutil/issues/986
- if not pid_exists(self.pid):
- raise NoSuchProcess(self.pid, self._name)
- else:
- raise ZombieProcess(self.pid, self._name, self._ppid)
+ return readlink("%s/%s/cwd" % (self._procfs_path, self.pid))
@wrap_exceptions
- def num_ctx_switches(self,
- _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)')):
+ def num_ctx_switches(
+ self, _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)')
+ ):
data = self._read_status_file()
ctxsw = _ctxsw_re.findall(data)
if not ctxsw:
raise NotImplementedError(
"'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'"
"lines were not found in %s/%s/status; the kernel is "
- "probably older than 2.6.23" % (
- self._procfs_path, self.pid))
+ "probably older than 2.6.23" % (self._procfs_path, self.pid)
+ )
else:
return _common.pctxsw(int(ctxsw[0]), int(ctxsw[1]))
@@ -1961,24 +2156,27 @@ class Process(object):
hit_enoent = False
for thread_id in thread_ids:
fname = "%s/%s/task/%s/stat" % (
- self._procfs_path, self.pid, thread_id)
+ self._procfs_path,
+ self.pid,
+ thread_id,
+ )
try:
with open_binary(fname) as f:
st = f.read().strip()
- except FileNotFoundError:
- # no such file or directory; it means thread
- # disappeared on us
+ except (FileNotFoundError, ProcessLookupError):
+ # no such file or directory or no such process;
+ # it means thread disappeared on us
hit_enoent = True
continue
# ignore the first two values ("pid (exe)")
- st = st[st.find(b')') + 2:]
+ st = st[st.find(b')') + 2 :]
values = st.split(b' ')
utime = float(values[11]) / CLOCK_TICKS
stime = float(values[12]) / CLOCK_TICKS
ntuple = _common.pthread(int(thread_id), utime, stime)
retlist.append(ntuple)
if hit_enoent:
- self._assert_alive()
+ self._raise_if_not_alive()
return retlist
@wrap_exceptions
@@ -2002,7 +2200,8 @@ class Process(object):
return cext.proc_cpu_affinity_get(self.pid)
def _get_eligible_cpus(
- self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")):
+ self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")
+ ):
# See: https://github.com/giampaolo/psutil/issues/956
data = self._read_status_file()
match = _re.findall(data)
@@ -2022,12 +2221,14 @@ class Process(object):
for cpu in cpus:
if cpu not in all_cpus:
raise ValueError(
- "invalid CPU number %r; choose between %s" % (
- cpu, eligible_cpus))
+ "invalid CPU number %r; choose between %s"
+ % (cpu, eligible_cpus)
+ )
if cpu not in eligible_cpus:
raise ValueError(
"CPU number %r is not eligible; choose "
- "between %s" % (cpu, eligible_cpus))
+ "between %s" % (cpu, eligible_cpus)
+ )
raise
# only starting from kernel 2.6.13
@@ -2047,7 +2248,8 @@ class Process(object):
if value and ioclass in (IOPRIO_CLASS_IDLE, IOPRIO_CLASS_NONE):
raise ValueError("%r ioclass accepts no value" % ioclass)
if value < 0 or value > 7:
- raise ValueError("value not in 0-7 range")
+ msg = "value not in 0-7 range"
+ raise ValueError(msg)
return cext.proc_ioprio_set(self.pid, ioclass, value)
if prlimit is not None:
@@ -2058,7 +2260,8 @@ class Process(object):
# we don't want that. We should never get here though as
# PID 0 is not supported on Linux.
if self.pid == 0:
- raise ValueError("can't use prlimit() against PID 0 process")
+ msg = "can't use prlimit() against PID 0 process"
+ raise ValueError(msg)
try:
if limits is None:
# get
@@ -2066,17 +2269,18 @@ class Process(object):
else:
# set
if len(limits) != 2:
- raise ValueError(
- "second argument must be a (soft, hard) tuple, "
- "got %s" % repr(limits))
+ msg = (
+ "second argument must be a (soft, hard) "
+ + "tuple, got %s" % repr(limits)
+ )
+ raise ValueError(msg)
prlimit(self.pid, resource_, limits)
except OSError as err:
- if err.errno == errno.ENOSYS and pid_exists(self.pid):
+ if err.errno == errno.ENOSYS:
# I saw this happening on Travis:
# https://travis-ci.org/giampaolo/psutil/jobs/51368273
- raise ZombieProcess(self.pid, self._name, self._ppid)
- else:
- raise
+ self._raise_if_zombie()
+ raise
@wrap_exceptions
def status(self):
@@ -2103,6 +2307,10 @@ class Process(object):
if err.errno == errno.EINVAL:
# not a link
continue
+ if err.errno == errno.ENAMETOOLONG:
+ # file name too long
+ debug(err)
+ continue
raise
else:
# If path is not an absolute there's no way to tell
@@ -2112,28 +2320,32 @@ class Process(object):
if path.startswith('/') and isfile_strict(path):
# Get file position and flags.
file = "%s/%s/fdinfo/%s" % (
- self._procfs_path, self.pid, fd)
+ self._procfs_path,
+ self.pid,
+ fd,
+ )
try:
with open_binary(file) as f:
pos = int(f.readline().split()[1])
flags = int(f.readline().split()[1], 8)
- except FileNotFoundError:
+ except (FileNotFoundError, ProcessLookupError):
# fd gone in the meantime; process may
# still be alive
hit_enoent = True
else:
mode = file_flags_to_mode(flags)
ntuple = popenfile(
- path, int(fd), int(pos), mode, flags)
+ path, int(fd), int(pos), mode, flags
+ )
retlist.append(ntuple)
if hit_enoent:
- self._assert_alive()
+ self._raise_if_not_alive()
return retlist
@wrap_exceptions
def connections(self, kind='inet'):
ret = _connections.retrieve(kind, self.pid)
- self._assert_alive()
+ self._raise_if_not_alive()
return ret
@wrap_exceptions
diff --git a/contrib/python/psutil/py3/psutil/_psosx.py b/contrib/python/psutil/py3/psutil/_psosx.py
index c7770d6591..673ac0db75 100644
--- a/contrib/python/psutil/py3/psutil/_psosx.py
+++ b/contrib/python/psutil/py3/psutil/_psosx.py
@@ -4,7 +4,6 @@
"""macOS platform implementation."""
-import contextlib
import errno
import functools
import os
@@ -15,14 +14,14 @@ from . import _psposix
from . import _psutil_osx as cext
from . import _psutil_posix as cext_posix
from ._common import AccessDenied
+from ._common import NoSuchProcess
+from ._common import ZombieProcess
from ._common import conn_tmap
from ._common import conn_to_ntuple
from ._common import isfile_strict
from ._common import memoize_when_activated
-from ._common import NoSuchProcess
from ._common import parse_environ_block
from ._common import usage_percent
-from ._common import ZombieProcess
from ._compat import PermissionError
from ._compat import ProcessLookupError
@@ -92,6 +91,7 @@ pidtaskinfo_map = dict(
# =====================================================================
+# fmt: off
# psutil.cpu_times()
scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
# psutil.virtual_memory()
@@ -102,6 +102,7 @@ svmem = namedtuple(
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
# psutil.Process.memory_full_info()
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
+# fmt: on
# =====================================================================
@@ -122,8 +123,7 @@ def virtual_memory():
# cmdline utility.
free -= speculative
percent = usage_percent((total - avail), total, round_=1)
- return svmem(total, avail, percent, used, free,
- active, inactive, wired)
+ return svmem(total, avail, percent, used, free, active, inactive, wired)
def swap_memory():
@@ -145,7 +145,7 @@ def cpu_times():
def per_cpu_times():
- """Return system CPU times as a named tuple"""
+ """Return system CPU times as a named tuple."""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle = cpu_t
@@ -159,23 +159,25 @@ def cpu_count_logical():
return cext.cpu_count_logical()
-def cpu_count_physical():
- """Return the number of physical CPUs in the system."""
- return cext.cpu_count_phys()
+def cpu_count_cores():
+ """Return the number of CPU cores in the system."""
+ return cext.cpu_count_cores()
def cpu_stats():
- ctx_switches, interrupts, soft_interrupts, syscalls, traps = \
+ ctx_switches, interrupts, soft_interrupts, syscalls, traps = (
cext.cpu_stats()
+ )
return _common.scpustats(
- ctx_switches, interrupts, soft_interrupts, syscalls)
+ ctx_switches, interrupts, soft_interrupts, syscalls
+ )
def cpu_freq():
"""Return CPU frequency.
On macOS per-cpu frequency is not supported.
Also, the returned frequency never changes, see:
- https://arstechnica.com/civis/viewtopic.php?f=19&t=465002
+ https://arstechnica.com/civis/viewtopic.php?f=19&t=465002.
"""
curr, min_, max_ = cext.cpu_freq()
return [_common.scpufreq(curr, min_, max_)]
@@ -202,8 +204,9 @@ def disk_partitions(all=False):
if not os.path.isabs(device) or not os.path.exists(device):
continue
maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
+ ntuple = _common.sdiskpart(
+ device, mountpoint, fstype, opts, maxfile, maxpath
+ )
retlist.append(ntuple)
return retlist
@@ -264,7 +267,7 @@ def net_if_stats():
for name in names:
try:
mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_is_running(name)
+ flags = cext_posix.net_if_flags(name)
duplex, speed = cext_posix.net_if_duplex_speed(name)
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1279
@@ -273,7 +276,11 @@ def net_if_stats():
else:
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ output_flags = ','.join(flags)
+ isup = 'running' in flags
+ ret[name] = _common.snicstats(
+ isup, duplex, speed, mtu, output_flags
+ )
return ret
@@ -330,7 +337,7 @@ def is_zombie(pid):
try:
st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']]
return st == cext.SZOMB
- except Exception:
+ except OSError:
return False
@@ -338,6 +345,7 @@ def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
@@ -349,38 +357,11 @@ def wrap_exceptions(fun):
raise NoSuchProcess(self.pid, self._name)
except PermissionError:
raise AccessDenied(self.pid, self._name)
- except cext.ZombieProcessError:
- raise ZombieProcess(self.pid, self._name, self._ppid)
- return wrapper
-
-@contextlib.contextmanager
-def catch_zombie(proc):
- """There are some poor C APIs which incorrectly raise ESRCH when
- the process is still alive or it's a zombie, or even RuntimeError
- (those who don't set errno). This is here in order to solve:
- https://github.com/giampaolo/psutil/issues/1044
- """
- try:
- yield
- except (OSError, RuntimeError) as err:
- if isinstance(err, RuntimeError) or err.errno == errno.ESRCH:
- try:
- # status() is not supposed to lie and correctly detect
- # zombies so if it raises ESRCH it's true.
- status = proc.status()
- except NoSuchProcess:
- raise err
- else:
- if status == _common.STATUS_ZOMBIE:
- raise ZombieProcess(proc.pid, proc._name, proc._ppid)
- else:
- raise AccessDenied(proc.pid, proc._name)
- else:
- raise
+ return wrapper
-class Process(object):
+class Process:
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid", "_cache"]
@@ -402,8 +383,7 @@ class Process(object):
@memoize_when_activated
def _get_pidtaskinfo(self):
# Note: should work for PIDs owned by user only.
- with catch_zombie(self):
- ret = cext.proc_pidtaskinfo_oneshot(self.pid)
+ ret = cext.proc_pidtaskinfo_oneshot(self.pid)
assert len(ret) == len(pidtaskinfo_map)
return ret
@@ -422,18 +402,15 @@ class Process(object):
@wrap_exceptions
def exe(self):
- with catch_zombie(self):
- return cext.proc_exe(self.pid)
+ return cext.proc_exe(self.pid)
@wrap_exceptions
def cmdline(self):
- with catch_zombie(self):
- return cext.proc_cmdline(self.pid)
+ return cext.proc_cmdline(self.pid)
@wrap_exceptions
def environ(self):
- with catch_zombie(self):
- return parse_environ_block(cext.proc_environ(self.pid))
+ return parse_environ_block(cext.proc_environ(self.pid))
@wrap_exceptions
def ppid(self):
@@ -442,8 +419,7 @@ class Process(object):
@wrap_exceptions
def cwd(self):
- with catch_zombie(self):
- return cext.proc_cwd(self.pid)
+ return cext.proc_cwd(self.pid)
@wrap_exceptions
def uids(self):
@@ -451,7 +427,8 @@ class Process(object):
return _common.puids(
rawtuple[kinfo_proc_map['ruid']],
rawtuple[kinfo_proc_map['euid']],
- rawtuple[kinfo_proc_map['suid']])
+ rawtuple[kinfo_proc_map['suid']],
+ )
@wrap_exceptions
def gids(self):
@@ -459,7 +436,8 @@ class Process(object):
return _common.puids(
rawtuple[kinfo_proc_map['rgid']],
rawtuple[kinfo_proc_map['egid']],
- rawtuple[kinfo_proc_map['sgid']])
+ rawtuple[kinfo_proc_map['sgid']],
+ )
@wrap_exceptions
def terminal(self):
@@ -484,7 +462,7 @@ class Process(object):
def memory_full_info(self):
basic_mem = self.memory_info()
uss = cext.proc_memory_uss(self.pid)
- return pfullmem(*basic_mem + (uss, ))
+ return pfullmem(*basic_mem + (uss,))
@wrap_exceptions
def cpu_times(self):
@@ -493,7 +471,9 @@ class Process(object):
rawtuple[pidtaskinfo_map['cpuutime']],
rawtuple[pidtaskinfo_map['cpustime']],
# children user / system times are not retrievable (set to 0)
- 0.0, 0.0)
+ 0.0,
+ 0.0,
+ )
@wrap_exceptions
def create_time(self):
@@ -516,8 +496,7 @@ class Process(object):
if self.pid == 0:
return []
files = []
- with catch_zombie(self):
- rawlist = cext.proc_open_files(self.pid)
+ rawlist = cext.proc_open_files(self.pid)
for path, fd in rawlist:
if isfile_strict(path):
ntuple = _common.popenfile(path, fd)
@@ -527,16 +506,18 @@ class Process(object):
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in conn_tmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in conn_tmap]))
+ )
families, types = conn_tmap[kind]
- with catch_zombie(self):
- rawlist = cext.proc_connections(self.pid, families, types)
+ rawlist = cext.proc_connections(self.pid, families, types)
ret = []
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
- nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
- TCP_STATUSES)
+ nt = conn_to_ntuple(
+ fd, fam, type, laddr, raddr, status, TCP_STATUSES
+ )
ret.append(nt)
return ret
@@ -544,8 +525,7 @@ class Process(object):
def num_fds(self):
if self.pid == 0:
return 0
- with catch_zombie(self):
- return cext.proc_num_fds(self.pid)
+ return cext.proc_num_fds(self.pid)
@wrap_exceptions
def wait(self, timeout=None):
@@ -553,13 +533,11 @@ class Process(object):
@wrap_exceptions
def nice_get(self):
- with catch_zombie(self):
- return cext_posix.getpriority(self.pid)
+ return cext_posix.getpriority(self.pid)
@wrap_exceptions
def nice_set(self, value):
- with catch_zombie(self):
- return cext_posix.setpriority(self.pid, value)
+ return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def status(self):
diff --git a/contrib/python/psutil/py3/psutil/_psposix.py b/contrib/python/psutil/py3/psutil/_psposix.py
index 706dab9ae8..42bdfa7ef6 100644
--- a/contrib/python/psutil/py3/psutil/_psposix.py
+++ b/contrib/python/psutil/py3/psutil/_psposix.py
@@ -10,19 +10,25 @@ import signal
import sys
import time
+from ._common import MACOS
+from ._common import TimeoutExpired
from ._common import memoize
from ._common import sdiskusage
-from ._common import TimeoutExpired
from ._common import usage_percent
+from ._compat import PY3
from ._compat import ChildProcessError
from ._compat import FileNotFoundError
from ._compat import InterruptedError
from ._compat import PermissionError
from ._compat import ProcessLookupError
-from ._compat import PY3
from ._compat import unicode
-if sys.version_info >= (3, 4):
+
+if MACOS:
+ from . import _psutil_osx
+
+
+if PY3:
import enum
else:
enum = None
@@ -57,7 +63,8 @@ def pid_exists(pid):
# https://bugs.python.org/issue21076
if enum is not None and hasattr(signal, "Signals"):
Negsignal = enum.IntEnum(
- 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals]))
+ 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals])
+ )
def negsig_to_enum(num):
"""Convert a negative signal value to an enum."""
@@ -65,17 +72,23 @@ if enum is not None and hasattr(signal, "Signals"):
return Negsignal(num)
except ValueError:
return num
+
else: # pragma: no cover
+
def negsig_to_enum(num):
return num
-def wait_pid(pid, timeout=None, proc_name=None,
- _waitpid=os.waitpid,
- _timer=getattr(time, 'monotonic', time.time),
- _min=min,
- _sleep=time.sleep,
- _pid_exists=pid_exists):
+def wait_pid(
+ pid,
+ timeout=None,
+ proc_name=None,
+ _waitpid=os.waitpid,
+ _timer=getattr(time, 'monotonic', time.time), # noqa: B008
+ _min=min,
+ _sleep=time.sleep,
+ _pid_exists=pid_exists,
+):
"""Wait for a process PID to terminate.
If the process terminated normally by calling exit(3) or _exit(2),
@@ -94,7 +107,9 @@ def wait_pid(pid, timeout=None, proc_name=None,
timeout=0 is also possible (either return immediately or raise).
"""
if pid <= 0:
- raise ValueError("can't wait for PID 0") # see "man waitpid"
+ # see "man waitpid"
+ msg = "can't wait for PID 0"
+ raise ValueError(msg)
interval = 0.0001
flags = 0
if timeout is not None:
@@ -130,7 +145,8 @@ def wait_pid(pid, timeout=None, proc_name=None,
# WNOHANG flag was used and PID is still running.
interval = sleep(interval)
continue
- elif os.WIFEXITED(status):
+
+ if os.WIFEXITED(status):
# Process terminated normally by calling exit(3) or _exit(2),
# or by returning from main(). The return value is the
# positive integer passed to *exit().
@@ -185,13 +201,16 @@ def disk_usage(path):
# Total space which is only available to root (unless changed
# at system level).
- total = (st.f_blocks * st.f_frsize)
+ total = st.f_blocks * st.f_frsize
# Remaining free space usable by root.
- avail_to_root = (st.f_bfree * st.f_frsize)
+ avail_to_root = st.f_bfree * st.f_frsize
# Remaining free space usable by user.
- avail_to_user = (st.f_bavail * st.f_frsize)
+ avail_to_user = st.f_bavail * st.f_frsize
# Total space being used in general.
- used = (total - avail_to_root)
+ used = total - avail_to_root
+ if MACOS:
+ # see: https://github.com/giampaolo/psutil/pull/2152
+ used = _psutil_osx.disk_usage_used(path, used)
# Total space which is available to user (same as 'total' but
# for the user).
total_user = used + avail_to_user
@@ -204,13 +223,14 @@ def disk_usage(path):
# reserved blocks that we are currently not considering:
# https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462
return sdiskusage(
- total=total, used=used, free=avail_to_user, percent=usage_percent_user)
+ total=total, used=used, free=avail_to_user, percent=usage_percent_user
+ )
@memoize
def get_terminal_map():
"""Get a map of device-id -> path as a dict.
- Used by Process.terminal()
+ Used by Process.terminal().
"""
ret = {}
ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
diff --git a/contrib/python/psutil/py3/psutil/_pssunos.py b/contrib/python/psutil/py3/psutil/_pssunos.py
index 5618bd4460..dddbece1f3 100644
--- a/contrib/python/psutil/py3/psutil/_pssunos.py
+++ b/contrib/python/psutil/py3/psutil/_pssunos.py
@@ -17,22 +17,22 @@ from . import _common
from . import _psposix
from . import _psutil_posix as cext_posix
from . import _psutil_sunos as cext
-from ._common import AccessDenied
from ._common import AF_INET6
+from ._common import AccessDenied
+from ._common import NoSuchProcess
+from ._common import ZombieProcess
from ._common import debug
from ._common import get_procfs_path
from ._common import isfile_strict
from ._common import memoize_when_activated
-from ._common import NoSuchProcess
from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
-from ._common import ZombieProcess
-from ._compat import b
+from ._compat import PY3
from ._compat import FileNotFoundError
from ._compat import PermissionError
from ._compat import ProcessLookupError
-from ._compat import PY3
+from ._compat import b
__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
@@ -89,7 +89,8 @@ proc_info_map = dict(
uid=8,
euid=9,
gid=10,
- egid=11)
+ egid=11,
+)
# =====================================================================
@@ -100,19 +101,22 @@ proc_info_map = dict(
# psutil.cpu_times()
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
# psutil.cpu_times(percpu=True)
-pcputimes = namedtuple('pcputimes',
- ['user', 'system', 'children_user', 'children_system'])
+pcputimes = namedtuple(
+ 'pcputimes', ['user', 'system', 'children_user', 'children_system']
+)
# psutil.virtual_memory()
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
# psutil.Process.memory_info()
pmem = namedtuple('pmem', ['rss', 'vms'])
pfullmem = pmem
# psutil.Process.memory_maps(grouped=True)
-pmmap_grouped = namedtuple('pmmap_grouped',
- ['path', 'rss', 'anonymous', 'locked'])
+pmmap_grouped = namedtuple(
+ 'pmmap_grouped', ['path', 'rss', 'anonymous', 'locked']
+)
# psutil.Process.memory_maps(grouped=False)
pmmap_ext = namedtuple(
- 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)
+)
# =====================================================================
@@ -140,10 +144,16 @@ def swap_memory():
# usr/src/cmd/swap/swap.c
# ...nevertheless I can't manage to obtain the same numbers as 'swap'
# cmdline utility, so let's parse its output (sigh!)
- p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
- os.environ['PATH'], 'swap', '-l'],
- stdout=subprocess.PIPE)
- stdout, stderr = p.communicate()
+ p = subprocess.Popen(
+ [
+ '/usr/bin/env',
+ 'PATH=/usr/sbin:/sbin:%s' % os.environ['PATH'],
+ 'swap',
+ '-l',
+ ],
+ stdout=subprocess.PIPE,
+ )
+ stdout, _ = p.communicate()
if PY3:
stdout = stdout.decode(sys.stdout.encoding)
if p.returncode != 0:
@@ -151,17 +161,19 @@ def swap_memory():
lines = stdout.strip().split('\n')[1:]
if not lines:
- raise RuntimeError('no swap device(s) configured')
+ msg = 'no swap device(s) configured'
+ raise RuntimeError(msg)
total = free = 0
for line in lines:
line = line.split()
- t, f = line[3:4]
+ t, f = line[3:5]
total += int(int(t) * 512)
free += int(int(f) * 512)
used = total - free
percent = usage_percent(used, total, round_=1)
- return _common.sswap(total, used, free, percent,
- sin * PAGE_SIZE, sout * PAGE_SIZE)
+ return _common.sswap(
+ total, used, free, percent, sin * PAGE_SIZE, sout * PAGE_SIZE
+ )
# =====================================================================
@@ -170,13 +182,13 @@ def swap_memory():
def cpu_times():
- """Return system-wide CPU times as a named tuple"""
+ """Return system-wide CPU times as a named tuple."""
ret = cext.per_cpu_times()
return scputimes(*[sum(x) for x in zip(*ret)])
def per_cpu_times():
- """Return system per-CPU times as a list of named tuples"""
+ """Return system per-CPU times as a list of named tuples."""
ret = cext.per_cpu_times()
return [scputimes(*x) for x in ret]
@@ -190,17 +202,18 @@ def cpu_count_logical():
return None
-def cpu_count_physical():
- """Return the number of physical CPUs in the system."""
- return cext.cpu_count_phys()
+def cpu_count_cores():
+ """Return the number of CPU cores in the system."""
+ return cext.cpu_count_cores()
def cpu_stats():
"""Return various CPU stats as a named tuple."""
ctx_switches, interrupts, syscalls, traps = cext.cpu_stats()
soft_interrupts = 0
- return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
- syscalls)
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls
+ )
# =====================================================================
@@ -231,11 +244,12 @@ def disk_partitions(all=False):
continue
except OSError as err:
# https://github.com/giampaolo/psutil/issues/1674
- debug("skipping %r: %r" % (mountpoint, err))
+ debug("skipping %r: %s" % (mountpoint, err))
continue
maxfile = maxpath = None # set later
- ntuple = _common.sdiskpart(device, mountpoint, fstype, opts,
- maxfile, maxpath)
+ ntuple = _common.sdiskpart(
+ device, mountpoint, fstype, opts, maxfile, maxpath
+ )
retlist.append(ntuple)
return retlist
@@ -258,8 +272,10 @@ def net_connections(kind, _pid=-1):
if _pid == -1:
cmap.pop('unix', 0)
if kind not in cmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in cmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in cmap]))
+ )
families, types = _common.conn_tmap[kind]
rawlist = cext.net_connections(_pid)
ret = set()
@@ -293,7 +309,7 @@ def net_if_stats():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu, '')
return ret
@@ -345,6 +361,7 @@ def wrap_exceptions(fun):
"""Call callable into a try/except clause and translate ENOENT,
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
@@ -366,10 +383,11 @@ def wrap_exceptions(fun):
else:
raise
raise
+
return wrapper
-class Process(object):
+class Process:
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
@@ -404,8 +422,9 @@ class Process(object):
@wrap_exceptions
@memoize_when_activated
def _proc_basic_info(self):
- if self.pid == 0 and not \
- os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)):
+ if self.pid == 0 and not os.path.exists(
+ '%s/%s/psinfo' % (self._procfs_path, self.pid)
+ ):
raise AccessDenied(self.pid)
ret = cext.proc_basic_info(self.pid, self._procfs_path)
assert len(ret) == len(proc_info_map)
@@ -425,9 +444,10 @@ class Process(object):
def exe(self):
try:
return os.readlink(
- "%s/%s/path/a.out" % (self._procfs_path, self.pid))
+ "%s/%s/path/a.out" % (self._procfs_path, self.pid)
+ )
except OSError:
- pass # continue and guess the exe name from the cmdline
+ pass # continue and guess the exe name from the cmdline
# Will be guessed later from cmdline but we want to explicitly
# invoke cmdline here in order to get an AccessDenied
# exception if the user has not enough privileges.
@@ -518,13 +538,13 @@ class Process(object):
def terminal(self):
procfs_path = self._procfs_path
hit_enoent = False
- tty = wrap_exceptions(
- self._proc_basic_info()[proc_info_map['ttynr']])
+ tty = wrap_exceptions(self._proc_basic_info()[proc_info_map['ttynr']])
if tty != cext.PRNODEV:
for x in (0, 1, 2, 255):
try:
return os.readlink(
- '%s/%d/path/%d' % (procfs_path, self.pid, x))
+ '%s/%d/path/%d' % (procfs_path, self.pid, x)
+ )
except FileNotFoundError:
hit_enoent = True
continue
@@ -542,7 +562,7 @@ class Process(object):
return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
except FileNotFoundError:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
- return None
+ return ""
@wrap_exceptions
def memory_info(self):
@@ -569,7 +589,8 @@ class Process(object):
tid = int(tid)
try:
utime, stime = cext.query_process_thread(
- self.pid, tid, procfs_path)
+ self.pid, tid, procfs_path
+ )
except EnvironmentError as err:
if err.errno == errno.EOVERFLOW and not IS_64_BIT:
# We may get here if we attempt to query a 64bit process
@@ -617,13 +638,15 @@ class Process(object):
"""Get UNIX sockets used by process by parsing 'pfiles' output."""
# TODO: rewrite this in C (...but the damn netstat source code
# does not include this part! Argh!!)
- cmd = "pfiles %s" % pid
- p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ cmd = ["pfiles", str(pid)]
+ p = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
stdout, stderr = p.communicate()
if PY3:
- stdout, stderr = [x.decode(sys.stdout.encoding)
- for x in (stdout, stderr)]
+ stdout, stderr = (
+ x.decode(sys.stdout.encoding) for x in (stdout, stderr)
+ )
if p.returncode != 0:
if 'permission denied' in stderr.lower():
raise AccessDenied(self.pid, self._name)
@@ -659,8 +682,10 @@ class Process(object):
# UNIX sockets
if kind in ('all', 'unix'):
- ret.extend([_common.pconn(*conn) for conn in
- self._get_unix_sockets(self.pid)])
+ ret.extend([
+ _common.pconn(*conn)
+ for conn in self._get_unix_sockets(self.pid)
+ ])
return ret
nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
@@ -669,8 +694,10 @@ class Process(object):
@wrap_exceptions
def memory_maps(self):
def toaddr(start, end):
- return '%s-%s' % (hex(start)[2:].strip('L'),
- hex(end)[2:].strip('L'))
+ return '%s-%s' % (
+ hex(start)[2:].strip('L'),
+ hex(end)[2:].strip('L'),
+ )
procfs_path = self._procfs_path
retlist = []
@@ -695,7 +722,8 @@ class Process(object):
if not name.startswith('['):
try:
name = os.readlink(
- '%s/%s/path/%s' % (procfs_path, self.pid, name))
+ '%s/%s/path/%s' % (procfs_path, self.pid, name)
+ )
except OSError as err:
if err.errno == errno.ENOENT:
# sometimes the link may not be resolved by
@@ -720,7 +748,8 @@ class Process(object):
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(
- *cext.proc_num_ctx_switches(self.pid, self._procfs_path))
+ *cext.proc_num_ctx_switches(self.pid, self._procfs_path)
+ )
@wrap_exceptions
def wait(self, timeout=None):
diff --git a/contrib/python/psutil/py3/psutil/_psutil_common.c b/contrib/python/psutil/py3/psutil/_psutil_common.c
index f371167f3e..76b197917b 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_common.c
+++ b/contrib/python/psutil/py3/psutil/_psutil_common.c
@@ -15,15 +15,13 @@
// ====================================================================
int PSUTIL_DEBUG = 0;
-int PSUTIL_TESTING = 0;
-// PSUTIL_CONN_NONE
// ====================================================================
// --- Backward compatibility with missing Python.h APIs
// ====================================================================
-// PyPy on Windows
+// PyPy on Windows. Missing APIs added in PyPy 7.3.14.
#if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
#if !defined(PyErr_SetFromWindowsErrWithFilename)
PyObject *
@@ -61,6 +59,18 @@ error:
#endif // !defined(PyErr_SetFromWindowsErrWithFilename)
+#if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)
+PyObject *
+PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type,
+ int ierr,
+ PyObject *filename) {
+ // Original function is too complex. Just raise OSError without
+ // filename.
+ return PyErr_SetFromWindowsErrWithFilename(ierr, NULL);
+}
+#endif // !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)
+
+
// PyPy 2.7
#if !defined(PyErr_SetFromWindowsErr)
PyObject *
@@ -84,8 +94,9 @@ PyErr_SetFromOSErrnoWithSyscall(const char *syscall) {
char fullmsg[1024];
#ifdef PSUTIL_WINDOWS
+ DWORD dwLastError = GetLastError();
sprintf(fullmsg, "(originated from %s)", syscall);
- PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg);
+ PyErr_SetFromWindowsErrWithFilename(dwLastError, fullmsg);
#else
PyObject *exc;
sprintf(fullmsg, "%s (originated from %s)", strerror(errno), syscall);
@@ -130,51 +141,47 @@ AccessDenied(const char *syscall) {
return NULL;
}
-
-// ====================================================================
-// --- Global utils
-// ====================================================================
-
/*
- * Enable testing mode. This has the same effect as setting PSUTIL_TESTING
- * env var. This dual method exists because updating os.environ on
- * Windows has no effect. Called on unit tests setup.
+ * Raise OverflowError if Python int value overflowed when converting to pid_t.
+ * Raise ValueError if Python int value is negative.
+ * Otherwise, return None.
*/
PyObject *
-psutil_set_testing(PyObject *self, PyObject *args) {
- PSUTIL_TESTING = 1;
- PSUTIL_DEBUG = 1;
- Py_INCREF(Py_None);
- return Py_None;
-}
-
+psutil_check_pid_range(PyObject *self, PyObject *args) {
+#ifdef PSUTIL_WINDOWS
+ DWORD pid;
+#else
+ pid_t pid;
+#endif
-/*
- * Print a debug message on stderr. No-op if PSUTIL_DEBUG env var is not set.
- */
-void
-psutil_debug(const char* format, ...) {
- va_list argptr;
- if (PSUTIL_DEBUG) {
- va_start(argptr, format);
- fprintf(stderr, "psutil-debug> ");
- vfprintf(stderr, format, argptr);
- fprintf(stderr, "\n");
- va_end(argptr);
+ if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (pid < 0) {
+ PyErr_SetString(PyExc_ValueError, "pid must be a positive integer");
+ return NULL;
}
+ Py_RETURN_NONE;
}
+// Enable or disable PSUTIL_DEBUG messages.
+PyObject *
+psutil_set_debug(PyObject *self, PyObject *args) {
+ PyObject *value;
+ int x;
-/*
- * Called on module import on all platforms.
- */
-int
-psutil_setup(void) {
- if (getenv("PSUTIL_DEBUG") != NULL)
+ if (!PyArg_ParseTuple(args, "O", &value))
+ return NULL;
+ x = PyObject_IsTrue(value);
+ if (x < 0) {
+ return NULL;
+ }
+ else if (x == 0) {
+ PSUTIL_DEBUG = 0;
+ }
+ else {
PSUTIL_DEBUG = 1;
- if (getenv("PSUTIL_TESTING") != NULL)
- PSUTIL_TESTING = 1;
- return 0;
+ }
+ Py_RETURN_NONE;
}
@@ -197,6 +204,15 @@ convert_kvm_err(const char *syscall, char *errbuf) {
}
#endif
+// ====================================================================
+// --- macOS
+// ====================================================================
+
+#ifdef PSUTIL_OSX
+#include <mach/mach_time.h>
+
+struct mach_timebase_info PSUTIL_MACH_TIMEBASE_INFO;
+#endif
// ====================================================================
// --- Windows
@@ -337,7 +353,7 @@ psutil_loadlibs() {
// minimum requirement: Win 7
GetActiveProcessorCount = psutil_GetProcAddress(
"kernel32", "GetActiveProcessorCount");
- // minumum requirement: Win 7
+ // minimum requirement: Win 7
GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib(
"kernel32", "GetLogicalProcessorInformationEx");
// minimum requirements: Windows Server Core
@@ -380,18 +396,6 @@ psutil_set_winver() {
}
-int
-psutil_load_globals() {
- if (psutil_loadlibs() != 0)
- return 1;
- if (psutil_set_winver() != 0)
- return 1;
- GetSystemInfo(&PSUTIL_SYSTEM_INFO);
- InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);
- return 0;
-}
-
-
/*
* Convert the hi and lo parts of a FILETIME structure or a LARGE_INTEGER
* to a UNIX time.
@@ -427,3 +431,24 @@ psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {
(ULONGLONG)li.LowPart);
}
#endif // PSUTIL_WINDOWS
+
+
+// Called on module import on all platforms.
+int
+psutil_setup(void) {
+ if (getenv("PSUTIL_DEBUG") != NULL)
+ PSUTIL_DEBUG = 1;
+
+#ifdef PSUTIL_WINDOWS
+ if (psutil_loadlibs() != 0)
+ return 1;
+ if (psutil_set_winver() != 0)
+ return 1;
+ GetSystemInfo(&PSUTIL_SYSTEM_INFO);
+ InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);
+#endif
+#ifdef PSUTIL_OSX
+ mach_timebase_info(&PSUTIL_MACH_TIMEBASE_INFO);
+#endif
+ return 0;
+}
diff --git a/contrib/python/psutil/py3/psutil/_psutil_common.h b/contrib/python/psutil/py3/psutil/_psutil_common.h
index cb0b399d8e..20c03ebd7c 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_common.h
+++ b/contrib/python/psutil/py3/psutil/_psutil_common.h
@@ -10,7 +10,6 @@
// --- Global vars / constants
// ====================================================================
-extern int PSUTIL_TESTING;
extern int PSUTIL_DEBUG;
// a signaler for connections without an actual status
static const int PSUTIL_CONN_NONE = 128;
@@ -32,11 +31,15 @@ static const int PSUTIL_CONN_NONE = 128;
#define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize
#endif
-#if defined(PSUTIL_WINDOWS) && \
- defined(PYPY_VERSION) && \
- !defined(PyErr_SetFromWindowsErrWithFilename)
- PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr,
- const char *filename);
+#if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
+ #if !defined(PyErr_SetFromWindowsErrWithFilename)
+ PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr,
+ const char *filename);
+ #endif
+ #if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject)
+ PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject(
+ PyObject *type, int ierr, PyObject *filename);
+ #endif
#endif
// --- _Py_PARSE_PID
@@ -100,10 +103,20 @@ PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall);
// --- Global utils
// ====================================================================
-PyObject* psutil_set_testing(PyObject *self, PyObject *args);
-void psutil_debug(const char* format, ...);
+PyObject* psutil_check_pid_range(PyObject *self, PyObject *args);
+PyObject* psutil_set_debug(PyObject *self, PyObject *args);
int psutil_setup(void);
+
+// Print a debug message on stderr.
+#define psutil_debug(...) do { \
+ if (! PSUTIL_DEBUG) \
+ break; \
+ fprintf(stderr, "psutil-debug [%s:%d]> ", __FILE__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n");} while(0)
+
+
// ====================================================================
// --- BSD
// ====================================================================
@@ -111,6 +124,16 @@ int psutil_setup(void);
void convert_kvm_err(const char *syscall, char *errbuf);
// ====================================================================
+// --- macOS
+// ====================================================================
+
+#ifdef PSUTIL_OSX
+ #include <mach/mach_time.h>
+
+ extern struct mach_timebase_info PSUTIL_MACH_TIMEBASE_INFO;
+#endif
+
+// ====================================================================
// --- Windows
// ====================================================================
@@ -149,7 +172,6 @@ void convert_kvm_err(const char *syscall, char *errbuf);
#define AF_INET6 23
#endif
- int psutil_load_globals();
PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname);
PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname);
PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall);
diff --git a/contrib/python/psutil/py3/psutil/_psutil_linux.c b/contrib/python/psutil/py3/psutil/_psutil_linux.c
index 5836cd6b78..292e1c5524 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_linux.c
+++ b/contrib/python/psutil/py3/psutil/_psutil_linux.c
@@ -10,504 +10,42 @@
#define _GNU_SOURCE 1
#endif
#include <Python.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <mntent.h>
-#include <features.h>
-#include <utmp.h>
-#include <sched.h>
-#include <linux/version.h>
-#include <sys/syscall.h>
-#include <sys/sysinfo.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <linux/sockios.h>
-#include <linux/if.h>
-#include <sys/resource.h>
-
-// see: https://github.com/giampaolo/psutil/issues/659
-#ifdef PSUTIL_ETHTOOL_MISSING_TYPES
- #include <linux/types.h>
- typedef __u64 u64;
- typedef __u32 u32;
- typedef __u16 u16;
- typedef __u8 u8;
-#endif
-/* Avoid redefinition of struct sysinfo with musl libc */
-#define _LINUX_SYSINFO_H
-#include <linux/ethtool.h>
-
-/* The minimum number of CPUs allocated in a cpu_set_t */
-static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT;
-
-// Linux >= 2.6.13
-#define PSUTIL_HAVE_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
-
-// Should exist starting from CentOS 6 (year 2011).
-#ifdef CPU_ALLOC
- #define PSUTIL_HAVE_CPU_AFFINITY
-#endif
+#include <linux/ethtool.h> // DUPLEX_*
#include "_psutil_common.h"
-#include "_psutil_posix.h"
-
-// May happen on old RedHat versions, see:
-// https://github.com/giampaolo/psutil/issues/607
-#ifndef DUPLEX_UNKNOWN
- #define DUPLEX_UNKNOWN 0xff
-#endif
-
-
-#if PSUTIL_HAVE_IOPRIO
-enum {
- IOPRIO_WHO_PROCESS = 1,
-};
-
-static inline int
-ioprio_get(int which, int who) {
- return syscall(__NR_ioprio_get, which, who);
-}
-
-static inline int
-ioprio_set(int which, int who, int ioprio) {
- return syscall(__NR_ioprio_set, which, who, ioprio);
-}
-
-#define IOPRIO_CLASS_SHIFT 13
-#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
-
-#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
-#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
-#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
-
-
-/*
- * Return a (ioclass, iodata) Python tuple representing process I/O priority.
- */
-static PyObject *
-psutil_proc_ioprio_get(PyObject *self, PyObject *args) {
- pid_t pid;
- int ioprio, ioclass, iodata;
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
- if (ioprio == -1)
- return PyErr_SetFromErrno(PyExc_OSError);
- ioclass = IOPRIO_PRIO_CLASS(ioprio);
- iodata = IOPRIO_PRIO_DATA(ioprio);
- return Py_BuildValue("ii", ioclass, iodata);
-}
-
-
-/*
- * A wrapper around ioprio_set(); sets process I/O priority.
- * ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE
- * or 0. iodata goes from 0 to 7 depending on ioclass specified.
- */
-static PyObject *
-psutil_proc_ioprio_set(PyObject *self, PyObject *args) {
- pid_t pid;
- int ioprio, ioclass, iodata;
- int retval;
-
- if (! PyArg_ParseTuple(
- args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) {
- return NULL;
- }
- ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
- retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
- if (retval == -1)
- return PyErr_SetFromErrno(PyExc_OSError);
- Py_RETURN_NONE;
-}
-#endif
-
-
-/*
- * Return disk mounted partitions as a list of tuples including device,
- * mount point and filesystem type
- */
-static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args) {
- FILE *file = NULL;
- struct mntent *entry;
- char *mtab_path;
- PyObject *py_dev = NULL;
- PyObject *py_mountp = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
-
- if (!PyArg_ParseTuple(args, "s", &mtab_path))
- return NULL;
-
- Py_BEGIN_ALLOW_THREADS
- file = setmntent(mtab_path, "r");
- Py_END_ALLOW_THREADS
- if ((file == 0) || (file == NULL)) {
- psutil_debug("setmntent() failed");
- PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path);
- goto error;
- }
-
- while ((entry = getmntent(file))) {
- if (entry == NULL) {
- PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
- goto error;
- }
- py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname);
- if (! py_dev)
- goto error;
- py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir);
- if (! py_mountp)
- goto error;
- py_tuple = Py_BuildValue("(OOss)",
- py_dev, // device
- py_mountp, // mount point
- entry->mnt_type, // fs type
- entry->mnt_opts); // options
- if (! py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_dev);
- Py_CLEAR(py_mountp);
- Py_CLEAR(py_tuple);
- }
- endmntent(file);
- return py_retlist;
-
-error:
- if (file != NULL)
- endmntent(file);
- Py_XDECREF(py_dev);
- Py_XDECREF(py_mountp);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- return NULL;
-}
-
-
-/*
- * A wrapper around sysinfo(), return system memory usage statistics.
- */
-static PyObject *
-psutil_linux_sysinfo(PyObject *self, PyObject *args) {
- struct sysinfo info;
-
- if (sysinfo(&info) != 0)
- return PyErr_SetFromErrno(PyExc_OSError);
- // note: boot time might also be determined from here
- return Py_BuildValue(
- "(kkkkkkI)",
- info.totalram, // total
- info.freeram, // free
- info.bufferram, // buffer
- info.sharedram, // shared
- info.totalswap, // swap tot
- info.freeswap, // swap free
- info.mem_unit // multiplier
- );
-}
-
-
-/*
- * Return process CPU affinity as a Python list
- */
-#ifdef PSUTIL_HAVE_CPU_AFFINITY
-
-static PyObject *
-psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
- int cpu, ncpus, count, cpucount_s;
- pid_t pid;
- size_t setsize;
- cpu_set_t *mask = NULL;
- PyObject *py_list = NULL;
+#include "arch/linux/disk.h"
+#include "arch/linux/mem.h"
+#include "arch/linux/net.h"
+#include "arch/linux/proc.h"
+#include "arch/linux/users.h"
- if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- ncpus = NCPUS_START;
- while (1) {
- setsize = CPU_ALLOC_SIZE(ncpus);
- mask = CPU_ALLOC(ncpus);
- if (mask == NULL) {
- psutil_debug("CPU_ALLOC() failed");
- return PyErr_NoMemory();
- }
- if (sched_getaffinity(pid, setsize, mask) == 0)
- break;
- CPU_FREE(mask);
- if (errno != EINVAL)
- return PyErr_SetFromErrno(PyExc_OSError);
- if (ncpus > INT_MAX / 2) {
- PyErr_SetString(PyExc_OverflowError, "could not allocate "
- "a large enough CPU set");
- return NULL;
- }
- ncpus = ncpus * 2;
- }
-
- py_list = PyList_New(0);
- if (py_list == NULL)
- goto error;
-
- cpucount_s = CPU_COUNT_S(setsize, mask);
- for (cpu = 0, count = cpucount_s; count; cpu++) {
- if (CPU_ISSET_S(cpu, setsize, mask)) {
-#if PY_MAJOR_VERSION >= 3
- PyObject *cpu_num = PyLong_FromLong(cpu);
-#else
- PyObject *cpu_num = PyInt_FromLong(cpu);
-#endif
- if (cpu_num == NULL)
- goto error;
- if (PyList_Append(py_list, cpu_num)) {
- Py_DECREF(cpu_num);
- goto error;
- }
- Py_DECREF(cpu_num);
- --count;
- }
- }
- CPU_FREE(mask);
- return py_list;
-
-error:
- if (mask)
- CPU_FREE(mask);
- Py_XDECREF(py_list);
- return NULL;
-}
-
-
-/*
- * Set process CPU affinity; expects a bitmask
- */
-static PyObject *
-psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
- cpu_set_t cpu_set;
- size_t len;
- pid_t pid;
- int i, seq_len;
- PyObject *py_cpu_set;
- PyObject *py_cpu_seq = NULL;
-
- if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set))
- return NULL;
-
- if (!PySequence_Check(py_cpu_set)) {
- PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s",
- Py_TYPE(py_cpu_set)->tp_name);
- goto error;
- }
-
- py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
- if (!py_cpu_seq)
- goto error;
- seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
- CPU_ZERO(&cpu_set);
- for (i = 0; i < seq_len; i++) {
- PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
-#if PY_MAJOR_VERSION >= 3
- long value = PyLong_AsLong(item);
-#else
- long value = PyInt_AsLong(item);
-#endif
- if ((value == -1) || PyErr_Occurred()) {
- if (!PyErr_Occurred())
- PyErr_SetString(PyExc_ValueError, "invalid CPU value");
- goto error;
- }
- CPU_SET(value, &cpu_set);
- }
-
- len = sizeof(cpu_set);
- if (sched_setaffinity(pid, len, &cpu_set)) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- Py_DECREF(py_cpu_seq);
- Py_RETURN_NONE;
-
-error:
- if (py_cpu_seq != NULL)
- Py_DECREF(py_cpu_seq);
- return NULL;
-}
-#endif /* PSUTIL_HAVE_CPU_AFFINITY */
-
-
-/*
- * Return currently connected users as a list of tuples.
- */
-static PyObject *
-psutil_users(PyObject *self, PyObject *args) {
- struct utmp *ut;
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_tuple = NULL;
- PyObject *py_username = NULL;
- PyObject *py_tty = NULL;
- PyObject *py_hostname = NULL;
- PyObject *py_user_proc = NULL;
-
- if (py_retlist == NULL)
- return NULL;
- setutent();
- while (NULL != (ut = getutent())) {
- py_tuple = NULL;
- py_user_proc = NULL;
- if (ut->ut_type == USER_PROCESS)
- py_user_proc = Py_True;
- else
- py_user_proc = Py_False;
- py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
- if (! py_username)
- goto error;
- py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
- if (! py_tty)
- goto error;
- py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
- if (! py_hostname)
- goto error;
-
- py_tuple = Py_BuildValue(
- "OOOfO" _Py_PARSE_PID,
- py_username, // username
- py_tty, // tty
- py_hostname, // hostname
- (float)ut->ut_tv.tv_sec, // tstamp
- py_user_proc, // (bool) user process
- ut->ut_pid // process id
- );
- if (! py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_username);
- Py_CLEAR(py_tty);
- Py_CLEAR(py_hostname);
- Py_CLEAR(py_tuple);
- }
- endutent();
- return py_retlist;
-
-error:
- Py_XDECREF(py_username);
- Py_XDECREF(py_tty);
- Py_XDECREF(py_hostname);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- endutent();
- return NULL;
-}
-
-
-/*
- * Return stats about a particular network
- * interface. References:
- * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
- * http://www.i-scream.org/libstatgrab/
- */
-static PyObject*
-psutil_net_if_duplex_speed(PyObject* self, PyObject* args) {
- char *nic_name;
- int sock = 0;
- int ret;
- int duplex;
- int speed;
- struct ifreq ifr;
- struct ethtool_cmd ethcmd;
- PyObject *py_retlist = NULL;
-
- if (! PyArg_ParseTuple(args, "s", &nic_name))
- return NULL;
-
- sock = socket(AF_INET, SOCK_DGRAM, 0);
- if (sock == -1)
- return PyErr_SetFromOSErrnoWithSyscall("socket()");
- PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
-
- // duplex and speed
- memset(&ethcmd, 0, sizeof ethcmd);
- ethcmd.cmd = ETHTOOL_GSET;
- ifr.ifr_data = (void *)&ethcmd;
- ret = ioctl(sock, SIOCETHTOOL, &ifr);
-
- if (ret != -1) {
- duplex = ethcmd.duplex;
- speed = ethcmd.speed;
- }
- else {
- if ((errno == EOPNOTSUPP) || (errno == EINVAL)) {
- // EOPNOTSUPP may occur in case of wi-fi cards.
- // For EINVAL see:
- // https://github.com/giampaolo/psutil/issues/797
- // #issuecomment-202999532
- duplex = DUPLEX_UNKNOWN;
- speed = 0;
- }
- else {
- PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)");
- goto error;
- }
- }
-
- py_retlist = Py_BuildValue("[ii]", duplex, speed);
- if (!py_retlist)
- goto error;
- close(sock);
- return py_retlist;
-
-error:
- if (sock != -1)
- close(sock);
- return NULL;
-}
-
-
-/*
- * Module init.
- */
static PyMethodDef mod_methods[] = {
// --- per-process functions
-
-#if PSUTIL_HAVE_IOPRIO
- {"proc_ioprio_get", psutil_proc_ioprio_get, METH_VARARGS,
- "Get process I/O priority"},
- {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS,
- "Set process I/O priority"},
+#ifdef PSUTIL_HAVE_IOPRIO
+ {"proc_ioprio_get", psutil_proc_ioprio_get, METH_VARARGS},
+ {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS},
#endif
#ifdef PSUTIL_HAVE_CPU_AFFINITY
- {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
- "Return process CPU affinity as a Python long (the bitmask)."},
- {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
- "Set process CPU affinity; expects a bitmask."},
+ {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS},
+ {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS},
#endif
-
// --- system related functions
-
- {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
- "Return disk mounted partitions as a list of tuples including "
- "device, mount point and filesystem type"},
- {"users", psutil_users, METH_VARARGS,
- "Return currently connected users as a list of tuples"},
- {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
- "Return duplex and speed info about a NIC"},
-
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS},
+ {"users", psutil_users, METH_VARARGS},
+ {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS},
// --- linux specific
-
- {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS,
- "A wrapper around sysinfo(), return system memory usage statistics"},
+ {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS},
// --- others
- {"set_testing", psutil_set_testing, METH_NOARGS,
- "Set psutil in testing mode"},
-
+ {"check_pid_range", psutil_check_pid_range, METH_VARARGS},
+ {"set_debug", psutil_set_debug, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
+// May happen on old RedHat versions, see:
+// https://github.com/giampaolo/psutil/issues/607
+#ifndef DUPLEX_UNKNOWN
+ #define DUPLEX_UNKNOWN 0xff
+#endif
#if PY_MAJOR_VERSION >= 3
diff --git a/contrib/python/psutil/py3/psutil/_psutil_osx.c b/contrib/python/psutil/py3/psutil/_psutil_osx.c
index 13d0bb6856..369fbbfb48 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_osx.c
+++ b/contrib/python/psutil/py3/psutil/_psutil_osx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
@@ -7,1816 +7,55 @@
*/
#include <Python.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <utmpx.h>
-#include <sys/sysctl.h>
-#include <sys/vmmeter.h>
-#include <libproc.h>
-#include <sys/proc_info.h>
+#include <sys/proc.h>
#include <netinet/tcp_fsm.h>
-#include <arpa/inet.h>
-#include <net/if_dl.h>
-#include <pwd.h>
-#include <unistd.h>
-
-#include <mach/mach.h>
-#include <mach/task.h>
-#include <mach/mach_init.h>
-#include <mach/host_info.h>
-#include <mach/mach_host.h>
-#include <mach/mach_traps.h>
-#include <mach/mach_vm.h>
-#include <mach/shared_region.h>
-
-#include <mach-o/loader.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/storage/IOBlockStorageDriver.h>
-#include <IOKit/storage/IOMedia.h>
-#include <IOKit/IOBSD.h>
-#include <IOKit/ps/IOPowerSources.h>
-#include <IOKit/ps/IOPSKeys.h>
#include "_psutil_common.h"
-#include "_psutil_posix.h"
-#include "arch/osx/process_info.h"
-
-
-#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
-
-static PyObject *ZombieProcessError;
-
-
-/*
- * A wrapper around host_statistics() invoked with HOST_VM_INFO.
- */
-int
-psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
- kern_return_t ret;
- mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
- mach_port_t mport = mach_host_self();
-
- ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
- if (ret != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "host_statistics(HOST_VM_INFO) syscall failed: %s",
- mach_error_string(ret));
- return 0;
- }
- mach_port_deallocate(mach_task_self(), mport);
- return 1;
-}
-
-
-/*
- * A wrapper around task_for_pid() which sucks big time:
- * - it's not documented
- * - errno is set only sometimes
- * - sometimes errno is ENOENT (?!?)
- * - for PIDs != getpid() or PIDs which are not members of the procmod
- * it requires root
- * As such we can only guess what the heck went wrong and fail either
- * with NoSuchProcess, ZombieProcessError or giveup with AccessDenied.
- * Here's some history:
- * https://github.com/giampaolo/psutil/issues/1181
- * https://github.com/giampaolo/psutil/issues/1209
- * https://github.com/giampaolo/psutil/issues/1291#issuecomment-396062519
- */
-int
-psutil_task_for_pid(pid_t pid, mach_port_t *task)
-{
- // See: https://github.com/giampaolo/psutil/issues/1181
- kern_return_t err = KERN_SUCCESS;
-
- err = task_for_pid(mach_task_self(), pid, task);
- if (err != KERN_SUCCESS) {
- if (psutil_pid_exists(pid) == 0)
- NoSuchProcess("task_for_pid");
- else if (psutil_is_zombie(pid) == 1)
- PyErr_SetString(ZombieProcessError,
- "task_for_pid -> psutil_is_zombie -> 1");
- else {
- psutil_debug(
- "task_for_pid() failed (pid=%ld, err=%i, errno=%i, msg='%s'); "
- "setting AccessDenied()",
- pid, err, errno, mach_error_string(err));
- AccessDenied("task_for_pid");
- }
- return 1;
- }
- return 0;
-}
-
-
-/*
- * Return a Python list of all the PIDs running on the system.
- */
-static PyObject *
-psutil_pids(PyObject *self, PyObject *args) {
- kinfo_proc *proclist = NULL;
- kinfo_proc *orig_address = NULL;
- size_t num_processes;
- size_t idx;
- PyObject *py_pid = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
-
- if (psutil_get_proc_list(&proclist, &num_processes) != 0)
- goto error;
-
- // save the address of proclist so we can free it later
- orig_address = proclist;
- for (idx = 0; idx < num_processes; idx++) {
- py_pid = PyLong_FromPid(proclist->kp_proc.p_pid);
- if (! py_pid)
- goto error;
- if (PyList_Append(py_retlist, py_pid))
- goto error;
- Py_CLEAR(py_pid);
- proclist++;
- }
- free(orig_address);
-
- return py_retlist;
-
-error:
- Py_XDECREF(py_pid);
- Py_DECREF(py_retlist);
- if (orig_address != NULL)
- free(orig_address);
- return NULL;
-}
-
-
-/*
- * Return multiple process info as a Python tuple in one shot by
- * using sysctl() and filling up a kinfo_proc struct.
- * It should be possible to do this for all processes without
- * incurring into permission (EPERM) errors.
- * This will also succeed for zombie processes returning correct
- * information.
- */
-static PyObject *
-psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) {
- pid_t pid;
- struct kinfo_proc kp;
- PyObject *py_name;
- PyObject *py_retlist;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
-
- py_name = PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
- if (! py_name) {
- // Likely a decoding error. We don't want to fail the whole
- // operation. The python module may retry with proc_name().
- PyErr_Clear();
- py_name = Py_None;
- }
-
- py_retlist = Py_BuildValue(
- _Py_PARSE_PID "llllllidiO",
- kp.kp_eproc.e_ppid, // (pid_t) ppid
- (long)kp.kp_eproc.e_pcred.p_ruid, // (long) real uid
- (long)kp.kp_eproc.e_ucred.cr_uid, // (long) effective uid
- (long)kp.kp_eproc.e_pcred.p_svuid, // (long) saved uid
- (long)kp.kp_eproc.e_pcred.p_rgid, // (long) real gid
- (long)kp.kp_eproc.e_ucred.cr_groups[0], // (long) effective gid
- (long)kp.kp_eproc.e_pcred.p_svgid, // (long) saved gid
- kp.kp_eproc.e_tdev, // (int) tty nr
- PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime), // (double) create time
- (int)kp.kp_proc.p_stat, // (int) status
- py_name // (pystr) name
- );
-
- if (py_retlist != NULL) {
- // XXX shall we decref() also in case of Py_BuildValue() error?
- Py_DECREF(py_name);
- }
- return py_retlist;
-}
-
-
-/*
- * Return multiple process info as a Python tuple in one shot by
- * using proc_pidinfo(PROC_PIDTASKINFO) and filling a proc_taskinfo
- * struct.
- * Contrarily from proc_kinfo above this function will fail with
- * EACCES for PIDs owned by another user and with ESRCH for zombie
- * processes.
- */
-static PyObject *
-psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) {
- pid_t pid;
- struct proc_taskinfo pti;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) <= 0)
- return NULL;
-
- return Py_BuildValue(
- "(ddKKkkkk)",
- (float)pti.pti_total_user / 1000000000.0, // (float) cpu user time
- (float)pti.pti_total_system / 1000000000.0, // (float) cpu sys time
- // Note about memory: determining other mem stats on macOS is a mess:
- // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
- // I just give up.
- // struct proc_regioninfo pri;
- // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, sizeof(pri))
- pti.pti_resident_size, // (uns long long) rss
- pti.pti_virtual_size, // (uns long long) vms
- pti.pti_faults, // (uns long) number of page faults (pages)
- pti.pti_pageins, // (uns long) number of actual pageins (pages)
- pti.pti_threadnum, // (uns long) num threads
- // Unvoluntary value seems not to be available;
- // pti.pti_csw probably refers to the sum of the two;
- // getrusage() numbers seems to confirm this theory.
- pti.pti_csw // (uns long) voluntary ctx switches
- );
-}
-
-
-/*
- * Return process name from kinfo_proc as a Python string.
- */
-static PyObject *
-psutil_proc_name(PyObject *self, PyObject *args) {
- pid_t pid;
- struct kinfo_proc kp;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
-}
-
-
-/*
- * Return process current working directory.
- * Raises NSP in case of zombie process.
- */
-static PyObject *
-psutil_proc_cwd(PyObject *self, PyObject *args) {
- pid_t pid;
- struct proc_vnodepathinfo pathinfo;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- if (psutil_proc_pidinfo(
- pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)) <= 0)
- {
- return NULL;
- }
-
- return PyUnicode_DecodeFSDefault(pathinfo.pvi_cdir.vip_path);
-}
-
-
-/*
- * Return path of the process executable.
- */
-static PyObject *
-psutil_proc_exe(PyObject *self, PyObject *args) {
- pid_t pid;
- char buf[PATH_MAX];
- int ret;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- errno = 0;
- ret = proc_pidpath(pid, &buf, sizeof(buf));
- if (ret == 0) {
- if (pid == 0) {
- AccessDenied("automatically set for PID 0");
- return NULL;
- }
- else if (errno == ENOENT) {
- // It may happen (file not found error) if the process is
- // still alive but the executable which launched it got
- // deleted, see:
- // https://github.com/giampaolo/psutil/issues/1738
- return Py_BuildValue("s", "");
- }
- else {
- psutil_raise_for_pid(pid, "proc_pidpath()");
- return NULL;
- }
- }
- return PyUnicode_DecodeFSDefault(buf);
-}
-
-
-/*
- * Return process cmdline as a Python list of cmdline arguments.
- */
-static PyObject *
-psutil_proc_cmdline(PyObject *self, PyObject *args) {
- pid_t pid;
- PyObject *py_retlist = NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- // get the commandline, defined in arch/osx/process_info.c
- py_retlist = psutil_get_cmdline(pid);
- return py_retlist;
-}
-
-
-/*
- * Return process environment as a Python string.
- */
-static PyObject *
-psutil_proc_environ(PyObject *self, PyObject *args) {
- pid_t pid;
- PyObject *py_retdict = NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- // get the environment block, defined in arch/osx/process_info.c
- py_retdict = psutil_get_environ(pid);
- return py_retdict;
-}
-
-
-/*
- * Return the number of logical CPUs in the system.
- * XXX this could be shared with BSD.
- */
-static PyObject *
-psutil_cpu_count_logical(PyObject *self, PyObject *args) {
- /*
- int mib[2];
- int ncpu;
- size_t len;
- mib[0] = CTL_HW;
- mib[1] = HW_NCPU;
- len = sizeof(ncpu);
-
- if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
- Py_RETURN_NONE; // mimic os.cpu_count()
- else
- return Py_BuildValue("i", ncpu);
- */
- int num;
- size_t size = sizeof(int);
-
- if (sysctlbyname("hw.logicalcpu", &num, &size, NULL, 2))
- Py_RETURN_NONE; // mimic os.cpu_count()
- else
- return Py_BuildValue("i", num);
-}
-
-
-/*
- * Return the number of physical CPUs in the system.
- */
-static PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args) {
- int num;
- size_t size = sizeof(int);
-
- if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0))
- Py_RETURN_NONE; // mimic os.cpu_count()
- else
- return Py_BuildValue("i", num);
-}
-
-
-/*
- * Indicates if the given virtual address on the given architecture is in the
- * shared VM region.
- */
-static bool
-psutil_in_shared_region(mach_vm_address_t addr, cpu_type_t type) {
- mach_vm_address_t base;
- mach_vm_address_t size;
-
- switch (type) {
- case CPU_TYPE_ARM:
- base = SHARED_REGION_BASE_ARM;
- size = SHARED_REGION_SIZE_ARM;
- break;
- case CPU_TYPE_I386:
- base = SHARED_REGION_BASE_I386;
- size = SHARED_REGION_SIZE_I386;
- break;
- case CPU_TYPE_X86_64:
- base = SHARED_REGION_BASE_X86_64;
- size = SHARED_REGION_SIZE_X86_64;
- break;
- default:
- return false;
- }
-
- return base <= addr && addr < (base + size);
-}
-
-
-/*
- * Returns the USS (unique set size) of the process. Reference:
- * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
- * nsMemoryReporterManager.cpp
- */
-static PyObject *
-psutil_proc_memory_uss(PyObject *self, PyObject *args) {
- pid_t pid;
- size_t len;
- cpu_type_t cpu_type;
- size_t private_pages = 0;
- mach_vm_size_t size = 0;
- mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
- kern_return_t kr;
- long pagesize = psutil_getpagesize();
- mach_vm_address_t addr = MACH_VM_MIN_ADDRESS;
- mach_port_t task = MACH_PORT_NULL;
- vm_region_top_info_data_t info;
- mach_port_t object_name;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- if (psutil_task_for_pid(pid, &task) != 0)
- return NULL;
-
- len = sizeof(cpu_type);
- if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
- return PyErr_SetFromOSErrnoWithSyscall(
- "sysctlbyname('sysctl.proc_cputype')");
- }
-
- // Roughly based on libtop_update_vm_regions in
- // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
- for (addr = 0; ; addr += size) {
- kr = mach_vm_region(
- task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info,
- &info_count, &object_name);
- if (kr == KERN_INVALID_ADDRESS) {
- // Done iterating VM regions.
- break;
- }
- else if (kr != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "mach_vm_region(VM_REGION_TOP_INFO) syscall failed");
- return NULL;
- }
-
- if (psutil_in_shared_region(addr, cpu_type) &&
- info.share_mode != SM_PRIVATE) {
- continue;
- }
-
- switch (info.share_mode) {
-#ifdef SM_LARGE_PAGE
- case SM_LARGE_PAGE:
- // NB: Large pages are not shareable and always resident.
-#endif
- case SM_PRIVATE:
- private_pages += info.private_pages_resident;
- private_pages += info.shared_pages_resident;
- break;
- case SM_COW:
- private_pages += info.private_pages_resident;
- if (info.ref_count == 1) {
- // Treat copy-on-write pages as private if they only
- // have one reference.
- private_pages += info.shared_pages_resident;
- }
- break;
- case SM_SHARED:
- default:
- break;
- }
- }
-
- mach_port_deallocate(mach_task_self(), task);
- return Py_BuildValue("K", private_pages * pagesize);
-}
-
-
-/*
- * Return system virtual memory stats.
- * See:
- * https://opensource.apple.com/source/system_cmds/system_cmds-790/
- * vm_stat.tproj/vm_stat.c.auto.html
- */
-static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args) {
- int mib[2];
- uint64_t total;
- size_t len = sizeof(total);
- vm_statistics_data_t vm;
- long pagesize = psutil_getpagesize();
- // physical mem
- mib[0] = CTL_HW;
- mib[1] = HW_MEMSIZE;
-
- // This is also available as sysctlbyname("hw.memsize").
- if (sysctl(mib, 2, &total, &len, NULL, 0)) {
- if (errno != 0)
- PyErr_SetFromErrno(PyExc_OSError);
- else
- PyErr_Format(
- PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
- return NULL;
- }
-
- // vm
- if (!psutil_sys_vminfo(&vm))
- return NULL;
-
- return Py_BuildValue(
- "KKKKKK",
- total,
- (unsigned long long) vm.active_count * pagesize, // active
- (unsigned long long) vm.inactive_count * pagesize, // inactive
- (unsigned long long) vm.wire_count * pagesize, // wired
- (unsigned long long) vm.free_count * pagesize, // free
- (unsigned long long) vm.speculative_count * pagesize // speculative
- );
-}
-
-
-/*
- * Return stats about swap memory.
- */
-static PyObject *
-psutil_swap_mem(PyObject *self, PyObject *args) {
- int mib[2];
- size_t size;
- struct xsw_usage totals;
- vm_statistics_data_t vmstat;
- long pagesize = psutil_getpagesize();
-
- mib[0] = CTL_VM;
- mib[1] = VM_SWAPUSAGE;
- size = sizeof(totals);
- if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
- if (errno != 0)
- PyErr_SetFromErrno(PyExc_OSError);
- else
- PyErr_Format(
- PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
- return NULL;
- }
- if (!psutil_sys_vminfo(&vmstat))
- return NULL;
-
- return Py_BuildValue(
- "LLLKK",
- totals.xsu_total,
- totals.xsu_used,
- totals.xsu_avail,
- (unsigned long long)vmstat.pageins * pagesize,
- (unsigned long long)vmstat.pageouts * pagesize);
-}
-
-
-/*
- * Return a Python tuple representing user, kernel and idle CPU times
- */
-static PyObject *
-psutil_cpu_times(PyObject *self, PyObject *args) {
- mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
- kern_return_t error;
- host_cpu_load_info_data_t r_load;
-
- mach_port_t host_port = mach_host_self();
- error = host_statistics(host_port, HOST_CPU_LOAD_INFO,
- (host_info_t)&r_load, &count);
- if (error != KERN_SUCCESS) {
- return PyErr_Format(
- PyExc_RuntimeError,
- "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: %s",
- mach_error_string(error));
- }
- mach_port_deallocate(mach_task_self(), host_port);
-
- return Py_BuildValue(
- "(dddd)",
- (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK,
- (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
- (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
- (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
- );
-}
-
-
-/*
- * Return a Python list of tuple representing per-cpu times
- */
-static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args) {
- natural_t cpu_count;
- natural_t i;
- processor_info_array_t info_array;
- mach_msg_type_number_t info_count;
- kern_return_t error;
- processor_cpu_load_info_data_t *cpu_load_info = NULL;
- int ret;
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_cputime = NULL;
-
- if (py_retlist == NULL)
- return NULL;
-
- mach_port_t host_port = mach_host_self();
- error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
- &cpu_count, &info_array, &info_count);
- if (error != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
- mach_error_string(error));
- goto error;
- }
- mach_port_deallocate(mach_task_self(), host_port);
-
- cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
-
- for (i = 0; i < cpu_count; i++) {
- py_cputime = Py_BuildValue(
- "(dddd)",
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
- );
- if (!py_cputime)
- goto error;
- if (PyList_Append(py_retlist, py_cputime))
- goto error;
- Py_CLEAR(py_cputime);
- }
-
- ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
- info_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
- return py_retlist;
-
-error:
- Py_XDECREF(py_cputime);
- Py_DECREF(py_retlist);
- if (cpu_load_info != NULL) {
- ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
- info_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
- }
- return NULL;
-}
-
-
-/*
- * Retrieve CPU frequency.
- */
-static PyObject *
-psutil_cpu_freq(PyObject *self, PyObject *args) {
- int64_t curr;
- int64_t min;
- int64_t max;
- size_t size = sizeof(int64_t);
-
- if (sysctlbyname("hw.cpufrequency", &curr, &size, NULL, 0)) {
- return PyErr_SetFromOSErrnoWithSyscall(
- "sysctlbyname('hw.cpufrequency')");
- }
- if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0)) {
- return PyErr_SetFromOSErrnoWithSyscall(
- "sysctlbyname('hw.cpufrequency_min')");
- }
- if (sysctlbyname("hw.cpufrequency_max", &max, &size, NULL, 0)) {
- return PyErr_SetFromOSErrnoWithSyscall(
- "sysctlbyname('hw.cpufrequency_max')");
- }
-
- return Py_BuildValue(
- "KKK",
- curr / 1000 / 1000,
- min / 1000 / 1000,
- max / 1000 / 1000);
-}
-
-
-/*
- * Return a Python float indicating the system boot time expressed in
- * seconds since the epoch.
- */
-static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args) {
- // fetch sysctl "kern.boottime"
- static int request[2] = { CTL_KERN, KERN_BOOTTIME };
- struct timeval result;
- size_t result_len = sizeof result;
- time_t boot_time = 0;
-
- if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1)
- return PyErr_SetFromErrno(PyExc_OSError);
- boot_time = result.tv_sec;
- return Py_BuildValue("f", (float)boot_time);
-}
-
-
-/*
- * Return a list of tuples including device, mount point and fs type
- * for all partitions mounted on the system.
- */
-static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args) {
- int num;
- int i;
- int len;
- uint64_t flags;
- char opts[400];
- struct statfs *fs = NULL;
- PyObject *py_dev = NULL;
- PyObject *py_mountp = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
+#include "arch/osx/cpu.h"
+#include "arch/osx/disk.h"
+#include "arch/osx/mem.h"
+#include "arch/osx/net.h"
+#include "arch/osx/proc.h"
+#include "arch/osx/sensors.h"
+#include "arch/osx/sys.h"
- if (py_retlist == NULL)
- return NULL;
- // get the number of mount points
- Py_BEGIN_ALLOW_THREADS
- num = getfsstat(NULL, 0, MNT_NOWAIT);
- Py_END_ALLOW_THREADS
- if (num == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- len = sizeof(*fs) * num;
- fs = malloc(len);
- if (fs == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- Py_BEGIN_ALLOW_THREADS
- num = getfsstat(fs, len, MNT_NOWAIT);
- Py_END_ALLOW_THREADS
- if (num == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- for (i = 0; i < num; i++) {
- opts[0] = 0;
- flags = fs[i].f_flags;
-
- // see sys/mount.h
- if (flags & MNT_RDONLY)
- strlcat(opts, "ro", sizeof(opts));
- else
- strlcat(opts, "rw", sizeof(opts));
- if (flags & MNT_SYNCHRONOUS)
- strlcat(opts, ",sync", sizeof(opts));
- if (flags & MNT_NOEXEC)
- strlcat(opts, ",noexec", sizeof(opts));
- if (flags & MNT_NOSUID)
- strlcat(opts, ",nosuid", sizeof(opts));
- if (flags & MNT_UNION)
- strlcat(opts, ",union", sizeof(opts));
- if (flags & MNT_ASYNC)
- strlcat(opts, ",async", sizeof(opts));
- if (flags & MNT_EXPORTED)
- strlcat(opts, ",exported", sizeof(opts));
- if (flags & MNT_QUARANTINE)
- strlcat(opts, ",quarantine", sizeof(opts));
- if (flags & MNT_LOCAL)
- strlcat(opts, ",local", sizeof(opts));
- if (flags & MNT_QUOTA)
- strlcat(opts, ",quota", sizeof(opts));
- if (flags & MNT_ROOTFS)
- strlcat(opts, ",rootfs", sizeof(opts));
- if (flags & MNT_DOVOLFS)
- strlcat(opts, ",dovolfs", sizeof(opts));
- if (flags & MNT_DONTBROWSE)
- strlcat(opts, ",dontbrowse", sizeof(opts));
- if (flags & MNT_IGNORE_OWNERSHIP)
- strlcat(opts, ",ignore-ownership", sizeof(opts));
- if (flags & MNT_AUTOMOUNTED)
- strlcat(opts, ",automounted", sizeof(opts));
- if (flags & MNT_JOURNALED)
- strlcat(opts, ",journaled", sizeof(opts));
- if (flags & MNT_NOUSERXATTR)
- strlcat(opts, ",nouserxattr", sizeof(opts));
- if (flags & MNT_DEFWRITE)
- strlcat(opts, ",defwrite", sizeof(opts));
- if (flags & MNT_MULTILABEL)
- strlcat(opts, ",multilabel", sizeof(opts));
- if (flags & MNT_NOATIME)
- strlcat(opts, ",noatime", sizeof(opts));
- if (flags & MNT_UPDATE)
- strlcat(opts, ",update", sizeof(opts));
- if (flags & MNT_RELOAD)
- strlcat(opts, ",reload", sizeof(opts));
- if (flags & MNT_FORCE)
- strlcat(opts, ",force", sizeof(opts));
- if (flags & MNT_CMDFLAGS)
- strlcat(opts, ",cmdflags", sizeof(opts));
-
- py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
- if (! py_dev)
- goto error;
- py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
- if (! py_mountp)
- goto error;
- py_tuple = Py_BuildValue(
- "(OOss)",
- py_dev, // device
- py_mountp, // mount point
- fs[i].f_fstypename, // fs type
- opts); // options
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_dev);
- Py_CLEAR(py_mountp);
- Py_CLEAR(py_tuple);
- }
-
- free(fs);
- return py_retlist;
-
-error:
- Py_XDECREF(py_dev);
- Py_XDECREF(py_mountp);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- if (fs != NULL)
- free(fs);
- return NULL;
-}
-
-
-/*
- * Return process threads
- */
-static PyObject *
-psutil_proc_threads(PyObject *self, PyObject *args) {
- pid_t pid;
- int err, ret;
- kern_return_t kr;
- unsigned int info_count = TASK_BASIC_INFO_COUNT;
- mach_port_t task = MACH_PORT_NULL;
- struct task_basic_info tasks_info;
- thread_act_port_array_t thread_list = NULL;
- thread_info_data_t thinfo_basic;
- thread_basic_info_t basic_info_th;
- mach_msg_type_number_t thread_count, thread_info_count, j;
-
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- goto error;
-
- if (psutil_task_for_pid(pid, &task) != 0)
- goto error;
-
- info_count = TASK_BASIC_INFO_COUNT;
- err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info,
- &info_count);
- if (err != KERN_SUCCESS) {
- // errcode 4 is "invalid argument" (access denied)
- if (err == 4) {
- AccessDenied("task_info");
- }
- else {
- // otherwise throw a runtime error with appropriate error code
- PyErr_Format(PyExc_RuntimeError,
- "task_info(TASK_BASIC_INFO) syscall failed");
- }
- goto error;
- }
-
- err = task_threads(task, &thread_list, &thread_count);
- if (err != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError, "task_threads() syscall failed");
- goto error;
- }
-
- for (j = 0; j < thread_count; j++) {
- thread_info_count = THREAD_INFO_MAX;
- kr = thread_info(thread_list[j], THREAD_BASIC_INFO,
- (thread_info_t)thinfo_basic, &thread_info_count);
- if (kr != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError,
- "thread_info(THREAD_BASIC_INFO) syscall failed");
- goto error;
- }
-
- basic_info_th = (thread_basic_info_t)thinfo_basic;
- py_tuple = Py_BuildValue(
- "Iff",
- j + 1,
- basic_info_th->user_time.seconds + \
- (float)basic_info_th->user_time.microseconds / 1000000.0,
- basic_info_th->system_time.seconds + \
- (float)basic_info_th->system_time.microseconds / 1000000.0
- );
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
- }
-
- ret = vm_deallocate(task, (vm_address_t)thread_list,
- thread_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
-
- mach_port_deallocate(mach_task_self(), task);
-
- return py_retlist;
-
-error:
- if (task != MACH_PORT_NULL)
- mach_port_deallocate(mach_task_self(), task);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- if (thread_list != NULL) {
- ret = vm_deallocate(task, (vm_address_t)thread_list,
- thread_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
- }
- return NULL;
-}
-
-
-/*
- * Return process open files as a Python tuple.
- * References:
- * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd
- * - /usr/include/sys/proc_info.h
- */
-static PyObject *
-psutil_proc_open_files(PyObject *self, PyObject *args) {
- pid_t pid;
- int pidinfo_result;
- int iterations;
- int i;
- unsigned long nb;
-
- struct proc_fdinfo *fds_pointer = NULL;
- struct proc_fdinfo *fdp_pointer;
- struct vnode_fdinfowithpath vi;
-
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_tuple = NULL;
- PyObject *py_path = NULL;
-
- if (py_retlist == NULL)
- return NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- goto error;
-
- pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
- if (pidinfo_result <= 0)
- goto error;
-
- fds_pointer = malloc(pidinfo_result);
- if (fds_pointer == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- pidinfo_result = psutil_proc_pidinfo(
- pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
- if (pidinfo_result <= 0)
- goto error;
-
- iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
-
- for (i = 0; i < iterations; i++) {
- fdp_pointer = &fds_pointer[i];
-
- if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
- errno = 0;
- nb = proc_pidfdinfo((pid_t)pid,
- fdp_pointer->proc_fd,
- PROC_PIDFDVNODEPATHINFO,
- &vi,
- sizeof(vi));
-
- // --- errors checking
- if ((nb <= 0) || nb < sizeof(vi)) {
- if ((errno == ENOENT) || (errno == EBADF)) {
- // no such file or directory or bad file descriptor;
- // let's assume the file has been closed or removed
- continue;
- }
- else {
- psutil_raise_for_pid(
- pid, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO)");
- goto error;
- }
- }
- // --- /errors checking
-
- // --- construct python list
- py_path = PyUnicode_DecodeFSDefault(vi.pvip.vip_path);
- if (! py_path)
- goto error;
- py_tuple = Py_BuildValue(
- "(Oi)",
- py_path,
- (int)fdp_pointer->proc_fd);
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
- Py_CLEAR(py_path);
- // --- /construct python list
- }
- }
-
- free(fds_pointer);
- return py_retlist;
-
-error:
- Py_XDECREF(py_tuple);
- Py_XDECREF(py_path);
- Py_DECREF(py_retlist);
- if (fds_pointer != NULL)
- free(fds_pointer);
- return NULL; // exception has already been set earlier
-}
-
-
-/*
- * Return process TCP and UDP connections as a list of tuples.
- * Raises NSP in case of zombie process.
- * References:
- * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
- * - /usr/include/sys/proc_info.h
- */
-static PyObject *
-psutil_proc_connections(PyObject *self, PyObject *args) {
- pid_t pid;
- int pidinfo_result;
- int iterations;
- int i;
- unsigned long nb;
-
- struct proc_fdinfo *fds_pointer = NULL;
- struct proc_fdinfo *fdp_pointer;
- struct socket_fdinfo si;
-
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_tuple = NULL;
- PyObject *py_laddr = NULL;
- PyObject *py_raddr = NULL;
- PyObject *py_af_filter = NULL;
- PyObject *py_type_filter = NULL;
-
- if (py_retlist == NULL)
- return NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, &py_af_filter,
- &py_type_filter)) {
- goto error;
- }
-
- if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
- PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
- goto error;
- }
-
- if (pid == 0)
- return py_retlist;
- pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
- if (pidinfo_result <= 0)
- goto error;
-
- fds_pointer = malloc(pidinfo_result);
- if (fds_pointer == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- pidinfo_result = psutil_proc_pidinfo(
- pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
- if (pidinfo_result <= 0)
- goto error;
-
- iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
- for (i = 0; i < iterations; i++) {
- py_tuple = NULL;
- py_laddr = NULL;
- py_raddr = NULL;
- fdp_pointer = &fds_pointer[i];
-
- if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {
- errno = 0;
- nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd,
- PROC_PIDFDSOCKETINFO, &si, sizeof(si));
-
- // --- errors checking
- if ((nb <= 0) || (nb < sizeof(si))) {
- if (errno == EBADF) {
- // let's assume socket has been closed
- continue;
- }
- else {
- psutil_raise_for_pid(
- pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)");
- goto error;
- }
- }
- // --- /errors checking
-
- //
- int fd, family, type, lport, rport, state;
- char lip[200], rip[200];
- int inseq;
- PyObject *py_family;
- PyObject *py_type;
-
- fd = (int)fdp_pointer->proc_fd;
- family = si.psi.soi_family;
- type = si.psi.soi_type;
-
- // apply filters
- py_family = PyLong_FromLong((long)family);
- inseq = PySequence_Contains(py_af_filter, py_family);
- Py_DECREF(py_family);
- if (inseq == 0)
- continue;
- py_type = PyLong_FromLong((long)type);
- inseq = PySequence_Contains(py_type_filter, py_type);
- Py_DECREF(py_type);
- if (inseq == 0)
- continue;
-
- if (errno != 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- if ((family == AF_INET) || (family == AF_INET6)) {
- if (family == AF_INET) {
- inet_ntop(AF_INET,
- &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
- insi_laddr.ina_46.i46a_addr4,
- lip,
- sizeof(lip));
- inet_ntop(AF_INET,
- &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr. \
- ina_46.i46a_addr4,
- rip,
- sizeof(rip));
- }
- else {
- inet_ntop(AF_INET6,
- &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
- insi_laddr.ina_6,
- lip, sizeof(lip));
- inet_ntop(AF_INET6,
- &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
- insi_faddr.ina_6,
- rip, sizeof(rip));
- }
-
- // check for inet_ntop failures
- if (errno != 0) {
- PyErr_SetFromOSErrnoWithSyscall("inet_ntop()");
- goto error;
- }
-
- lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);
- rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
- if (type == SOCK_STREAM)
- state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state;
- else
- state = PSUTIL_CONN_NONE;
-
- py_laddr = Py_BuildValue("(si)", lip, lport);
- if (!py_laddr)
- goto error;
- if (rport != 0)
- py_raddr = Py_BuildValue("(si)", rip, rport);
- else
- py_raddr = Py_BuildValue("()");
- if (!py_raddr)
- goto error;
-
- // construct the python list
- py_tuple = Py_BuildValue(
- "(iiiNNi)", fd, family, type, py_laddr, py_raddr, state);
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
- }
- else if (family == AF_UNIX) {
- py_laddr = PyUnicode_DecodeFSDefault(
- si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path);
- if (!py_laddr)
- goto error;
- py_raddr = PyUnicode_DecodeFSDefault(
- si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path);
- if (!py_raddr)
- goto error;
- // construct the python list
- py_tuple = Py_BuildValue(
- "(iiiOOi)",
- fd, family, type,
- py_laddr,
- py_raddr,
- PSUTIL_CONN_NONE);
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
- Py_CLEAR(py_laddr);
- Py_CLEAR(py_raddr);
- }
- }
- }
-
- free(fds_pointer);
- return py_retlist;
-
-error:
- Py_XDECREF(py_tuple);
- Py_XDECREF(py_laddr);
- Py_XDECREF(py_raddr);
- Py_DECREF(py_retlist);
- if (fds_pointer != NULL)
- free(fds_pointer);
- return NULL;
-}
-
-
-/*
- * Return number of file descriptors opened by process.
- * Raises NSP in case of zombie process.
- */
-static PyObject *
-psutil_proc_num_fds(PyObject *self, PyObject *args) {
- pid_t pid;
- int pidinfo_result;
- int num;
- struct proc_fdinfo *fds_pointer;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
- if (pidinfo_result <= 0)
- return PyErr_SetFromErrno(PyExc_OSError);
-
- fds_pointer = malloc(pidinfo_result);
- if (fds_pointer == NULL)
- return PyErr_NoMemory();
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
- pidinfo_result);
- if (pidinfo_result <= 0) {
- free(fds_pointer);
- return PyErr_SetFromErrno(PyExc_OSError);
- }
-
- num = (pidinfo_result / PROC_PIDLISTFD_SIZE);
- free(fds_pointer);
- return Py_BuildValue("i", num);
-}
-
-
-/*
- * Return a Python list of named tuples with overall network I/O information
- */
-static PyObject *
-psutil_net_io_counters(PyObject *self, PyObject *args) {
- char *buf = NULL, *lim, *next;
- struct if_msghdr *ifm;
- int mib[6];
- mib[0] = CTL_NET; // networking subsystem
- mib[1] = PF_ROUTE; // type of information
- mib[2] = 0; // protocol (IPPROTO_xxx)
- mib[3] = 0; // address family
- mib[4] = NET_RT_IFLIST2; // operation
- mib[5] = 0;
- size_t len;
- PyObject *py_ifc_info = NULL;
- PyObject *py_retdict = PyDict_New();
-
- if (py_retdict == NULL)
- return NULL;
-
- if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- buf = malloc(len);
- if (buf == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- lim = buf + len;
-
- for (next = buf; next < lim; ) {
- ifm = (struct if_msghdr *)next;
- next += ifm->ifm_msglen;
-
- if (ifm->ifm_type == RTM_IFINFO2) {
- py_ifc_info = NULL;
- struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
- char ifc_name[32];
-
- strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
- ifc_name[sdl->sdl_nlen] = 0;
-
- py_ifc_info = Py_BuildValue(
- "(KKKKKKKi)",
- if2m->ifm_data.ifi_obytes,
- if2m->ifm_data.ifi_ibytes,
- if2m->ifm_data.ifi_opackets,
- if2m->ifm_data.ifi_ipackets,
- if2m->ifm_data.ifi_ierrors,
- if2m->ifm_data.ifi_oerrors,
- if2m->ifm_data.ifi_iqdrops,
- 0); // dropout not supported
-
- if (!py_ifc_info)
- goto error;
- if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
- goto error;
- Py_CLEAR(py_ifc_info);
- }
- else {
- continue;
- }
- }
-
- free(buf);
- return py_retdict;
-
-error:
- Py_XDECREF(py_ifc_info);
- Py_DECREF(py_retdict);
- if (buf != NULL)
- free(buf);
- return NULL;
-}
-
-
-/*
- * Return a Python dict of tuples for disk I/O information
- */
-static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args) {
- CFDictionaryRef parent_dict;
- CFDictionaryRef props_dict;
- CFDictionaryRef stats_dict;
- io_registry_entry_t parent;
- io_registry_entry_t disk;
- io_iterator_t disk_list;
- PyObject *py_disk_info = NULL;
- PyObject *py_retdict = PyDict_New();
-
- if (py_retdict == NULL)
- return NULL;
-
- // Get list of disks
- if (IOServiceGetMatchingServices(kIOMasterPortDefault,
- IOServiceMatching(kIOMediaClass),
- &disk_list) != kIOReturnSuccess) {
- PyErr_SetString(
- PyExc_RuntimeError, "unable to get the list of disks.");
- goto error;
- }
-
- // Iterate over disks
- while ((disk = IOIteratorNext(disk_list)) != 0) {
- py_disk_info = NULL;
- parent_dict = NULL;
- props_dict = NULL;
- stats_dict = NULL;
-
- if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent)
- != kIOReturnSuccess) {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the disk's parent.");
- IOObjectRelease(disk);
- goto error;
- }
-
- if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
- if (IORegistryEntryCreateCFProperties(
- disk,
- (CFMutableDictionaryRef *) &parent_dict,
- kCFAllocatorDefault,
- kNilOptions
- ) != kIOReturnSuccess)
- {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the parent's properties.");
- IOObjectRelease(disk);
- IOObjectRelease(parent);
- goto error;
- }
-
- if (IORegistryEntryCreateCFProperties(
- parent,
- (CFMutableDictionaryRef *) &props_dict,
- kCFAllocatorDefault,
- kNilOptions
- ) != kIOReturnSuccess)
- {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the disk properties.");
- CFRelease(props_dict);
- IOObjectRelease(disk);
- IOObjectRelease(parent);
- goto error;
- }
-
- const int kMaxDiskNameSize = 64;
- CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue(
- parent_dict, CFSTR(kIOBSDNameKey));
- char disk_name[kMaxDiskNameSize];
-
- CFStringGetCString(disk_name_ref,
- disk_name,
- kMaxDiskNameSize,
- CFStringGetSystemEncoding());
-
- stats_dict = (CFDictionaryRef)CFDictionaryGetValue(
- props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
-
- if (stats_dict == NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "Unable to get disk stats.");
- goto error;
- }
-
- CFNumberRef number;
- int64_t reads = 0;
- int64_t writes = 0;
- int64_t read_bytes = 0;
- int64_t write_bytes = 0;
- int64_t read_time = 0;
- int64_t write_time = 0;
-
- // Get disk reads/writes
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsReadsKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &reads);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsWritesKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &writes);
- }
-
- // Get disk bytes read/written
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes);
- }
-
- // Get disk time spent reading/writing (nanoseconds)
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &read_time);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &write_time);
- }
-
- // Read/Write time on macOS comes back in nanoseconds and in psutil
- // we've standardized on milliseconds so do the conversion.
- py_disk_info = Py_BuildValue(
- "(KKKKKK)",
- reads,
- writes,
- read_bytes,
- write_bytes,
- read_time / 1000 / 1000,
- write_time / 1000 / 1000);
- if (!py_disk_info)
- goto error;
- if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
- goto error;
- Py_CLEAR(py_disk_info);
-
- CFRelease(parent_dict);
- IOObjectRelease(parent);
- CFRelease(props_dict);
- IOObjectRelease(disk);
- }
- }
-
- IOObjectRelease (disk_list);
-
- return py_retdict;
-
-error:
- Py_XDECREF(py_disk_info);
- Py_DECREF(py_retdict);
- return NULL;
-}
-
-
-/*
- * Return currently connected users as a list of tuples.
- */
-static PyObject *
-psutil_users(PyObject *self, PyObject *args) {
- struct utmpx *utx;
- PyObject *py_username = NULL;
- PyObject *py_tty = NULL;
- PyObject *py_hostname = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
- while ((utx = getutxent()) != NULL) {
- if (utx->ut_type != USER_PROCESS)
- continue;
- py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
- if (! py_username)
- goto error;
- py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
- if (! py_tty)
- goto error;
- py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
- if (! py_hostname)
- goto error;
- py_tuple = Py_BuildValue(
- "(OOOfi)",
- py_username, // username
- py_tty, // tty
- py_hostname, // hostname
- (float)utx->ut_tv.tv_sec, // start time
- utx->ut_pid // process id
- );
- if (!py_tuple) {
- endutxent();
- goto error;
- }
- if (PyList_Append(py_retlist, py_tuple)) {
- endutxent();
- goto error;
- }
- Py_CLEAR(py_username);
- Py_CLEAR(py_tty);
- Py_CLEAR(py_hostname);
- Py_CLEAR(py_tuple);
- }
-
- endutxent();
- return py_retlist;
-
-error:
- Py_XDECREF(py_username);
- Py_XDECREF(py_tty);
- Py_XDECREF(py_hostname);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- return NULL;
-}
-
-
-/*
- * Return CPU statistics.
- */
-static PyObject *
-psutil_cpu_stats(PyObject *self, PyObject *args) {
- struct vmmeter vmstat;
- kern_return_t ret;
- mach_msg_type_number_t count = sizeof(vmstat) / sizeof(integer_t);
- mach_port_t mport = mach_host_self();
-
- ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);
- if (ret != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "host_statistics(HOST_VM_INFO) failed: %s",
- mach_error_string(ret));
- return NULL;
- }
- mach_port_deallocate(mach_task_self(), mport);
-
- return Py_BuildValue(
- "IIIII",
- vmstat.v_swtch, // ctx switches
- vmstat.v_intr, // interrupts
- vmstat.v_soft, // software interrupts
- vmstat.v_syscall, // syscalls
- vmstat.v_trap // traps
- );
-}
-
-
-/*
- * Return battery information.
- */
-static PyObject *
-psutil_sensors_battery(PyObject *self, PyObject *args) {
- PyObject *py_tuple = NULL;
- CFTypeRef power_info = NULL;
- CFArrayRef power_sources_list = NULL;
- CFDictionaryRef power_sources_information = NULL;
- CFNumberRef capacity_ref = NULL;
- CFNumberRef time_to_empty_ref = NULL;
- CFStringRef ps_state_ref = NULL;
- uint32_t capacity; /* units are percent */
- int time_to_empty; /* units are minutes */
- int is_power_plugged;
-
- power_info = IOPSCopyPowerSourcesInfo();
-
- if (!power_info) {
- PyErr_SetString(PyExc_RuntimeError,
- "IOPSCopyPowerSourcesInfo() syscall failed");
- goto error;
- }
-
- power_sources_list = IOPSCopyPowerSourcesList(power_info);
- if (!power_sources_list) {
- PyErr_SetString(PyExc_RuntimeError,
- "IOPSCopyPowerSourcesList() syscall failed");
- goto error;
- }
-
- /* Should only get one source. But in practice, check for > 0 sources */
- if (!CFArrayGetCount(power_sources_list)) {
- PyErr_SetString(PyExc_NotImplementedError, "no battery");
- goto error;
- }
-
- power_sources_information = IOPSGetPowerSourceDescription(
- power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
-
- capacity_ref = (CFNumberRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
- if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
- PyErr_SetString(PyExc_RuntimeError,
- "No battery capacity infomration in power sources info");
- goto error;
- }
-
- ps_state_ref = (CFStringRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
- is_power_plugged = CFStringCompare(
- ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
- == kCFCompareEqualTo;
-
- time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
- if (!CFNumberGetValue(time_to_empty_ref,
- kCFNumberIntType, &time_to_empty)) {
- /* This value is recommended for non-Apple power sources, so it's not
- * an error if it doesn't exist. We'll return -1 for "unknown" */
- /* A value of -1 indicates "Still Calculating the Time" also for
- * apple power source */
- time_to_empty = -1;
- }
-
- py_tuple = Py_BuildValue("Iii",
- capacity, time_to_empty, is_power_plugged);
- if (!py_tuple) {
- goto error;
- }
-
- CFRelease(power_info);
- CFRelease(power_sources_list);
- /* Caller should NOT release power_sources_information */
-
- return py_tuple;
-
-error:
- if (power_info)
- CFRelease(power_info);
- if (power_sources_list)
- CFRelease(power_sources_list);
- Py_XDECREF(py_tuple);
- return NULL;
-}
-
-
-/*
- * define the psutil C module methods and initialize the module.
- */
static PyMethodDef mod_methods[] = {
// --- per-process functions
-
- {"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS,
- "Return multiple process info."},
- {"proc_pidtaskinfo_oneshot", psutil_proc_pidtaskinfo_oneshot, METH_VARARGS,
- "Return multiple process info."},
- {"proc_name", psutil_proc_name, METH_VARARGS,
- "Return process name"},
- {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
- "Return process cmdline as a list of cmdline arguments"},
- {"proc_environ", psutil_proc_environ, METH_VARARGS,
- "Return process environment data"},
- {"proc_exe", psutil_proc_exe, METH_VARARGS,
- "Return path of the process executable"},
- {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
- "Return process current working directory."},
- {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
- "Return process USS memory"},
- {"proc_threads", psutil_proc_threads, METH_VARARGS,
- "Return process threads as a list of tuples"},
- {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
- "Return files opened by process as a list of tuples"},
- {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
- "Return the number of fds opened by process."},
- {"proc_connections", psutil_proc_connections, METH_VARARGS,
- "Get process TCP and UDP connections as a list of tuples"},
+ {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS},
+ {"proc_connections", psutil_proc_connections, METH_VARARGS},
+ {"proc_cwd", psutil_proc_cwd, METH_VARARGS},
+ {"proc_environ", psutil_proc_environ, METH_VARARGS},
+ {"proc_exe", psutil_proc_exe, METH_VARARGS},
+ {"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS},
+ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS},
+ {"proc_name", psutil_proc_name, METH_VARARGS},
+ {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS},
+ {"proc_open_files", psutil_proc_open_files, METH_VARARGS},
+ {"proc_pidtaskinfo_oneshot", psutil_proc_pidtaskinfo_oneshot, METH_VARARGS},
+ {"proc_threads", psutil_proc_threads, METH_VARARGS},
// --- system-related functions
-
- {"pids", psutil_pids, METH_VARARGS,
- "Returns a list of PIDs currently running on the system"},
- {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
- "Return number of logical CPUs on the system"},
- {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
- "Return number of physical CPUs on the system"},
- {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
- "Return system virtual memory stats"},
- {"swap_mem", psutil_swap_mem, METH_VARARGS,
- "Return stats about swap memory, in bytes"},
- {"cpu_times", psutil_cpu_times, METH_VARARGS,
- "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
- {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
- "Return system per-cpu times as a list of tuples"},
- {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
- "Return cpu current frequency"},
- {"boot_time", psutil_boot_time, METH_VARARGS,
- "Return the system boot time expressed in seconds since the epoch."},
- {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
- "Return a list of tuples including device, mount point and "
- "fs type for all partitions mounted on the system."},
- {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
- "Return dict of tuples of networks I/O information."},
- {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
- "Return dict of tuples of disks I/O information."},
- {"users", psutil_users, METH_VARARGS,
- "Return currently connected users as a list of tuples"},
- {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
- "Return CPU statistics"},
- {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
- "Return battery information."},
+ {"boot_time", psutil_boot_time, METH_VARARGS},
+ {"cpu_count_cores", psutil_cpu_count_cores, METH_VARARGS},
+ {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS},
+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS},
+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS},
+ {"cpu_times", psutil_cpu_times, METH_VARARGS},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS},
+ {"disk_usage_used", psutil_disk_usage_used, METH_VARARGS},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS},
+ {"pids", psutil_pids, METH_VARARGS},
+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS},
+ {"swap_mem", psutil_swap_mem, METH_VARARGS},
+ {"users", psutil_users, METH_VARARGS},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS},
// --- others
- {"set_testing", psutil_set_testing, METH_NOARGS,
- "Set psutil in testing mode"},
+ {"check_pid_range", psutil_check_pid_range, METH_VARARGS},
+ {"set_debug", psutil_set_debug, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
@@ -1895,17 +134,6 @@ static PyMethodDef mod_methods[] = {
if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE))
INITERR;
- // Exception.
- ZombieProcessError = PyErr_NewException(
- "_psutil_osx.ZombieProcessError", NULL, NULL);
- if (ZombieProcessError == NULL)
- INITERR;
- Py_INCREF(ZombieProcessError);
- if (PyModule_AddObject(mod, "ZombieProcessError", ZombieProcessError)) {
- Py_DECREF(ZombieProcessError);
- INITERR;
- }
-
if (mod == NULL)
INITERR;
#if PY_MAJOR_VERSION >= 3
diff --git a/contrib/python/psutil/py3/psutil/_psutil_posix.c b/contrib/python/psutil/py3/psutil/_psutil_posix.c
index 3447fc9017..ad0d4b17ee 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_posix.c
+++ b/contrib/python/psutil/py3/psutil/_psutil_posix.c
@@ -147,7 +147,7 @@ psutil_pid_exists(pid_t pid) {
*/
void
psutil_raise_for_pid(long pid, char *syscall) {
- if (errno != 0) // unlikely
+ if (errno != 0)
PyErr_SetFromOSErrnoWithSyscall(syscall);
else if (psutil_pid_exists(pid) == 0)
NoSuchProcess(syscall);
@@ -429,6 +429,210 @@ error:
return PyErr_SetFromErrno(PyExc_OSError);
}
+static int
+append_flag(PyObject *py_retlist, const char * flag_name)
+{
+ PyObject *py_str = NULL;
+
+#if PY_MAJOR_VERSION >= 3
+ py_str = PyUnicode_FromString(flag_name);
+#else
+ py_str = PyString_FromString(flag_name);
+#endif
+ if (! py_str)
+ return 0;
+ if (PyList_Append(py_retlist, py_str)) {
+ Py_DECREF(py_str);
+ return 0;
+ }
+ Py_CLEAR(py_str);
+
+ return 1;
+}
+
+/*
+ * Get all of the NIC flags and return them.
+ */
+static PyObject *
+psutil_net_if_flags(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = -1;
+ int ret;
+ struct ifreq ifr;
+ PyObject *py_retlist = PyList_New(0);
+ short int flags;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ goto error;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ PyErr_SetFromOSErrnoWithSyscall("socket(SOCK_DGRAM)");
+ goto error;
+ }
+
+ PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+ ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+ if (ret == -1) {
+ PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCGIFFLAGS)");
+ goto error;
+ }
+
+ close(sock);
+ sock = -1;
+
+ flags = ifr.ifr_flags & 0xFFFF;
+
+ // Linux/glibc IFF flags: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/gnu/net/if.h;h=251418f82331c0426e58707fe4473d454893b132;hb=HEAD
+ // macOS IFF flags: https://opensource.apple.com/source/xnu/xnu-792/bsd/net/if.h.auto.html
+ // AIX IFF flags: https://www.ibm.com/support/pages/how-hexadecimal-flags-displayed-ifconfig-are-calculated
+ // FreeBSD IFF flags: https://www.freebsd.org/cgi/man.cgi?query=if_allmulti&apropos=0&sektion=0&manpath=FreeBSD+10-current&format=html
+
+#ifdef IFF_UP
+ // Available in (at least) Linux, macOS, AIX, BSD
+ if (flags & IFF_UP)
+ if (!append_flag(py_retlist, "up"))
+ goto error;
+#endif
+#ifdef IFF_BROADCAST
+ // Available in (at least) Linux, macOS, AIX, BSD
+ if (flags & IFF_BROADCAST)
+ if (!append_flag(py_retlist, "broadcast"))
+ goto error;
+#endif
+#ifdef IFF_DEBUG
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_DEBUG)
+ if (!append_flag(py_retlist, "debug"))
+ goto error;
+#endif
+#ifdef IFF_LOOPBACK
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_LOOPBACK)
+ if (!append_flag(py_retlist, "loopback"))
+ goto error;
+#endif
+#ifdef IFF_POINTOPOINT
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_POINTOPOINT)
+ if (!append_flag(py_retlist, "pointopoint"))
+ goto error;
+#endif
+#ifdef IFF_NOTRAILERS
+ // Available in (at least) Linux, macOS, AIX
+ if (flags & IFF_NOTRAILERS)
+ if (!append_flag(py_retlist, "notrailers"))
+ goto error;
+#endif
+#ifdef IFF_RUNNING
+ // Available in (at least) Linux, macOS, AIX, BSD
+ if (flags & IFF_RUNNING)
+ if (!append_flag(py_retlist, "running"))
+ goto error;
+#endif
+#ifdef IFF_NOARP
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_NOARP)
+ if (!append_flag(py_retlist, "noarp"))
+ goto error;
+#endif
+#ifdef IFF_PROMISC
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_PROMISC)
+ if (!append_flag(py_retlist, "promisc"))
+ goto error;
+#endif
+#ifdef IFF_ALLMULTI
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_ALLMULTI)
+ if (!append_flag(py_retlist, "allmulti"))
+ goto error;
+#endif
+#ifdef IFF_MASTER
+ // Available in (at least) Linux
+ if (flags & IFF_MASTER)
+ if (!append_flag(py_retlist, "master"))
+ goto error;
+#endif
+#ifdef IFF_SLAVE
+ // Available in (at least) Linux
+ if (flags & IFF_SLAVE)
+ if (!append_flag(py_retlist, "slave"))
+ goto error;
+#endif
+#ifdef IFF_MULTICAST
+ // Available in (at least) Linux, macOS, BSD
+ if (flags & IFF_MULTICAST)
+ if (!append_flag(py_retlist, "multicast"))
+ goto error;
+#endif
+#ifdef IFF_PORTSEL
+ // Available in (at least) Linux
+ if (flags & IFF_PORTSEL)
+ if (!append_flag(py_retlist, "portsel"))
+ goto error;
+#endif
+#ifdef IFF_AUTOMEDIA
+ // Available in (at least) Linux
+ if (flags & IFF_AUTOMEDIA)
+ if (!append_flag(py_retlist, "automedia"))
+ goto error;
+#endif
+#ifdef IFF_DYNAMIC
+ // Available in (at least) Linux
+ if (flags & IFF_DYNAMIC)
+ if (!append_flag(py_retlist, "dynamic"))
+ goto error;
+#endif
+#ifdef IFF_OACTIVE
+ // Available in (at least) macOS, BSD
+ if (flags & IFF_OACTIVE)
+ if (!append_flag(py_retlist, "oactive"))
+ goto error;
+#endif
+#ifdef IFF_SIMPLEX
+ // Available in (at least) macOS, AIX, BSD
+ if (flags & IFF_SIMPLEX)
+ if (!append_flag(py_retlist, "simplex"))
+ goto error;
+#endif
+#ifdef IFF_LINK0
+ // Available in (at least) macOS, BSD
+ if (flags & IFF_LINK0)
+ if (!append_flag(py_retlist, "link0"))
+ goto error;
+#endif
+#ifdef IFF_LINK1
+ // Available in (at least) macOS, BSD
+ if (flags & IFF_LINK1)
+ if (!append_flag(py_retlist, "link1"))
+ goto error;
+#endif
+#ifdef IFF_LINK2
+ // Available in (at least) macOS, BSD
+ if (flags & IFF_LINK2)
+ if (!append_flag(py_retlist, "link2"))
+ goto error;
+#endif
+#ifdef IFF_D2
+ // Available in (at least) AIX
+ if (flags & IFF_D2)
+ if (!append_flag(py_retlist, "d2"))
+ goto error;
+#endif
+
+ return py_retlist;
+
+error:
+ Py_DECREF(py_retlist);
+ if (sock != -1)
+ close(sock);
+ return NULL;
+}
+
/*
* Inspect NIC flags, returns a bool indicating whether the NIC is
@@ -504,13 +708,14 @@ int psutil_get_nic_speed(int ifm_active) {
case(IFM_1000_LX): // 1000baseLX - single-mode fiber
case(IFM_1000_CX): // 1000baseCX - 150ohm STP
#if defined(IFM_1000_TX) && !defined(PSUTIL_OPENBSD)
+ #define HAS_CASE_IFM_1000_TX 1
// FreeBSD 4 and others (but NOT OpenBSD) -> #define IFM_1000_T in net/if_media.h
case(IFM_1000_TX):
#endif
#ifdef IFM_1000_FX
case(IFM_1000_FX):
#endif
-#ifdef IFM_1000_T
+#if defined(IFM_1000_T) && (!HAS_CASE_IFM_1000_TX || IFM_1000_T != IFM_1000_TX)
case(IFM_1000_T):
#endif
return 1000;
@@ -664,21 +869,15 @@ extern "C" {
* define the psutil C module methods and initialize the module.
*/
static PyMethodDef mod_methods[] = {
- {"getpriority", psutil_posix_getpriority, METH_VARARGS,
- "Return process priority"},
- {"setpriority", psutil_posix_setpriority, METH_VARARGS,
- "Set process priority"},
- {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
- "Retrieve NICs information"},
- {"net_if_mtu", psutil_net_if_mtu, METH_VARARGS,
- "Retrieve NIC MTU"},
- {"net_if_is_running", psutil_net_if_is_running, METH_VARARGS,
- "Return True if the NIC is running."},
- {"getpagesize", psutil_getpagesize_pywrapper, METH_VARARGS,
- "Return memory page size."},
+ {"getpagesize", psutil_getpagesize_pywrapper, METH_VARARGS},
+ {"getpriority", psutil_posix_getpriority, METH_VARARGS},
+ {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS},
+ {"net_if_flags", psutil_net_if_flags, METH_VARARGS},
+ {"net_if_is_running", psutil_net_if_is_running, METH_VARARGS},
+ {"net_if_mtu", psutil_net_if_mtu, METH_VARARGS},
+ {"setpriority", psutil_posix_setpriority, METH_VARARGS},
#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
- {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
- "Return NIC stats."},
+ {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS},
#endif
{NULL, NULL, 0, NULL}
};
diff --git a/contrib/python/psutil/py3/psutil/_psutil_windows.c b/contrib/python/psutil/py3/psutil/_psutil_windows.c
index a2154e923e..bb6e12ff80 100644
--- a/contrib/python/psutil/py3/psutil/_psutil_windows.c
+++ b/contrib/python/psutil/py3/psutil/_psutil_windows.c
@@ -14,1536 +14,25 @@
* - NtResumeProcess
*/
-// Fixes clash between winsock2.h and windows.h
-#define WIN32_LEAN_AND_MEAN
-
#include <Python.h>
#include <windows.h>
-#include <Psapi.h> // memory_info(), memory_maps()
-#include <signal.h>
-#include <tlhelp32.h> // threads(), PROCESSENTRY32
-
-// Link with Iphlpapi.lib
-#pragma comment(lib, "IPHLPAPI.lib")
#include "_psutil_common.h"
-#include "arch/windows/security.h"
-#include "arch/windows/process_utils.h"
-#include "arch/windows/process_info.h"
-#include "arch/windows/process_handles.h"
-#include "arch/windows/disk.h"
#include "arch/windows/cpu.h"
+#include "arch/windows/disk.h"
+#include "arch/windows/mem.h"
#include "arch/windows/net.h"
+#include "arch/windows/proc.h"
+#include "arch/windows/proc_handles.h"
+#include "arch/windows/proc_info.h"
+#include "arch/windows/proc_utils.h"
+#include "arch/windows/security.h"
+#include "arch/windows/sensors.h"
#include "arch/windows/services.h"
#include "arch/windows/socks.h"
+#include "arch/windows/sys.h"
#include "arch/windows/wmi.h"
-// Raised by Process.wait().
-static PyObject *TimeoutExpired;
-static PyObject *TimeoutAbandoned;
-
-
-/*
- * Return the number of logical, active CPUs. Return 0 if undetermined.
- * See discussion at: https://bugs.python.org/issue33166#msg314631
- */
-unsigned int
-psutil_get_num_cpus(int fail_on_err) {
- unsigned int ncpus = 0;
-
- // Minimum requirement: Windows 7
- if (GetActiveProcessorCount != NULL) {
- ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
- if ((ncpus == 0) && (fail_on_err == 1)) {
- PyErr_SetFromWindowsErr(0);
- }
- }
- else {
- psutil_debug("GetActiveProcessorCount() not available; "
- "using GetSystemInfo()");
- ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors;
- if ((ncpus <= 0) && (fail_on_err == 1)) {
- PyErr_SetString(
- PyExc_RuntimeError,
- "GetSystemInfo() failed to retrieve CPU count");
- }
- }
- return ncpus;
-}
-
-
-/*
- * Return a Python float representing the system uptime expressed in seconds
- * since the epoch.
- */
-static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args) {
- ULONGLONG upTime;
- FILETIME fileTime;
-
- GetSystemTimeAsFileTime(&fileTime);
- // Number of milliseconds that have elapsed since the system was started.
- upTime = GetTickCount64() / 1000ull;
- return Py_BuildValue("d", psutil_FiletimeToUnixTime(fileTime) - upTime);
-}
-
-
-/*
- * Return 1 if PID exists in the current process list, else 0.
- */
-static PyObject *
-psutil_pid_exists(PyObject *self, PyObject *args) {
- DWORD pid;
- int status;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- status = psutil_pid_is_running(pid);
- if (-1 == status)
- return NULL; // exception raised in psutil_pid_is_running()
- return PyBool_FromLong(status);
-}
-
-
-/*
- * Return a Python list of all the PIDs running on the system.
- */
-static PyObject *
-psutil_pids(PyObject *self, PyObject *args) {
- DWORD *proclist = NULL;
- DWORD numberOfReturnedPIDs;
- DWORD i;
- PyObject *py_pid = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
- proclist = psutil_get_pids(&numberOfReturnedPIDs);
- if (proclist == NULL)
- goto error;
-
- for (i = 0; i < numberOfReturnedPIDs; i++) {
- py_pid = PyLong_FromPid(proclist[i]);
- if (!py_pid)
- goto error;
- if (PyList_Append(py_retlist, py_pid))
- goto error;
- Py_CLEAR(py_pid);
- }
-
- // free C array allocated for PIDs
- free(proclist);
- return py_retlist;
-
-error:
- Py_XDECREF(py_pid);
- Py_DECREF(py_retlist);
- if (proclist != NULL)
- free(proclist);
- return NULL;
-}
-
-
-/*
- * Kill a process given its PID.
- */
-static PyObject *
-psutil_proc_kill(PyObject *self, PyObject *args) {
- HANDLE hProcess;
- DWORD pid;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if (pid == 0)
- return AccessDenied("automatically set for PID 0");
-
- hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
- hProcess = psutil_check_phandle(hProcess, pid, 0);
- if (hProcess == NULL) {
- return NULL;
- }
-
- if (! TerminateProcess(hProcess, SIGTERM)) {
- // ERROR_ACCESS_DENIED may happen if the process already died. See:
- // https://github.com/giampaolo/psutil/issues/1099
- // http://bugs.python.org/issue14252
- if (GetLastError() != ERROR_ACCESS_DENIED) {
- PyErr_SetFromOSErrnoWithSyscall("TerminateProcess");
- return NULL;
- }
- }
-
- CloseHandle(hProcess);
- Py_RETURN_NONE;
-}
-
-
-/*
- * Wait for process to terminate and return its exit code.
- */
-static PyObject *
-psutil_proc_wait(PyObject *self, PyObject *args) {
- HANDLE hProcess;
- DWORD ExitCode;
- DWORD retVal;
- DWORD pid;
- long timeout;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "l", &pid, &timeout))
- return NULL;
- if (pid == 0)
- return AccessDenied("automatically set for PID 0");
-
- hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
- FALSE, pid);
- if (hProcess == NULL) {
- if (GetLastError() == ERROR_INVALID_PARAMETER) {
- // no such process; we do not want to raise NSP but
- // return None instead.
- Py_RETURN_NONE;
- }
- else {
- PyErr_SetFromOSErrnoWithSyscall("OpenProcess");
- return NULL;
- }
- }
-
- // wait until the process has terminated
- Py_BEGIN_ALLOW_THREADS
- retVal = WaitForSingleObject(hProcess, timeout);
- Py_END_ALLOW_THREADS
-
- // handle return code
- if (retVal == WAIT_FAILED) {
- PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
- CloseHandle(hProcess);
- return NULL;
- }
- if (retVal == WAIT_TIMEOUT) {
- PyErr_SetString(TimeoutExpired,
- "WaitForSingleObject() returned WAIT_TIMEOUT");
- CloseHandle(hProcess);
- return NULL;
- }
- if (retVal == WAIT_ABANDONED) {
- psutil_debug("WaitForSingleObject() -> WAIT_ABANDONED");
- PyErr_SetString(TimeoutAbandoned,
- "WaitForSingleObject() returned WAIT_ABANDONED");
- CloseHandle(hProcess);
- return NULL;
- }
-
- // WaitForSingleObject() returned WAIT_OBJECT_0. It means the
- // process is gone so we can get its process exit code. The PID
- // may still stick around though but we'll handle that from Python.
- if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
- PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess");
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
-
-#if PY_MAJOR_VERSION >= 3
- return PyLong_FromLong((long) ExitCode);
-#else
- return PyInt_FromLong((long) ExitCode);
-#endif
-}
-
-
-/*
- * Return a Python tuple (user_time, kernel_time)
- */
-static PyObject *
-psutil_proc_times(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- FILETIME ftCreate, ftExit, ftKernel, ftUser;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
-
- if (hProcess == NULL)
- return NULL;
- if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
- if (GetLastError() == ERROR_ACCESS_DENIED) {
- // usually means the process has died so we throw a NoSuchProcess
- // here
- NoSuchProcess("GetProcessTimes -> ERROR_ACCESS_DENIED");
- }
- else {
- PyErr_SetFromWindowsErr(0);
- }
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
-
- /*
- * User and kernel times are represented as a FILETIME structure
- * which contains a 64-bit value representing the number of
- * 100-nanosecond intervals since January 1, 1601 (UTC):
- * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
- * To convert it into a float representing the seconds that the
- * process has executed in user/kernel mode I borrowed the code
- * below from Python's Modules/posixmodule.c
- */
- return Py_BuildValue(
- "(ddd)",
- (double)(ftUser.dwHighDateTime * HI_T + \
- ftUser.dwLowDateTime * LO_T),
- (double)(ftKernel.dwHighDateTime * HI_T + \
- ftKernel.dwLowDateTime * LO_T),
- psutil_FiletimeToUnixTime(ftCreate)
- );
-}
-
-
-/*
- * Return process cmdline as a Python list of cmdline arguments.
- */
-static PyObject *
-psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) {
- DWORD pid;
- int pid_return;
- int use_peb;
- PyObject *py_usepeb = Py_True;
- static char *keywords[] = {"pid", "use_peb", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwdict, _Py_PARSE_PID "|O",
- keywords, &pid, &py_usepeb))
- {
- return NULL;
- }
- if ((pid == 0) || (pid == 4))
- return Py_BuildValue("[]");
-
- pid_return = psutil_pid_is_running(pid);
- if (pid_return == 0)
- return NoSuchProcess("psutil_pid_is_running -> 0");
- if (pid_return == -1)
- return NULL;
-
- use_peb = (py_usepeb == Py_True) ? 1 : 0;
- return psutil_get_cmdline(pid, use_peb);
-}
-
-
-/*
- * Return process cmdline as a Python list of cmdline arguments.
- */
-static PyObject *
-psutil_proc_environ(PyObject *self, PyObject *args) {
- DWORD pid;
- int pid_return;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if ((pid == 0) || (pid == 4))
- return Py_BuildValue("s", "");
-
- pid_return = psutil_pid_is_running(pid);
- if (pid_return == 0)
- return NoSuchProcess("psutil_pid_is_running -> 0");
- if (pid_return == -1)
- return NULL;
-
- return psutil_get_environ(pid);
-}
-
-
-/*
- * Return process executable path. Works for all processes regardless of
- * privilege. NtQuerySystemInformation has some sort of internal cache,
- * since it succeeds even when a process is gone (but not if a PID never
- * existed).
- */
-static PyObject *
-psutil_proc_exe(PyObject *self, PyObject *args) {
- DWORD pid;
- NTSTATUS status;
- PVOID buffer;
- ULONG bufferSize = 0x100;
- SYSTEM_PROCESS_ID_INFORMATION processIdInfo;
- PyObject *py_exe;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- if (pid == 0)
- return AccessDenied("automatically set for PID 0");
-
- buffer = MALLOC_ZERO(bufferSize);
- if (! buffer)
- return PyErr_NoMemory();
- processIdInfo.ProcessId = (HANDLE)(ULONG_PTR)pid;
- processIdInfo.ImageName.Length = 0;
- processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize;
- processIdInfo.ImageName.Buffer = buffer;
-
- status = NtQuerySystemInformation(
- SystemProcessIdInformation,
- &processIdInfo,
- sizeof(SYSTEM_PROCESS_ID_INFORMATION),
- NULL);
-
- if (status == STATUS_INFO_LENGTH_MISMATCH) {
- // Required length is stored in MaximumLength.
- FREE(buffer);
- buffer = MALLOC_ZERO(processIdInfo.ImageName.MaximumLength);
- if (! buffer)
- return PyErr_NoMemory();
- processIdInfo.ImageName.Buffer = buffer;
-
- status = NtQuerySystemInformation(
- SystemProcessIdInformation,
- &processIdInfo,
- sizeof(SYSTEM_PROCESS_ID_INFORMATION),
- NULL);
- }
-
- if (! NT_SUCCESS(status)) {
- FREE(buffer);
- if (psutil_pid_is_running(pid) == 0)
- NoSuchProcess("psutil_pid_is_running -> 0");
- else
- psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
- return NULL;
- }
-
- if (processIdInfo.ImageName.Buffer == NULL) {
- // Happens for PID 4.
- py_exe = Py_BuildValue("s", "");
- }
- else {
- py_exe = PyUnicode_FromWideChar(processIdInfo.ImageName.Buffer,
- processIdInfo.ImageName.Length / 2);
- }
- FREE(buffer);
- return py_exe;
-}
-
-
-/*
- * Return process memory information as a Python tuple.
- */
-static PyObject *
-psutil_proc_memory_info(PyObject *self, PyObject *args) {
- HANDLE hProcess;
- DWORD pid;
- PROCESS_MEMORY_COUNTERS_EX cnt;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (NULL == hProcess)
- return NULL;
-
- if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt,
- sizeof(cnt))) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
- CloseHandle(hProcess);
-
- // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits
- // is an (unsigned long long) and on 32bits is an (unsigned int).
- // "_WIN64" is defined if we're running a 64bit Python interpreter not
- // exclusively if the *system* is 64bit.
-#if defined(_WIN64)
- return Py_BuildValue(
- "(kKKKKKKKKK)",
- cnt.PageFaultCount, // unsigned long
- (unsigned long long)cnt.PeakWorkingSetSize,
- (unsigned long long)cnt.WorkingSetSize,
- (unsigned long long)cnt.QuotaPeakPagedPoolUsage,
- (unsigned long long)cnt.QuotaPagedPoolUsage,
- (unsigned long long)cnt.QuotaPeakNonPagedPoolUsage,
- (unsigned long long)cnt.QuotaNonPagedPoolUsage,
- (unsigned long long)cnt.PagefileUsage,
- (unsigned long long)cnt.PeakPagefileUsage,
- (unsigned long long)cnt.PrivateUsage);
-#else
- return Py_BuildValue(
- "(kIIIIIIIII)",
- cnt.PageFaultCount, // unsigned long
- (unsigned int)cnt.PeakWorkingSetSize,
- (unsigned int)cnt.WorkingSetSize,
- (unsigned int)cnt.QuotaPeakPagedPoolUsage,
- (unsigned int)cnt.QuotaPagedPoolUsage,
- (unsigned int)cnt.QuotaPeakNonPagedPoolUsage,
- (unsigned int)cnt.QuotaNonPagedPoolUsage,
- (unsigned int)cnt.PagefileUsage,
- (unsigned int)cnt.PeakPagefileUsage,
- (unsigned int)cnt.PrivateUsage);
-#endif
-}
-
-
-static int
-psutil_GetProcWsetInformation(
- DWORD pid,
- HANDLE hProcess,
- PMEMORY_WORKING_SET_INFORMATION *wSetInfo)
-{
- NTSTATUS status;
- PVOID buffer;
- SIZE_T bufferSize;
-
- bufferSize = 0x8000;
- buffer = MALLOC_ZERO(bufferSize);
- if (! buffer) {
- PyErr_NoMemory();
- return 1;
- }
-
- while ((status = NtQueryVirtualMemory(
- hProcess,
- NULL,
- MemoryWorkingSetInformation,
- buffer,
- bufferSize,
- NULL)) == STATUS_INFO_LENGTH_MISMATCH)
- {
- FREE(buffer);
- bufferSize *= 2;
- // Fail if we're resizing the buffer to something very large.
- if (bufferSize > 256 * 1024 * 1024) {
- PyErr_SetString(PyExc_RuntimeError,
- "NtQueryVirtualMemory bufsize is too large");
- return 1;
- }
- buffer = MALLOC_ZERO(bufferSize);
- if (! buffer) {
- PyErr_NoMemory();
- return 1;
- }
- }
-
- if (!NT_SUCCESS(status)) {
- if (status == STATUS_ACCESS_DENIED) {
- AccessDenied("NtQueryVirtualMemory -> STATUS_ACCESS_DENIED");
- }
- else if (psutil_pid_is_running(pid) == 0) {
- NoSuchProcess("psutil_pid_is_running -> 0");
- }
- else {
- PyErr_Clear();
- psutil_SetFromNTStatusErr(
- status, "NtQueryVirtualMemory(MemoryWorkingSetInformation)");
- }
- HeapFree(GetProcessHeap(), 0, buffer);
- return 1;
- }
-
- *wSetInfo = (PMEMORY_WORKING_SET_INFORMATION)buffer;
- return 0;
-}
-
-
-/*
- * Returns the USS of the process.
- * Reference:
- * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
- * nsMemoryReporterManager.cpp
- */
-static PyObject *
-psutil_proc_memory_uss(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- PSUTIL_PROCESS_WS_COUNTERS wsCounters;
- PMEMORY_WORKING_SET_INFORMATION wsInfo;
- ULONG_PTR i;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_INFORMATION);
- if (hProcess == NULL)
- return NULL;
-
- if (psutil_GetProcWsetInformation(pid, hProcess, &wsInfo) != 0) {
- CloseHandle(hProcess);
- return NULL;
- }
- memset(&wsCounters, 0, sizeof(PSUTIL_PROCESS_WS_COUNTERS));
-
- for (i = 0; i < wsInfo->NumberOfEntries; i++) {
- // This is what ProcessHacker does.
- /*
- wsCounters.NumberOfPages++;
- if (wsInfo->WorkingSetInfo[i].ShareCount > 1)
- wsCounters.NumberOfSharedPages++;
- if (wsInfo->WorkingSetInfo[i].ShareCount == 0)
- wsCounters.NumberOfPrivatePages++;
- if (wsInfo->WorkingSetInfo[i].Shared)
- wsCounters.NumberOfShareablePages++;
- */
-
- // This is what we do: count shared pages that only one process
- // is using as private (USS).
- if (!wsInfo->WorkingSetInfo[i].Shared ||
- wsInfo->WorkingSetInfo[i].ShareCount <= 1) {
- wsCounters.NumberOfPrivatePages++;
- }
- }
-
- HeapFree(GetProcessHeap(), 0, wsInfo);
- CloseHandle(hProcess);
-
- return Py_BuildValue("I", wsCounters.NumberOfPrivatePages);
-}
-
-
-/*
- * Return a Python integer indicating the total amount of physical memory
- * in bytes.
- */
-static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args) {
- MEMORYSTATUSEX memInfo;
- memInfo.dwLength = sizeof(MEMORYSTATUSEX);
-
- if (! GlobalMemoryStatusEx(&memInfo)) {
- PyErr_SetFromWindowsErr(0);
- return NULL;
- }
- return Py_BuildValue("(LLLLLL)",
- memInfo.ullTotalPhys, // total
- memInfo.ullAvailPhys, // avail
- memInfo.ullTotalPageFile, // total page file
- memInfo.ullAvailPageFile, // avail page file
- memInfo.ullTotalVirtual, // total virtual
- memInfo.ullAvailVirtual); // avail virtual
-}
-
-
-/*
- * Return process current working directory as a Python string.
- */
-static PyObject *
-psutil_proc_cwd(PyObject *self, PyObject *args) {
- DWORD pid;
- int pid_return;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- pid_return = psutil_pid_is_running(pid);
- if (pid_return == 0)
- return NoSuchProcess("psutil_pid_is_running -> 0");
- if (pid_return == -1)
- return NULL;
-
- return psutil_get_cwd(pid);
-}
-
-
-/*
- * Resume or suspends a process
- */
-static PyObject *
-psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) {
- DWORD pid;
- NTSTATUS status;
- HANDLE hProcess;
- PyObject* suspend;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &suspend))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_SUSPEND_RESUME);
- if (hProcess == NULL)
- return NULL;
-
- if (PyObject_IsTrue(suspend))
- status = NtSuspendProcess(hProcess);
- else
- status = NtResumeProcess(hProcess);
-
- if (! NT_SUCCESS(status)) {
- CloseHandle(hProcess);
- return psutil_SetFromNTStatusErr(status, "NtSuspend|ResumeProcess");
- }
-
- CloseHandle(hProcess);
- Py_RETURN_NONE;
-}
-
-
-static PyObject *
-psutil_proc_threads(PyObject *self, PyObject *args) {
- HANDLE hThread = NULL;
- THREADENTRY32 te32 = {0};
- DWORD pid;
- int pid_return;
- int rc;
- FILETIME ftDummy, ftKernel, ftUser;
- HANDLE hThreadSnap = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- goto error;
- if (pid == 0) {
- // raise AD instead of returning 0 as procexp is able to
- // retrieve useful information somehow
- AccessDenied("forced for PID 0");
- goto error;
- }
-
- pid_return = psutil_pid_is_running(pid);
- if (pid_return == 0) {
- NoSuchProcess("psutil_pid_is_running -> 0");
- goto error;
- }
- if (pid_return == -1)
- goto error;
-
- hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
- if (hThreadSnap == INVALID_HANDLE_VALUE) {
- PyErr_SetFromOSErrnoWithSyscall("CreateToolhelp32Snapshot");
- goto error;
- }
-
- // Fill in the size of the structure before using it
- te32.dwSize = sizeof(THREADENTRY32);
-
- if (! Thread32First(hThreadSnap, &te32)) {
- PyErr_SetFromOSErrnoWithSyscall("Thread32First");
- goto error;
- }
-
- // Walk the thread snapshot to find all threads of the process.
- // If the thread belongs to the process, increase the counter.
- do {
- if (te32.th32OwnerProcessID == pid) {
- py_tuple = NULL;
- hThread = NULL;
- hThread = OpenThread(THREAD_QUERY_INFORMATION,
- FALSE, te32.th32ThreadID);
- if (hThread == NULL) {
- // thread has disappeared on us
- continue;
- }
-
- rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel,
- &ftUser);
- if (rc == 0) {
- PyErr_SetFromOSErrnoWithSyscall("GetThreadTimes");
- goto error;
- }
-
- /*
- * User and kernel times are represented as a FILETIME structure
- * which contains a 64-bit value representing the number of
- * 100-nanosecond intervals since January 1, 1601 (UTC):
- * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
- * To convert it into a float representing the seconds that the
- * process has executed in user/kernel mode I borrowed the code
- * below from Python's Modules/posixmodule.c
- */
- py_tuple = Py_BuildValue(
- "kdd",
- te32.th32ThreadID,
- (double)(ftUser.dwHighDateTime * HI_T + \
- ftUser.dwLowDateTime * LO_T),
- (double)(ftKernel.dwHighDateTime * HI_T + \
- ftKernel.dwLowDateTime * LO_T));
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
-
- CloseHandle(hThread);
- }
- } while (Thread32Next(hThreadSnap, &te32));
-
- CloseHandle(hThreadSnap);
- return py_retlist;
-
-error:
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- if (hThread != NULL)
- CloseHandle(hThread);
- if (hThreadSnap != NULL)
- CloseHandle(hThreadSnap);
- return NULL;
-}
-
-
-static PyObject *
-psutil_proc_open_files(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE processHandle;
- DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
- PyObject *py_retlist;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- processHandle = psutil_handle_from_pid(pid, access);
- if (processHandle == NULL)
- return NULL;
-
- py_retlist = psutil_get_open_files(pid, processHandle);
- CloseHandle(processHandle);
- return py_retlist;
-}
-
-
-static PTOKEN_USER
-_psutil_user_token_from_pid(DWORD pid) {
- HANDLE hProcess = NULL;
- HANDLE hToken = NULL;
- PTOKEN_USER userToken = NULL;
- ULONG bufferSize = 0x100;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (hProcess == NULL)
- return NULL;
-
- if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
- PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken");
- goto error;
- }
-
- // Get the user SID.
- while (1) {
- userToken = malloc(bufferSize);
- if (userToken == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- if (!GetTokenInformation(hToken, TokenUser, userToken, bufferSize,
- &bufferSize))
- {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- free(userToken);
- continue;
- }
- else {
- PyErr_SetFromOSErrnoWithSyscall("GetTokenInformation");
- goto error;
- }
- }
- break;
- }
-
- CloseHandle(hProcess);
- CloseHandle(hToken);
- return userToken;
-
-error:
- if (hProcess != NULL)
- CloseHandle(hProcess);
- if (hToken != NULL)
- CloseHandle(hToken);
- return NULL;
-}
-
-
-/*
- * Return process username as a "DOMAIN//USERNAME" string.
- */
-static PyObject *
-psutil_proc_username(PyObject *self, PyObject *args) {
- DWORD pid;
- PTOKEN_USER userToken = NULL;
- WCHAR *userName = NULL;
- WCHAR *domainName = NULL;
- ULONG nameSize = 0x100;
- ULONG domainNameSize = 0x100;
- SID_NAME_USE nameUse;
- PyObject *py_username = NULL;
- PyObject *py_domain = NULL;
- PyObject *py_tuple = NULL;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- userToken = _psutil_user_token_from_pid(pid);
- if (userToken == NULL)
- return NULL;
-
- // resolve the SID to a name
- while (1) {
- userName = malloc(nameSize * sizeof(WCHAR));
- if (userName == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- domainName = malloc(domainNameSize * sizeof(WCHAR));
- if (domainName == NULL) {
- PyErr_NoMemory();
- goto error;
- }
- if (!LookupAccountSidW(NULL, userToken->User.Sid, userName, &nameSize,
- domainName, &domainNameSize, &nameUse))
- {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- free(userName);
- free(domainName);
- continue;
- }
- else if (GetLastError() == ERROR_NONE_MAPPED) {
- // From MS doc:
- // https://docs.microsoft.com/en-us/windows/win32/api/winbase/
- // nf-winbase-lookupaccountsida
- // If the function cannot find an account name for the SID,
- // GetLastError returns ERROR_NONE_MAPPED. This can occur if
- // a network time-out prevents the function from finding the
- // name. It also occurs for SIDs that have no corresponding
- // account name, such as a logon SID that identifies a logon
- // session.
- AccessDenied("LookupAccountSidW -> ERROR_NONE_MAPPED");
- goto error;
- }
- else {
- PyErr_SetFromOSErrnoWithSyscall("LookupAccountSidW");
- goto error;
- }
- }
- break;
- }
-
- py_domain = PyUnicode_FromWideChar(domainName, wcslen(domainName));
- if (! py_domain)
- goto error;
- py_username = PyUnicode_FromWideChar(userName, wcslen(userName));
- if (! py_username)
- goto error;
- py_tuple = Py_BuildValue("OO", py_domain, py_username);
- if (! py_tuple)
- goto error;
- Py_DECREF(py_domain);
- Py_DECREF(py_username);
-
- free(userName);
- free(domainName);
- free(userToken);
- return py_tuple;
-
-error:
- if (userName != NULL)
- free(userName);
- if (domainName != NULL)
- free(domainName);
- if (userToken != NULL)
- free(userToken);
- Py_XDECREF(py_domain);
- Py_XDECREF(py_username);
- Py_XDECREF(py_tuple);
- return NULL;
-}
-
-
-/*
- * Get process priority as a Python integer.
- */
-static PyObject *
-psutil_proc_priority_get(PyObject *self, PyObject *args) {
- DWORD pid;
- DWORD priority;
- HANDLE hProcess;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (hProcess == NULL)
- return NULL;
-
- priority = GetPriorityClass(hProcess);
- if (priority == 0) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
- CloseHandle(hProcess);
- return Py_BuildValue("i", priority);
-}
-
-
-/*
- * Set process priority.
- */
-static PyObject *
-psutil_proc_priority_set(PyObject *self, PyObject *args) {
- DWORD pid;
- int priority;
- int retval;
- HANDLE hProcess;
- DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &priority))
- return NULL;
- hProcess = psutil_handle_from_pid(pid, access);
- if (hProcess == NULL)
- return NULL;
-
- retval = SetPriorityClass(hProcess, priority);
- if (retval == 0) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
- Py_RETURN_NONE;
-}
-
-
-/*
- * Get process IO priority as a Python integer.
- */
-static PyObject *
-psutil_proc_io_priority_get(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- DWORD IoPriority;
- NTSTATUS status;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (hProcess == NULL)
- return NULL;
-
- status = NtQueryInformationProcess(
- hProcess,
- ProcessIoPriority,
- &IoPriority,
- sizeof(DWORD),
- NULL
- );
-
- CloseHandle(hProcess);
- if (! NT_SUCCESS(status))
- return psutil_SetFromNTStatusErr(status, "NtQueryInformationProcess");
- return Py_BuildValue("i", IoPriority);
-}
-
-
-/*
- * Set process IO priority.
- */
-static PyObject *
-psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
- DWORD pid;
- DWORD prio;
- HANDLE hProcess;
- NTSTATUS status;
- DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &prio))
- return NULL;
-
- hProcess = psutil_handle_from_pid(pid, access);
- if (hProcess == NULL)
- return NULL;
-
- status = NtSetInformationProcess(
- hProcess,
- ProcessIoPriority,
- (PVOID)&prio,
- sizeof(DWORD)
- );
-
- CloseHandle(hProcess);
- if (! NT_SUCCESS(status))
- return psutil_SetFromNTStatusErr(status, "NtSetInformationProcess");
- Py_RETURN_NONE;
-}
-
-
-/*
- * Return a Python tuple referencing process I/O counters.
- */
-static PyObject *
-psutil_proc_io_counters(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- IO_COUNTERS IoCounters;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (NULL == hProcess)
- return NULL;
-
- if (! GetProcessIoCounters(hProcess, &IoCounters)) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
- return Py_BuildValue("(KKKKKK)",
- IoCounters.ReadOperationCount,
- IoCounters.WriteOperationCount,
- IoCounters.ReadTransferCount,
- IoCounters.WriteTransferCount,
- IoCounters.OtherOperationCount,
- IoCounters.OtherTransferCount);
-}
-
-
-/*
- * Return process CPU affinity as a bitmask
- */
-static PyObject *
-psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- DWORD_PTR proc_mask;
- DWORD_PTR system_mask;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (hProcess == NULL) {
- return NULL;
- }
- if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
-#ifdef _WIN64
- return Py_BuildValue("K", (unsigned long long)proc_mask);
-#else
- return Py_BuildValue("k", (unsigned long)proc_mask);
-#endif
-}
-
-
-/*
- * Set process CPU affinity
- */
-static PyObject *
-psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
- DWORD_PTR mask;
-
-#ifdef _WIN64
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "K", &pid, &mask))
-#else
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID "k", &pid, &mask))
-#endif
- {
- return NULL;
- }
- hProcess = psutil_handle_from_pid(pid, access);
- if (hProcess == NULL)
- return NULL;
-
- if (SetProcessAffinityMask(hProcess, mask) == 0) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
-
- CloseHandle(hProcess);
- Py_RETURN_NONE;
-}
-
-
-/*
- * Return True if all process threads are in waiting/suspended state.
- */
-static PyObject *
-psutil_proc_is_suspended(PyObject *self, PyObject *args) {
- DWORD pid;
- ULONG i;
- PSYSTEM_PROCESS_INFORMATION process;
- PVOID buffer;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- if (! psutil_get_proc_info(pid, &process, &buffer))
- return NULL;
- for (i = 0; i < process->NumberOfThreads; i++) {
- if (process->Threads[i].ThreadState != Waiting ||
- process->Threads[i].WaitReason != Suspended)
- {
- free(buffer);
- Py_RETURN_FALSE;
- }
- }
- free(buffer);
- Py_RETURN_TRUE;
-}
-
-
-/*
- * Return a Python dict of tuples for disk I/O information
- */
-static PyObject *
-psutil_users(PyObject *self, PyObject *args) {
- HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
- LPWSTR buffer_user = NULL;
- LPWSTR buffer_addr = NULL;
- LPWSTR buffer_info = NULL;
- PWTS_SESSION_INFOW sessions = NULL;
- DWORD count;
- DWORD i;
- DWORD sessionId;
- DWORD bytes;
- PWTS_CLIENT_ADDRESS address;
- char address_str[50];
- PWTSINFOW wts_info;
- PyObject *py_tuple = NULL;
- PyObject *py_address = NULL;
- PyObject *py_username = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
-
- if (WTSEnumerateSessionsW == NULL ||
- WTSQuerySessionInformationW == NULL ||
- WTSFreeMemory == NULL) {
- // If we don't run in an environment that is a Remote Desktop Services environment
- // the Wtsapi32 proc might not be present.
- // https://docs.microsoft.com/en-us/windows/win32/termserv/run-time-linking-to-wtsapi32-dll
- return py_retlist;
- }
-
- if (WTSEnumerateSessionsW(hServer, 0, 1, &sessions, &count) == 0) {
- if (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) {
- // On Windows Nano server, the Wtsapi32 API can be present, but return WinError 120.
- return py_retlist;
- }
- PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessionsW");
- goto error;
- }
-
- for (i = 0; i < count; i++) {
- py_address = NULL;
- py_tuple = NULL;
- sessionId = sessions[i].SessionId;
- if (buffer_user != NULL)
- WTSFreeMemory(buffer_user);
- if (buffer_addr != NULL)
- WTSFreeMemory(buffer_addr);
- if (buffer_info != NULL)
- WTSFreeMemory(buffer_info);
-
- buffer_user = NULL;
- buffer_addr = NULL;
- buffer_info = NULL;
-
- // username
- bytes = 0;
- if (WTSQuerySessionInformationW(hServer, sessionId, WTSUserName,
- &buffer_user, &bytes) == 0) {
- PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
- goto error;
- }
- if (bytes <= 2)
- continue;
-
- // address
- bytes = 0;
- if (WTSQuerySessionInformationW(hServer, sessionId, WTSClientAddress,
- &buffer_addr, &bytes) == 0) {
- PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
- goto error;
- }
-
- address = (PWTS_CLIENT_ADDRESS)buffer_addr;
- if (address->AddressFamily == 2) { // AF_INET == 2
- sprintf_s(address_str,
- _countof(address_str),
- "%u.%u.%u.%u",
- // The IP address is offset by two bytes from the start of the Address member of the WTS_CLIENT_ADDRESS structure.
- address->Address[2],
- address->Address[3],
- address->Address[4],
- address->Address[5]);
- py_address = Py_BuildValue("s", address_str);
- if (!py_address)
- goto error;
- }
- else {
- py_address = Py_None;
- }
-
- // login time
- bytes = 0;
- if (WTSQuerySessionInformationW(hServer, sessionId, WTSSessionInfo,
- &buffer_info, &bytes) == 0) {
- PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
- goto error;
- }
- wts_info = (PWTSINFOW)buffer_info;
-
- py_username = PyUnicode_FromWideChar(buffer_user, wcslen(buffer_user));
- if (py_username == NULL)
- goto error;
-
- py_tuple = Py_BuildValue(
- "OOd",
- py_username,
- py_address,
- psutil_LargeIntegerToUnixTime(wts_info->ConnectTime)
- );
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_username);
- Py_CLEAR(py_address);
- Py_CLEAR(py_tuple);
- }
-
- WTSFreeMemory(sessions);
- WTSFreeMemory(buffer_user);
- WTSFreeMemory(buffer_addr);
- WTSFreeMemory(buffer_info);
- return py_retlist;
-
-error:
- Py_XDECREF(py_username);
- Py_XDECREF(py_tuple);
- Py_XDECREF(py_address);
- Py_DECREF(py_retlist);
-
- if (sessions != NULL)
- WTSFreeMemory(sessions);
- if (buffer_user != NULL)
- WTSFreeMemory(buffer_user);
- if (buffer_addr != NULL)
- WTSFreeMemory(buffer_addr);
- if (buffer_info != NULL)
- WTSFreeMemory(buffer_info);
- return NULL;
-}
-
-
-/*
- * Return the number of handles opened by process.
- */
-static PyObject *
-psutil_proc_num_handles(PyObject *self, PyObject *args) {
- DWORD pid;
- HANDLE hProcess;
- DWORD handleCount;
-
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- return NULL;
- hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
- if (NULL == hProcess)
- return NULL;
- if (! GetProcessHandleCount(hProcess, &handleCount)) {
- PyErr_SetFromWindowsErr(0);
- CloseHandle(hProcess);
- return NULL;
- }
- CloseHandle(hProcess);
- return Py_BuildValue("k", handleCount);
-}
-
-
-static char *get_region_protection_string(ULONG protection) {
- switch (protection & 0xff) {
- case PAGE_NOACCESS:
- return "";
- case PAGE_READONLY:
- return "r";
- case PAGE_READWRITE:
- return "rw";
- case PAGE_WRITECOPY:
- return "wc";
- case PAGE_EXECUTE:
- return "x";
- case PAGE_EXECUTE_READ:
- return "xr";
- case PAGE_EXECUTE_READWRITE:
- return "xrw";
- case PAGE_EXECUTE_WRITECOPY:
- return "xwc";
- default:
- return "?";
- }
-}
-
-
-/*
- * Return a list of process's memory mappings.
- */
-static PyObject *
-psutil_proc_memory_maps(PyObject *self, PyObject *args) {
- MEMORY_BASIC_INFORMATION basicInfo;
- DWORD pid;
- HANDLE hProcess = NULL;
- PVOID baseAddress;
- WCHAR mappedFileName[MAX_PATH];
- LPVOID maxAddr;
- // required by GetMappedFileNameW
- DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_tuple = NULL;
- PyObject *py_str = NULL;
-
- if (py_retlist == NULL)
- return NULL;
- if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
- goto error;
- hProcess = psutil_handle_from_pid(pid, access);
- if (NULL == hProcess)
- goto error;
-
- maxAddr = PSUTIL_SYSTEM_INFO.lpMaximumApplicationAddress;
- baseAddress = NULL;
-
- while (VirtualQueryEx(hProcess, baseAddress, &basicInfo,
- sizeof(MEMORY_BASIC_INFORMATION)))
- {
- py_tuple = NULL;
- if (baseAddress > maxAddr)
- break;
- if (GetMappedFileNameW(hProcess, baseAddress, mappedFileName,
- sizeof(mappedFileName)))
- {
- py_str = PyUnicode_FromWideChar(mappedFileName,
- wcslen(mappedFileName));
- if (py_str == NULL)
- goto error;
-#ifdef _WIN64
- py_tuple = Py_BuildValue(
- "(KsOI)",
- (unsigned long long)baseAddress,
-#else
- py_tuple = Py_BuildValue(
- "(ksOI)",
- (unsigned long)baseAddress,
-#endif
- get_region_protection_string(basicInfo.Protect),
- py_str,
- basicInfo.RegionSize);
-
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_tuple);
- Py_CLEAR(py_str);
- }
- baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize;
- }
-
- CloseHandle(hProcess);
- return py_retlist;
-
-error:
- Py_XDECREF(py_tuple);
- Py_XDECREF(py_str);
- Py_DECREF(py_retlist);
- if (hProcess != NULL)
- CloseHandle(hProcess);
- return NULL;
-}
-
-
-/*
- * Return a {pid:ppid, ...} dict for all running processes.
- */
-static PyObject *
-psutil_ppid_map(PyObject *self, PyObject *args) {
- PyObject *py_pid = NULL;
- PyObject *py_ppid = NULL;
- PyObject *py_retdict = PyDict_New();
- HANDLE handle = NULL;
- PROCESSENTRY32 pe = {0};
- pe.dwSize = sizeof(PROCESSENTRY32);
-
- if (py_retdict == NULL)
- return NULL;
- handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (handle == INVALID_HANDLE_VALUE) {
- PyErr_SetFromWindowsErr(0);
- Py_DECREF(py_retdict);
- return NULL;
- }
-
- if (Process32First(handle, &pe)) {
- do {
- py_pid = PyLong_FromPid(pe.th32ProcessID);
- if (py_pid == NULL)
- goto error;
- py_ppid = PyLong_FromPid(pe.th32ParentProcessID);
- if (py_ppid == NULL)
- goto error;
- if (PyDict_SetItem(py_retdict, py_pid, py_ppid))
- goto error;
- Py_CLEAR(py_pid);
- Py_CLEAR(py_ppid);
- } while (Process32Next(handle, &pe));
- }
-
- CloseHandle(handle);
- return py_retdict;
-
-error:
- Py_XDECREF(py_pid);
- Py_XDECREF(py_ppid);
- Py_DECREF(py_retdict);
- CloseHandle(handle);
- return NULL;
-}
-
-
-/*
- * Return battery usage stats.
- */
-static PyObject *
-psutil_sensors_battery(PyObject *self, PyObject *args) {
- SYSTEM_POWER_STATUS sps;
-
- if (GetSystemPowerStatus(&sps) == 0) {
- PyErr_SetFromWindowsErr(0);
- return NULL;
- }
- return Py_BuildValue(
- "iiiI",
- sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown
- // status flag:
- // 1, 2, 4 = high, low, critical
- // 8 = charging
- // 128 = no battery
- sps.BatteryFlag,
- sps.BatteryLifePercent, // percent
- sps.BatteryLifeTime // remaining secs
- );
-}
-
-
-/*
- * System memory page size as an int.
- */
-static PyObject *
-psutil_getpagesize(PyObject *self, PyObject *args) {
- // XXX: we may want to use GetNativeSystemInfo to differentiate
- // page size for WoW64 processes (but am not sure).
- return Py_BuildValue("I", PSUTIL_SYSTEM_INFO.dwPageSize);
-}
-
// ------------------------ Python init ---------------------------
@@ -1551,127 +40,73 @@ static PyMethodDef
PsutilMethods[] = {
// --- per-process functions
{"proc_cmdline", (PyCFunction)(void(*)(void))psutil_proc_cmdline,
- METH_VARARGS | METH_KEYWORDS,
- "Return process cmdline as a list of cmdline arguments"},
- {"proc_environ", psutil_proc_environ, METH_VARARGS,
- "Return process environment data"},
- {"proc_exe", psutil_proc_exe, METH_VARARGS,
- "Return path of the process executable"},
- {"proc_kill", psutil_proc_kill, METH_VARARGS,
- "Kill the process identified by the given PID"},
- {"proc_times", psutil_proc_times, METH_VARARGS,
- "Return tuple of user/kern time for the given PID"},
- {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS,
- "Return a tuple of process memory information"},
- {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
- "Return the USS of the process"},
- {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
- "Return process current working directory"},
- {"proc_suspend_or_resume", psutil_proc_suspend_or_resume, METH_VARARGS,
- "Suspend or resume a process"},
- {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
- "Return files opened by process"},
- {"proc_username", psutil_proc_username, METH_VARARGS,
- "Return the username of a process"},
- {"proc_threads", psutil_proc_threads, METH_VARARGS,
- "Return process threads information as a list of tuple"},
- {"proc_wait", psutil_proc_wait, METH_VARARGS,
- "Wait for process to terminate and return its exit code."},
- {"proc_priority_get", psutil_proc_priority_get, METH_VARARGS,
- "Return process priority."},
- {"proc_priority_set", psutil_proc_priority_set, METH_VARARGS,
- "Set process priority."},
- {"proc_io_priority_get", psutil_proc_io_priority_get, METH_VARARGS,
- "Return process IO priority."},
- {"proc_io_priority_set", psutil_proc_io_priority_set, METH_VARARGS,
- "Set process IO priority."},
- {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
- "Return process CPU affinity as a bitmask."},
- {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
- "Set process CPU affinity."},
- {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
- "Get process I/O counters."},
- {"proc_is_suspended", psutil_proc_is_suspended, METH_VARARGS,
- "Return True if one of the process threads is in a suspended state"},
- {"proc_num_handles", psutil_proc_num_handles, METH_VARARGS,
- "Return the number of handles opened by process."},
- {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
- "Return a list of process's memory mappings"},
+ METH_VARARGS | METH_KEYWORDS},
+ {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS},
+ {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS},
+ {"proc_cwd", psutil_proc_cwd, METH_VARARGS},
+ {"proc_environ", psutil_proc_environ, METH_VARARGS},
+ {"proc_exe", psutil_proc_exe, METH_VARARGS},
+ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS},
+ {"proc_io_priority_get", psutil_proc_io_priority_get, METH_VARARGS},
+ {"proc_io_priority_set", psutil_proc_io_priority_set, METH_VARARGS},
+ {"proc_is_suspended", psutil_proc_is_suspended, METH_VARARGS},
+ {"proc_kill", psutil_proc_kill, METH_VARARGS},
+ {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS},
+ {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS},
+ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS},
+ {"proc_num_handles", psutil_proc_num_handles, METH_VARARGS},
+ {"proc_open_files", psutil_proc_open_files, METH_VARARGS},
+ {"proc_priority_get", psutil_proc_priority_get, METH_VARARGS},
+ {"proc_priority_set", psutil_proc_priority_set, METH_VARARGS},
+ {"proc_suspend_or_resume", psutil_proc_suspend_or_resume, METH_VARARGS},
+ {"proc_threads", psutil_proc_threads, METH_VARARGS},
+ {"proc_times", psutil_proc_times, METH_VARARGS},
+ {"proc_username", psutil_proc_username, METH_VARARGS},
+ {"proc_wait", psutil_proc_wait, METH_VARARGS},
// --- alternative pinfo interface
- {"proc_info", psutil_proc_info, METH_VARARGS,
- "Various process information"},
+ {"proc_info", psutil_proc_info, METH_VARARGS},
// --- system-related functions
- {"pids", psutil_pids, METH_VARARGS,
- "Returns a list of PIDs currently running on the system"},
- {"ppid_map", psutil_ppid_map, METH_VARARGS,
- "Return a {pid:ppid, ...} dict for all running processes"},
- {"pid_exists", psutil_pid_exists, METH_VARARGS,
- "Determine if the process exists in the current process list."},
- {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
- "Returns the number of logical CPUs on the system"},
- {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
- "Returns the number of physical CPUs on the system"},
- {"boot_time", psutil_boot_time, METH_VARARGS,
- "Return the system boot time expressed in seconds since the epoch."},
- {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
- "Return the total amount of physical memory, in bytes"},
- {"cpu_times", psutil_cpu_times, METH_VARARGS,
- "Return system cpu times as a list"},
- {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
- "Return system per-cpu times as a list of tuples"},
- {"disk_usage", psutil_disk_usage, METH_VARARGS,
- "Return path's disk total and free as a Python tuple."},
- {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
- "Return dict of tuples of networks I/O information."},
- {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
- "Return dict of tuples of disks I/O information."},
- {"users", psutil_users, METH_VARARGS,
- "Return a list of currently connected users."},
- {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
- "Return disk partitions."},
- {"net_connections", psutil_net_connections, METH_VARARGS,
- "Return system-wide connections"},
- {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
- "Return NICs addresses."},
- {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
- "Return NICs stats."},
- {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
- "Return NICs stats."},
- {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
- "Return CPU frequency."},
- {"init_loadavg_counter", (PyCFunction)psutil_init_loadavg_counter,
- METH_VARARGS,
- "Initializes the emulated load average calculator."},
- {"getloadavg", (PyCFunction)psutil_get_loadavg, METH_VARARGS,
- "Returns the emulated POSIX-like load average."},
- {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
- "Return battery metrics usage."},
- {"getpagesize", psutil_getpagesize, METH_VARARGS,
- "Return system memory page size."},
+ {"boot_time", psutil_boot_time, METH_VARARGS},
+ {"cpu_count_cores", psutil_cpu_count_cores, METH_VARARGS},
+ {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS},
+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS},
+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS},
+ {"cpu_times", psutil_cpu_times, METH_VARARGS},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS},
+ {"disk_usage", psutil_disk_usage, METH_VARARGS},
+ {"getloadavg", (PyCFunction)psutil_get_loadavg, METH_VARARGS},
+ {"getpagesize", psutil_getpagesize, METH_VARARGS},
+ {"swap_percent", psutil_swap_percent, METH_VARARGS},
+ {"init_loadavg_counter", (PyCFunction)psutil_init_loadavg_counter, METH_VARARGS},
+ {"net_connections", psutil_net_connections, METH_VARARGS},
+ {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS},
+ {"net_if_stats", psutil_net_if_stats, METH_VARARGS},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS},
+ {"pid_exists", psutil_pid_exists, METH_VARARGS},
+ {"pids", psutil_pids, METH_VARARGS},
+ {"ppid_map", psutil_ppid_map, METH_VARARGS},
+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS},
+ {"users", psutil_users, METH_VARARGS},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS},
// --- windows services
- {"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS,
- "List all services"},
- {"winservice_query_config", psutil_winservice_query_config, METH_VARARGS,
- "Return service config"},
- {"winservice_query_status", psutil_winservice_query_status, METH_VARARGS,
- "Return service config"},
- {"winservice_query_descr", psutil_winservice_query_descr, METH_VARARGS,
- "Return the description of a service"},
- {"winservice_start", psutil_winservice_start, METH_VARARGS,
- "Start a service"},
- {"winservice_stop", psutil_winservice_stop, METH_VARARGS,
- "Stop a service"},
+ {"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS},
+ {"winservice_query_config", psutil_winservice_query_config, METH_VARARGS},
+ {"winservice_query_descr", psutil_winservice_query_descr, METH_VARARGS},
+ {"winservice_query_status", psutil_winservice_query_status, METH_VARARGS},
+ {"winservice_start", psutil_winservice_start, METH_VARARGS},
+ {"winservice_stop", psutil_winservice_stop, METH_VARARGS},
// --- windows API bindings
- {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
- "QueryDosDevice binding"},
+ {"QueryDosDevice", psutil_QueryDosDevice, METH_VARARGS},
// --- others
- {"set_testing", psutil_set_testing, METH_NOARGS,
- "Set psutil in testing mode"},
+ {"check_pid_range", psutil_check_pid_range, METH_VARARGS},
+ {"set_debug", psutil_set_debug, METH_VARARGS},
{NULL, NULL, 0, NULL}
};
@@ -1732,8 +167,6 @@ void init_psutil_windows(void)
if (psutil_setup() != 0)
INITERROR;
- if (psutil_load_globals() != 0)
- INITERROR;
if (psutil_set_se_debug() != 0)
INITERROR;
diff --git a/contrib/python/psutil/py3/psutil/_pswindows.py b/contrib/python/psutil/py3/psutil/_pswindows.py
index 98baef5955..2d3a0c9fdb 100644
--- a/contrib/python/psutil/py3/psutil/_pswindows.py
+++ b/contrib/python/psutil/py3/psutil/_pswindows.py
@@ -14,22 +14,22 @@ import time
from collections import namedtuple
from . import _common
+from ._common import ENCODING
+from ._common import ENCODING_ERRS
from ._common import AccessDenied
+from ._common import NoSuchProcess
+from ._common import TimeoutExpired
from ._common import conn_tmap
from ._common import conn_to_ntuple
from ._common import debug
-from ._common import ENCODING
-from ._common import ENCODING_ERRS
from ._common import isfile_strict
from ._common import memoize
from ._common import memoize_when_activated
-from ._common import NoSuchProcess
from ._common import parse_environ_block
-from ._common import TimeoutExpired
from ._common import usage_percent
+from ._compat import PY3
from ._compat import long
from ._compat import lru_cache
-from ._compat import PY3
from ._compat import range
from ._compat import unicode
from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
@@ -39,11 +39,14 @@ from ._psutil_windows import IDLE_PRIORITY_CLASS
from ._psutil_windows import NORMAL_PRIORITY_CLASS
from ._psutil_windows import REALTIME_PRIORITY_CLASS
+
try:
from . import _psutil_windows as cext
except ImportError as err:
- if str(err).lower().startswith("dll load failed") and \
- sys.getwindowsversion()[0] < 6:
+ if (
+ str(err).lower().startswith("dll load failed")
+ and sys.getwindowsversion()[0] < 6
+ ):
# We may get here if:
# 1) we are on an old Windows version
# 2) psutil was installed via pip + wheel
@@ -55,13 +58,14 @@ except ImportError as err:
else:
raise
-if sys.version_info >= (3, 4):
+if PY3:
import enum
else:
enum = None
# process priority constants, import from __init__.py:
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
+# fmt: off
__extra__all__ = [
"win_service_iter", "win_service_get",
# Process priority
@@ -73,6 +77,7 @@ __extra__all__ = [
# others
"CONN_DELETE_TCB", "AF_LINK",
]
+# fmt: on
# =====================================================================
@@ -106,6 +111,7 @@ TCP_STATUSES = {
}
if enum is not None:
+
class Priority(enum.IntEnum):
ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS
BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS
@@ -122,11 +128,13 @@ if enum is None:
IOPRIO_NORMAL = 2
IOPRIO_HIGH = 3
else:
+
class IOPriority(enum.IntEnum):
IOPRIO_VERYLOW = 0
IOPRIO_LOW = 1
IOPRIO_NORMAL = 2
IOPRIO_HIGH = 3
+
globals().update(IOPriority.__members__)
pinfo_map = dict(
@@ -160,6 +168,7 @@ pinfo_map = dict(
# =====================================================================
+# fmt: off
# psutil.cpu_times()
scputimes = namedtuple('scputimes',
['user', 'system', 'idle', 'interrupt', 'dpc'])
@@ -182,6 +191,7 @@ pmmap_ext = namedtuple(
pio = namedtuple('pio', ['read_count', 'write_count',
'read_bytes', 'write_bytes',
'other_count', 'other_bytes'])
+# fmt: on
# =====================================================================
@@ -194,11 +204,11 @@ def convert_dos_path(s):
r"""Convert paths using native DOS format like:
"\Device\HarddiskVolume1\Windows\systemew\file.txt"
into:
- "C:\Windows\systemew\file.txt"
+ "C:\Windows\systemew\file.txt".
"""
rawdrive = '\\'.join(s.split('\\')[:3])
- driveletter = cext.win32_QueryDosDevice(rawdrive)
- remainder = s[len(rawdrive):]
+ driveletter = cext.QueryDosDevice(rawdrive)
+ remainder = s[len(rawdrive) :]
return os.path.join(driveletter, remainder)
@@ -228,7 +238,7 @@ def getpagesize():
def virtual_memory():
"""System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
- totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
+ totphys, availphys, totsys, availsys = mem
#
total = totphys
avail = availphys
@@ -241,10 +251,26 @@ def virtual_memory():
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
mem = cext.virtual_mem()
- total = mem[2]
- free = mem[3]
- used = total - free
- percent = usage_percent(used, total, round_=1)
+
+ total_phys = mem[0]
+ total_system = mem[2]
+
+ # system memory (commit total/limit) is the sum of physical and swap
+ # thus physical memory values need to be subtracted to get swap values
+ total = total_system - total_phys
+ # commit total is incremented immediately (decrementing free_system)
+ # while the corresponding free physical value is not decremented until
+ # pages are accessed, so we can't use free system memory for swap.
+ # instead, we calculate page file usage based on performance counter
+ if total > 0:
+ percentswap = cext.swap_percent()
+ used = int(0.01 * percentswap * total)
+ else:
+ percentswap = 0.0
+ used = 0
+
+ free = total - used
+ percent = round(percentswap, 1)
return _common.sswap(total, used, free, percent, 0, 0)
@@ -286,8 +312,9 @@ def cpu_times():
# interrupt and dpc times. cext.per_cpu_times() does, so we
# rely on it to get those only.
percpu_summed = scputimes(*[sum(n) for n in zip(*cext.per_cpu_times())])
- return scputimes(user, system, idle,
- percpu_summed.interrupt, percpu_summed.dpc)
+ return scputimes(
+ user, system, idle, percpu_summed.interrupt, percpu_summed.dpc
+ )
def per_cpu_times():
@@ -304,17 +331,18 @@ def cpu_count_logical():
return cext.cpu_count_logical()
-def cpu_count_physical():
- """Return the number of physical CPU cores in the system."""
- return cext.cpu_count_phys()
+def cpu_count_cores():
+ """Return the number of CPU cores in the system."""
+ return cext.cpu_count_cores()
def cpu_stats():
"""Return CPU statistics."""
ctx_switches, interrupts, dpcs, syscalls = cext.cpu_stats()
soft_interrupts = 0
- return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
- syscalls)
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls
+ )
def cpu_freq():
@@ -331,7 +359,8 @@ _loadavg_inititialized = False
def getloadavg():
"""Return the number of processes in the system run queue averaged
- over the last 1, 5, and 15 minutes respectively as a tuple"""
+ over the last 1, 5, and 15 minutes respectively as a tuple.
+ """
global _loadavg_inititialized
if not _loadavg_inititialized:
@@ -353,15 +382,25 @@ def net_connections(kind, _pid=-1):
connections (as opposed to connections opened by one process only).
"""
if kind not in conn_tmap:
- raise ValueError("invalid %r kind argument; choose between %s"
- % (kind, ', '.join([repr(x) for x in conn_tmap])))
+ raise ValueError(
+ "invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in conn_tmap]))
+ )
families, types = conn_tmap[kind]
rawlist = cext.net_connections(_pid, families, types)
ret = set()
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
- nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES,
- pid=pid if _pid == -1 else None)
+ nt = conn_to_ntuple(
+ fd,
+ fam,
+ type,
+ laddr,
+ raddr,
+ status,
+ TCP_STATUSES,
+ pid=pid if _pid == -1 else None,
+ )
ret.add(nt)
return list(ret)
@@ -377,7 +416,7 @@ def net_if_stats():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu, '')
return ret
@@ -476,7 +515,7 @@ def win_service_get(name):
return service
-class WindowsService(object):
+class WindowsService:
"""Represents an installed Windows service."""
def __init__(self, name, display_name):
@@ -485,7 +524,9 @@ class WindowsService(object):
def __str__(self):
details = "(name=%r, display_name=%r)" % (
- self._name, self._display_name)
+ self._name,
+ self._display_name,
+ )
return "%s%s" % (self.__class__.__name__, details)
def __repr__(self):
@@ -503,14 +544,16 @@ class WindowsService(object):
def _query_config(self):
with self._wrap_exceptions():
- display_name, binpath, username, start_type = \
+ display_name, binpath, username, start_type = (
cext.winservice_query_config(self._name)
+ )
# XXX - update _self.display_name?
return dict(
display_name=py2_strencode(display_name),
binpath=py2_strencode(binpath),
username=py2_strencode(username),
- start_type=py2_strencode(start_type))
+ start_type=py2_strencode(start_type),
+ )
def _query_status(self):
with self._wrap_exceptions():
@@ -528,15 +571,17 @@ class WindowsService(object):
yield
except OSError as err:
if is_permission_err(err):
- raise AccessDenied(
- pid=None, name=self._name,
- msg="service %r is not querable (not enough privileges)" %
- self._name)
- elif err.winerror in (cext.ERROR_INVALID_NAME,
- cext.ERROR_SERVICE_DOES_NOT_EXIST):
- raise NoSuchProcess(
- pid=None, name=self._name,
- msg="service %r does not exist)" % self._name)
+ msg = (
+ "service %r is not querable (not enough privileges)"
+ % self._name
+ )
+ raise AccessDenied(pid=None, name=self._name, msg=msg)
+ elif err.winerror in (
+ cext.ERROR_INVALID_NAME,
+ cext.ERROR_SERVICE_DOES_NOT_EXIST,
+ ):
+ msg = "service %r does not exist" % self._name
+ raise NoSuchProcess(pid=None, name=self._name, msg=msg)
else:
raise
@@ -653,12 +698,17 @@ ppid_map = cext.ppid_map # used internally by Process.children()
def is_permission_err(exc):
"""Return True if this is a permission error."""
assert isinstance(exc, OSError), exc
+ if exc.errno in (errno.EPERM, errno.EACCES):
+ return True
# On Python 2 OSError doesn't always have 'winerror'. Sometimes
# it does, in which case the original exception was WindowsError
# (which is a subclass of OSError).
- return exc.errno in (errno.EPERM, errno.EACCES) or \
- getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED,
- cext.ERROR_PRIVILEGE_NOT_HELD)
+ if getattr(exc, "winerror", -1) in (
+ cext.ERROR_ACCESS_DENIED,
+ cext.ERROR_PRIVILEGE_NOT_HELD,
+ ):
+ return True
+ return False
def convert_oserror(exc, pid=None, name=None):
@@ -673,24 +723,27 @@ def convert_oserror(exc, pid=None, name=None):
def wrap_exceptions(fun):
"""Decorator which converts OSError into NoSuchProcess or AccessDenied."""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
raise convert_oserror(err, pid=self.pid, name=self._name)
+
return wrapper
def retry_error_partial_copy(fun):
"""Workaround for https://github.com/giampaolo/psutil/issues/875.
- See: https://stackoverflow.com/questions/4457745#4457745
+ See: https://stackoverflow.com/questions/4457745#4457745.
"""
+
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
delay = 0.0001
times = 33
- for x in range(times): # retries for roughly 1 second
+ for _ in range(times): # retries for roughly 1 second
try:
return fun(self, *args, **kwargs)
except WindowsError as _:
@@ -699,16 +752,17 @@ def retry_error_partial_copy(fun):
time.sleep(delay)
delay = min(delay * 2, 0.04)
continue
- else:
- raise
- else:
- msg = "%s retried %s times, converted to AccessDenied as it's " \
- "still returning %r" % (fun, times, err)
- raise AccessDenied(pid=self.pid, name=self._name, msg=msg)
+ raise
+ msg = (
+ "{} retried {} times, converted to AccessDenied as it's still"
+ "returning {}".format(fun, times, err)
+ )
+ raise AccessDenied(pid=self.pid, name=self._name, msg=msg)
+
return wrapper
-class Process(object):
+class Process:
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid", "_cache"]
@@ -759,7 +813,7 @@ class Process(object):
# 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens
# (perhaps PyPy's JIT delaying garbage collection of files?).
if err.errno == 24:
- debug("%r forced into AccessDenied" % err)
+ debug("%r translated into AccessDenied" % err)
raise AccessDenied(self.pid, self._name)
raise
else:
@@ -834,14 +888,14 @@ class Process(object):
t = self._get_raw_meminfo()
rss = t[2] # wset
vms = t[7] # pagefile
- return pmem(*(rss, vms, ) + t)
+ return pmem(*(rss, vms) + t)
@wrap_exceptions
def memory_full_info(self):
basic_mem = self.memory_info()
uss = cext.proc_memory_uss(self.pid)
uss *= getpagesize()
- return pfullmem(*basic_mem + (uss, ))
+ return pfullmem(*basic_mem + (uss,))
def memory_maps(self):
try:
@@ -867,13 +921,17 @@ class Process(object):
if sig == signal.SIGTERM:
cext.proc_kill(self.pid)
# py >= 2.7
- elif sig in (getattr(signal, "CTRL_C_EVENT", object()),
- getattr(signal, "CTRL_BREAK_EVENT", object())):
+ elif sig in (
+ getattr(signal, "CTRL_C_EVENT", object()),
+ getattr(signal, "CTRL_BREAK_EVENT", object()),
+ ):
os.kill(self.pid, sig)
else:
- raise ValueError(
+ msg = (
"only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals "
- "are supported on Windows")
+ "are supported on Windows"
+ )
+ raise ValueError(msg)
@wrap_exceptions
def wait(self, timeout=None):
@@ -1025,9 +1083,14 @@ class Process(object):
@wrap_exceptions
def ionice_set(self, ioclass, value):
if value:
- raise TypeError("value argument not accepted on Windows")
- if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL,
- IOPRIO_HIGH):
+ msg = "value argument not accepted on Windows"
+ raise TypeError(msg)
+ if ioclass not in (
+ IOPRIO_VERYLOW,
+ IOPRIO_LOW,
+ IOPRIO_NORMAL,
+ IOPRIO_HIGH,
+ ):
raise ValueError("%s is not a valid priority" % ioclass)
cext.proc_io_priority_set(self.pid, ioclass)
@@ -1061,6 +1124,7 @@ class Process(object):
def cpu_affinity_get(self):
def from_bitmask(x):
return [i for i in range(64) if (1 << i) & x]
+
bitmask = cext.proc_cpu_affinity_get(self.pid)
return from_bitmask(bitmask)
@@ -1071,7 +1135,7 @@ class Process(object):
raise ValueError("invalid argument %r" % ls)
out = 0
for b in ls:
- out |= 2 ** b
+ out |= 2**b
return out
# SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
@@ -1082,7 +1146,8 @@ class Process(object):
if cpu not in allcpus:
if not isinstance(cpu, (int, long)):
raise TypeError(
- "invalid CPU %r; an integer is required" % cpu)
+ "invalid CPU %r; an integer is required" % cpu
+ )
else:
raise ValueError("invalid CPU %r" % cpu)
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/disk.c b/contrib/python/psutil/py3/psutil/arch/linux/disk.c
new file mode 100644
index 0000000000..692a7d5d47
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/disk.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <mntent.h>
+
+#include "../../_psutil_common.h"
+
+
+// Return disk mounted partitions as a list of tuples including device,
+// mount point and filesystem type.
+PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ FILE *file = NULL;
+ struct mntent *entry;
+ char *mtab_path;
+ PyObject *py_dev = NULL;
+ PyObject *py_mountp = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &mtab_path))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ file = setmntent(mtab_path, "r");
+ Py_END_ALLOW_THREADS
+ if ((file == 0) || (file == NULL)) {
+ psutil_debug("setmntent() failed");
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path);
+ goto error;
+ }
+
+ while ((entry = getmntent(file))) {
+ if (entry == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
+ goto error;
+ }
+ py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue("(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ entry->mnt_type, // fs type
+ entry->mnt_opts); // options
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_dev);
+ Py_CLEAR(py_mountp);
+ Py_CLEAR(py_tuple);
+ }
+ endmntent(file);
+ return py_retlist;
+
+error:
+ if (file != NULL)
+ endmntent(file);
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/disk.h b/contrib/python/psutil/py3/psutil/arch/linux/disk.h
new file mode 100644
index 0000000000..90a86d611b
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/disk.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_disk_partitions(PyObject* self, PyObject* args);
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/mem.c b/contrib/python/psutil/py3/psutil/arch/linux/mem.c
new file mode 100644
index 0000000000..3b9b4fef3f
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/mem.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <sys/sysinfo.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_linux_sysinfo(PyObject *self, PyObject *args) {
+ struct sysinfo info;
+
+ if (sysinfo(&info) != 0)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ // note: boot time might also be determined from here
+ return Py_BuildValue(
+ "(kkkkkkI)",
+ info.totalram, // total
+ info.freeram, // free
+ info.bufferram, // buffer
+ info.sharedram, // shared
+ info.totalswap, // swap tot
+ info.freeswap, // swap free
+ info.mem_unit // multiplier
+ );
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/mem.h b/contrib/python/psutil/py3/psutil/arch/linux/mem.h
new file mode 100644
index 0000000000..582d3e0314
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/mem.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_linux_sysinfo(PyObject* self, PyObject* args);
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/net.c b/contrib/python/psutil/py3/psutil/arch/linux/net.c
new file mode 100644
index 0000000000..d193e94087
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/net.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <linux/sockios.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/version.h>
+#include <unistd.h>
+
+// see: https://github.com/giampaolo/psutil/issues/659
+#ifdef PSUTIL_ETHTOOL_MISSING_TYPES
+ #include <linux/types.h>
+ typedef __u64 u64;
+ typedef __u32 u32;
+ typedef __u16 u16;
+ typedef __u8 u8;
+#endif
+
+// Avoid redefinition of struct sysinfo with musl libc.
+#define _LINUX_SYSINFO_H
+#include <linux/ethtool.h>
+
+#include "../../_psutil_common.h"
+
+
+// * defined in linux/ethtool.h but not always available (e.g. Android)
+// * #ifdef check needed for old kernels, see:
+// https://github.com/giampaolo/psutil/issues/2164
+static inline uint32_t
+psutil_ethtool_cmd_speed(const struct ethtool_cmd *ecmd) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
+ return ecmd->speed;
+#else
+ return (ecmd->speed_hi << 16) | ecmd->speed;
+#endif
+}
+
+// May happen on old RedHat versions, see:
+// https://github.com/giampaolo/psutil/issues/607
+#ifndef DUPLEX_UNKNOWN
+ #define DUPLEX_UNKNOWN 0xff
+#endif
+// https://github.com/giampaolo/psutil/pull/2156
+#ifndef SPEED_UNKNOWN
+ #define SPEED_UNKNOWN -1
+#endif
+
+
+// References:
+// * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
+// * http://www.i-scream.org/libstatgrab/
+PyObject*
+psutil_net_if_duplex_speed(PyObject* self, PyObject* args) {
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ int duplex;
+ __u32 uint_speed;
+ int speed;
+ struct ifreq ifr;
+ struct ethtool_cmd ethcmd;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return PyErr_SetFromOSErrnoWithSyscall("socket()");
+ PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+
+ // duplex and speed
+ memset(&ethcmd, 0, sizeof ethcmd);
+ ethcmd.cmd = ETHTOOL_GSET;
+ ifr.ifr_data = (void *)&ethcmd;
+ ret = ioctl(sock, SIOCETHTOOL, &ifr);
+
+ if (ret != -1) {
+ duplex = ethcmd.duplex;
+ // speed is returned from ethtool as a __u32 ranging from 0 to INT_MAX
+ // or SPEED_UNKNOWN (-1)
+ uint_speed = psutil_ethtool_cmd_speed(&ethcmd);
+ if (uint_speed == (__u32)SPEED_UNKNOWN || uint_speed > INT_MAX) {
+ speed = 0;
+ }
+ else {
+ speed = (int)uint_speed;
+ }
+ }
+ else {
+ if ((errno == EOPNOTSUPP) || (errno == EINVAL)) {
+ // EOPNOTSUPP may occur in case of wi-fi cards.
+ // For EINVAL see:
+ // https://github.com/giampaolo/psutil/issues/797
+ // #issuecomment-202999532
+ duplex = DUPLEX_UNKNOWN;
+ speed = 0;
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)");
+ goto error;
+ }
+ }
+
+ py_retlist = Py_BuildValue("[ii]", duplex, speed);
+ if (!py_retlist)
+ goto error;
+ close(sock);
+ return py_retlist;
+
+error:
+ if (sock != -1)
+ close(sock);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/net.h b/contrib/python/psutil/py3/psutil/arch/linux/net.h
new file mode 100644
index 0000000000..55095c06c9
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/net.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_net_if_duplex_speed(PyObject* self, PyObject* args);
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/proc.c b/contrib/python/psutil/py3/psutil/arch/linux/proc.c
new file mode 100644
index 0000000000..b58a3ce2a2
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/proc.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <sys/syscall.h>
+#include <sched.h>
+#include <unistd.h>
+
+#include "proc.h"
+#include "../../_psutil_common.h"
+
+
+#ifdef PSUTIL_HAVE_IOPRIO
+enum {
+ IOPRIO_WHO_PROCESS = 1,
+};
+
+static inline int
+ioprio_get(int which, int who) {
+ return syscall(__NR_ioprio_get, which, who);
+}
+
+static inline int
+ioprio_set(int which, int who, int ioprio) {
+ return syscall(__NR_ioprio_set, which, who, ioprio);
+}
+
+#define IOPRIO_CLASS_SHIFT 13
+#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
+
+#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
+#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
+#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
+
+
+// Return a (ioclass, iodata) Python tuple representing process I/O
+// priority.
+PyObject *
+psutil_proc_ioprio_get(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int ioprio, ioclass, iodata;
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
+ if (ioprio == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ ioclass = IOPRIO_PRIO_CLASS(ioprio);
+ iodata = IOPRIO_PRIO_DATA(ioprio);
+ return Py_BuildValue("ii", ioclass, iodata);
+}
+
+
+// A wrapper around ioprio_set(); sets process I/O priority. ioclass
+// can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE or
+// 0. iodata goes from 0 to 7 depending on ioclass specified.
+PyObject *
+psutil_proc_ioprio_set(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int ioprio, ioclass, iodata;
+ int retval;
+
+ if (! PyArg_ParseTuple(
+ args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) {
+ return NULL;
+ }
+ ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
+ retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
+ if (retval == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ Py_RETURN_NONE;
+}
+#endif // PSUTIL_HAVE_IOPRIO
+
+
+#ifdef PSUTIL_HAVE_CPU_AFFINITY
+
+// Return process CPU affinity as a Python list.
+PyObject *
+psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
+ int cpu, ncpus, count, cpucount_s;
+ pid_t pid;
+ size_t setsize;
+ cpu_set_t *mask = NULL;
+ PyObject *py_list = NULL;
+
+ if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ // The minimum number of CPUs allocated in a cpu_set_t.
+ ncpus = sizeof(unsigned long) * CHAR_BIT;
+ while (1) {
+ setsize = CPU_ALLOC_SIZE(ncpus);
+ mask = CPU_ALLOC(ncpus);
+ if (mask == NULL) {
+ psutil_debug("CPU_ALLOC() failed");
+ return PyErr_NoMemory();
+ }
+ if (sched_getaffinity(pid, setsize, mask) == 0)
+ break;
+ CPU_FREE(mask);
+ if (errno != EINVAL)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ if (ncpus > INT_MAX / 2) {
+ PyErr_SetString(PyExc_OverflowError, "could not allocate "
+ "a large enough CPU set");
+ return NULL;
+ }
+ ncpus = ncpus * 2;
+ }
+
+ py_list = PyList_New(0);
+ if (py_list == NULL)
+ goto error;
+
+ cpucount_s = CPU_COUNT_S(setsize, mask);
+ for (cpu = 0, count = cpucount_s; count; cpu++) {
+ if (CPU_ISSET_S(cpu, setsize, mask)) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *cpu_num = PyLong_FromLong(cpu);
+#else
+ PyObject *cpu_num = PyInt_FromLong(cpu);
+#endif
+ if (cpu_num == NULL)
+ goto error;
+ if (PyList_Append(py_list, cpu_num)) {
+ Py_DECREF(cpu_num);
+ goto error;
+ }
+ Py_DECREF(cpu_num);
+ --count;
+ }
+ }
+ CPU_FREE(mask);
+ return py_list;
+
+error:
+ if (mask)
+ CPU_FREE(mask);
+ Py_XDECREF(py_list);
+ return NULL;
+}
+
+
+// Set process CPU affinity; expects a bitmask.
+PyObject *
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
+ cpu_set_t cpu_set;
+ size_t len;
+ pid_t pid;
+ Py_ssize_t i, seq_len;
+ PyObject *py_cpu_set;
+
+ if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set))
+ return NULL;
+
+ if (!PySequence_Check(py_cpu_set)) {
+ return PyErr_Format(
+ PyExc_TypeError,
+#if PY_MAJOR_VERSION >= 3
+ "sequence argument expected, got %R", Py_TYPE(py_cpu_set)
+#else
+ "sequence argument expected, got %s", Py_TYPE(py_cpu_set)->tp_name
+#endif
+ );
+ }
+
+ seq_len = PySequence_Size(py_cpu_set);
+ if (seq_len < 0) {
+ return NULL;
+ }
+ CPU_ZERO(&cpu_set);
+ for (i = 0; i < seq_len; i++) {
+ PyObject *item = PySequence_GetItem(py_cpu_set, i);
+ if (!item) {
+ return NULL;
+ }
+#if PY_MAJOR_VERSION >= 3
+ long value = PyLong_AsLong(item);
+#else
+ long value = PyInt_AsLong(item);
+#endif
+ Py_XDECREF(item);
+ if ((value == -1) || PyErr_Occurred()) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_ValueError, "invalid CPU value");
+ return NULL;
+ }
+ CPU_SET(value, &cpu_set);
+ }
+
+ len = sizeof(cpu_set);
+ if (sched_setaffinity(pid, len, &cpu_set)) {
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ Py_RETURN_NONE;
+}
+#endif // PSUTIL_HAVE_CPU_AFFINITY
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/proc.h b/contrib/python/psutil/py3/psutil/arch/linux/proc.h
new file mode 100644
index 0000000000..94a84c62ec
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/proc.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <sys/syscall.h> // __NR_*
+#include <sched.h> // CPU_ALLOC
+
+// Linux >= 2.6.13
+#if defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
+ #define PSUTIL_HAVE_IOPRIO
+
+ PyObject *psutil_proc_ioprio_get(PyObject *self, PyObject *args);
+ PyObject *psutil_proc_ioprio_set(PyObject *self, PyObject *args);
+#endif
+
+// Should exist starting from CentOS 6 (year 2011).
+#ifdef CPU_ALLOC
+ #define PSUTIL_HAVE_CPU_AFFINITY
+
+ PyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args);
+ PyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args);
+#endif
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/users.c b/contrib/python/psutil/py3/psutil/arch/linux/users.c
new file mode 100644
index 0000000000..546e29ee98
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/users.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <utmp.h>
+#include <string.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ struct utmp *ut;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_username = NULL;
+ PyObject *py_tty = NULL;
+ PyObject *py_hostname = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ setutent();
+ while (NULL != (ut = getutent())) {
+ if (ut->ut_type != USER_PROCESS)
+ continue;
+ py_tuple = NULL;
+ py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
+ if (! py_username)
+ goto error;
+ py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
+ if (! py_tty)
+ goto error;
+ if (strcmp(ut->ut_host, ":0") || strcmp(ut->ut_host, ":0.0"))
+ py_hostname = PyUnicode_DecodeFSDefault("localhost");
+ else
+ py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
+ if (! py_hostname)
+ goto error;
+
+ py_tuple = Py_BuildValue(
+ "OOOd" _Py_PARSE_PID,
+ py_username, // username
+ py_tty, // tty
+ py_hostname, // hostname
+ (double)ut->ut_tv.tv_sec, // tstamp
+ ut->ut_pid // process id
+ );
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_username);
+ Py_CLEAR(py_tty);
+ Py_CLEAR(py_hostname);
+ Py_CLEAR(py_tuple);
+ }
+ endutent();
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tty);
+ Py_XDECREF(py_hostname);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ endutent();
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/linux/users.h b/contrib/python/psutil/py3/psutil/arch/linux/users.h
new file mode 100644
index 0000000000..ba2735d1d2
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/linux/users.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_users(PyObject* self, PyObject* args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/cpu.c b/contrib/python/psutil/py3/psutil/arch/osx/cpu.c
new file mode 100644
index 0000000000..3759dda44e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/cpu.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+System-wide CPU related functions.
+
+Original code was refactored and moved from psutil/_psutil_osx.c in 2020
+right before a4c0a0eb0d2a872ab7a45e47fcf37ef1fde5b012.
+For reference, here's the git history with original implementations:
+
+- CPU count logical: 3d291d425b856077e65163e43244050fb188def1
+- CPU count physical: 4263e354bb4984334bc44adf5dd2f32013d69fba
+- CPU times: 32488bdf54aed0f8cef90d639c1667ffaa3c31c7
+- CPU stat: fa00dfb961ef63426c7818899340866ced8d2418
+- CPU frequency: 6ba1ac4ebfcd8c95fca324b15606ab0ec1412d39
+*/
+
+#include <Python.h>
+#include <mach/mach_error.h>
+#include <mach/mach_host.h>
+#include <mach/mach_port.h>
+#include <mach/mach_vm.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+#include <mach/mach.h>
+#if defined(__arm64__) || defined(__aarch64__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#endif
+
+#if (defined __MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 120000)
+#define kIOMainPortDefault kIOMasterPortDefault
+#endif
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+
+PyObject *
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
+ int num;
+ size_t size = sizeof(int);
+
+ if (sysctlbyname("hw.logicalcpu", &num, &size, NULL, 2))
+ Py_RETURN_NONE; // mimic os.cpu_count()
+ else
+ return Py_BuildValue("i", num);
+}
+
+
+PyObject *
+psutil_cpu_count_cores(PyObject *self, PyObject *args) {
+ int num;
+ size_t size = sizeof(int);
+
+ if (sysctlbyname("hw.physicalcpu", &num, &size, NULL, 0))
+ Py_RETURN_NONE; // mimic os.cpu_count()
+ else
+ return Py_BuildValue("i", num);
+}
+
+
+PyObject *
+psutil_cpu_times(PyObject *self, PyObject *args) {
+ mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+ kern_return_t error;
+ host_cpu_load_info_data_t r_load;
+
+ mach_port_t host_port = mach_host_self();
+ error = host_statistics(host_port, HOST_CPU_LOAD_INFO,
+ (host_info_t)&r_load, &count);
+ if (error != KERN_SUCCESS) {
+ return PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: %s",
+ mach_error_string(error));
+ }
+ mach_port_deallocate(mach_task_self(), host_port);
+
+ return Py_BuildValue(
+ "(dddd)",
+ (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK,
+ (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
+ (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
+ (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
+ );
+}
+
+
+PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+ struct vmmeter vmstat;
+ kern_return_t ret;
+ mach_msg_type_number_t count = sizeof(vmstat) / sizeof(integer_t);
+ mach_port_t mport = mach_host_self();
+
+ ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);
+ if (ret != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_VM_INFO) failed: %s",
+ mach_error_string(ret));
+ return NULL;
+ }
+ mach_port_deallocate(mach_task_self(), mport);
+
+ return Py_BuildValue(
+ "IIIII",
+ vmstat.v_swtch, // ctx switches
+ vmstat.v_intr, // interrupts
+ vmstat.v_soft, // software interrupts
+ vmstat.v_syscall, // syscalls
+ vmstat.v_trap // traps
+ );
+}
+
+#if defined(__arm64__) || defined(__aarch64__)
+PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ uint32_t min;
+ uint32_t curr;
+ uint32_t pMin;
+ uint32_t eMin;
+ uint32_t max;
+ kern_return_t status;
+ CFDictionaryRef matching = NULL;
+ CFTypeRef pCoreRef = NULL;
+ CFTypeRef eCoreRef = NULL;
+ io_iterator_t iter = 0;
+ io_registry_entry_t entry = 0;
+ io_name_t name;
+
+ matching = IOServiceMatching("AppleARMIODevice");
+ if (matching == 0) {
+ return PyErr_Format(
+ PyExc_RuntimeError,
+ "IOServiceMatching call failed, 'AppleARMIODevice' not found"
+ );
+ }
+
+ status = IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter);
+ if (status != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError, "IOServiceGetMatchingServices call failed"
+ );
+ goto error;
+ }
+
+ while ((entry = IOIteratorNext(iter)) != 0) {
+ status = IORegistryEntryGetName(entry, name);
+ if (status != KERN_SUCCESS) {
+ IOObjectRelease(entry);
+ continue;
+ }
+ if (strcmp(name, "pmgr") == 0) {
+ break;
+ }
+ IOObjectRelease(entry);
+ }
+
+ if (entry == 0) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "'pmgr' entry was not found in AppleARMIODevice service"
+ );
+ goto error;
+ }
+
+ pCoreRef = IORegistryEntryCreateCFProperty(
+ entry, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0);
+ if (pCoreRef == NULL) {
+ PyErr_Format(
+ PyExc_RuntimeError, "'voltage-states5-sram' property not found");
+ goto error;
+ }
+
+ eCoreRef = IORegistryEntryCreateCFProperty(
+ entry, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0);
+ if (eCoreRef == NULL) {
+ PyErr_Format(
+ PyExc_RuntimeError, "'voltage-states1-sram' property not found");
+ goto error;
+ }
+
+ size_t pCoreLength = CFDataGetLength(pCoreRef);
+ size_t eCoreLength = CFDataGetLength(eCoreRef);
+ if (pCoreLength < 8) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "expected 'voltage-states5-sram' buffer to have at least size 8"
+ );
+ goto error;
+ }
+ if (eCoreLength < 4) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "expected 'voltage-states1-sram' buffer to have at least size 4"
+ );
+ goto error;
+ }
+
+ CFDataGetBytes(pCoreRef, CFRangeMake(0, 4), (UInt8 *) &pMin);
+ CFDataGetBytes(eCoreRef, CFRangeMake(0, 4), (UInt8 *) &eMin);
+ CFDataGetBytes(pCoreRef, CFRangeMake(pCoreLength - 8, 4), (UInt8 *) &max);
+
+ min = pMin < eMin ? pMin : eMin;
+ curr = max;
+
+ CFRelease(pCoreRef);
+ CFRelease(eCoreRef);
+ IOObjectRelease(iter);
+ IOObjectRelease(entry);
+
+ return Py_BuildValue(
+ "IKK",
+ curr / 1000 / 1000,
+ min / 1000 / 1000,
+ max / 1000 / 1000
+ );
+
+error:
+ if (pCoreRef != NULL)
+ CFRelease(pCoreRef);
+ if (eCoreRef != NULL)
+ CFRelease(eCoreRef);
+ if (iter != 0)
+ IOObjectRelease(iter);
+ if (entry != 0)
+ IOObjectRelease(entry);
+ return NULL;
+}
+#else
+PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ unsigned int curr;
+ int64_t min = 0;
+ int64_t max = 0;
+ int mib[2];
+ size_t len = sizeof(curr);
+ size_t size = sizeof(min);
+
+ // also available as "hw.cpufrequency" but it's deprecated
+ mib[0] = CTL_HW;
+ mib[1] = HW_CPU_FREQ;
+
+ if (sysctl(mib, 2, &curr, &len, NULL, 0) < 0)
+ return PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_CPU_FREQ)");
+
+ if (sysctlbyname("hw.cpufrequency_min", &min, &size, NULL, 0))
+ psutil_debug("sysctl('hw.cpufrequency_min') failed (set to 0)");
+
+ if (sysctlbyname("hw.cpufrequency_max", &max, &size, NULL, 0))
+ psutil_debug("sysctl('hw.cpufrequency_min') failed (set to 0)");
+
+ return Py_BuildValue(
+ "IKK",
+ curr / 1000 / 1000,
+ min / 1000 / 1000,
+ max / 1000 / 1000);
+}
+#endif
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ natural_t cpu_count;
+ natural_t i;
+ processor_info_array_t info_array;
+ mach_msg_type_number_t info_count;
+ kern_return_t error;
+ processor_cpu_load_info_data_t *cpu_load_info = NULL;
+ int ret;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ mach_port_t host_port = mach_host_self();
+ error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
+ &cpu_count, &info_array, &info_count);
+ if (error != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
+ mach_error_string(error));
+ goto error;
+ }
+ mach_port_deallocate(mach_task_self(), host_port);
+
+ cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
+
+ for (i = 0; i < cpu_count; i++) {
+ py_cputime = Py_BuildValue(
+ "(dddd)",
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
+ );
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_CLEAR(py_cputime);
+ }
+
+ ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
+ info_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ if (cpu_load_info != NULL) {
+ ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
+ info_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+ }
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/cpu.h b/contrib/python/psutil/py3/psutil/arch/osx/cpu.h
new file mode 100644
index 0000000000..6cf92f82b3
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/cpu.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
+PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/disk.c b/contrib/python/psutil/py3/psutil/arch/osx/disk.c
new file mode 100644
index 0000000000..961fc42a48
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/disk.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Disk related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/IOBSD.h>
+
+#include "../../_psutil_common.h"
+
+
+/*
+ * Return a list of tuples including device, mount point and fs type
+ * for all partitions mounted on the system.
+ */
+PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ int num;
+ int i;
+ int len;
+ uint64_t flags;
+ char opts[400];
+ struct statfs *fs = NULL;
+ PyObject *py_dev = NULL;
+ PyObject *py_mountp = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ // get the number of mount points
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(NULL, 0, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ len = sizeof(*fs) * num;
+ fs = malloc(len);
+ if (fs == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(fs, len, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < num; i++) {
+ opts[0] = 0;
+ flags = fs[i].f_flags;
+
+ // see sys/mount.h
+ if (flags & MNT_RDONLY)
+ strlcat(opts, "ro", sizeof(opts));
+ else
+ strlcat(opts, "rw", sizeof(opts));
+ if (flags & MNT_SYNCHRONOUS)
+ strlcat(opts, ",sync", sizeof(opts));
+ if (flags & MNT_NOEXEC)
+ strlcat(opts, ",noexec", sizeof(opts));
+ if (flags & MNT_NOSUID)
+ strlcat(opts, ",nosuid", sizeof(opts));
+ if (flags & MNT_UNION)
+ strlcat(opts, ",union", sizeof(opts));
+ if (flags & MNT_ASYNC)
+ strlcat(opts, ",async", sizeof(opts));
+ if (flags & MNT_EXPORTED)
+ strlcat(opts, ",exported", sizeof(opts));
+ if (flags & MNT_QUARANTINE)
+ strlcat(opts, ",quarantine", sizeof(opts));
+ if (flags & MNT_LOCAL)
+ strlcat(opts, ",local", sizeof(opts));
+ if (flags & MNT_QUOTA)
+ strlcat(opts, ",quota", sizeof(opts));
+ if (flags & MNT_ROOTFS)
+ strlcat(opts, ",rootfs", sizeof(opts));
+ if (flags & MNT_DOVOLFS)
+ strlcat(opts, ",dovolfs", sizeof(opts));
+ if (flags & MNT_DONTBROWSE)
+ strlcat(opts, ",dontbrowse", sizeof(opts));
+ if (flags & MNT_IGNORE_OWNERSHIP)
+ strlcat(opts, ",ignore-ownership", sizeof(opts));
+ if (flags & MNT_AUTOMOUNTED)
+ strlcat(opts, ",automounted", sizeof(opts));
+ if (flags & MNT_JOURNALED)
+ strlcat(opts, ",journaled", sizeof(opts));
+ if (flags & MNT_NOUSERXATTR)
+ strlcat(opts, ",nouserxattr", sizeof(opts));
+ if (flags & MNT_DEFWRITE)
+ strlcat(opts, ",defwrite", sizeof(opts));
+ if (flags & MNT_MULTILABEL)
+ strlcat(opts, ",multilabel", sizeof(opts));
+ if (flags & MNT_NOATIME)
+ strlcat(opts, ",noatime", sizeof(opts));
+ if (flags & MNT_UPDATE)
+ strlcat(opts, ",update", sizeof(opts));
+ if (flags & MNT_RELOAD)
+ strlcat(opts, ",reload", sizeof(opts));
+ if (flags & MNT_FORCE)
+ strlcat(opts, ",force", sizeof(opts));
+ if (flags & MNT_CMDFLAGS)
+ strlcat(opts, ",cmdflags", sizeof(opts));
+
+ py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ fs[i].f_fstypename, // fs type
+ opts); // options
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_dev);
+ Py_CLEAR(py_mountp);
+ Py_CLEAR(py_tuple);
+ }
+
+ free(fs);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (fs != NULL)
+ free(fs);
+ return NULL;
+}
+
+
+PyObject *
+psutil_disk_usage_used(PyObject *self, PyObject *args) {
+ PyObject *py_default_value;
+ PyObject *py_mount_point_bytes = NULL;
+ char* mount_point;
+
+#if PY_MAJOR_VERSION >= 3
+ if (!PyArg_ParseTuple(args, "O&O", PyUnicode_FSConverter, &py_mount_point_bytes, &py_default_value)) {
+ return NULL;
+ }
+ mount_point = PyBytes_AsString(py_mount_point_bytes);
+ if (NULL == mount_point) {
+ Py_XDECREF(py_mount_point_bytes);
+ return NULL;
+ }
+#else
+ if (!PyArg_ParseTuple(args, "sO", &mount_point, &py_default_value)) {
+ return NULL;
+ }
+#endif
+
+#ifdef ATTR_VOL_SPACEUSED
+ /* Call getattrlist(ATTR_VOL_SPACEUSED) to get used space info. */
+ int ret;
+ struct {
+ uint32_t size;
+ uint64_t spaceused;
+ } __attribute__((aligned(4), packed)) attrbuf = {0};
+ struct attrlist attrs = {0};
+
+ attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrs.volattr = ATTR_VOL_INFO | ATTR_VOL_SPACEUSED;
+ Py_BEGIN_ALLOW_THREADS
+ ret = getattrlist(mount_point, &attrs, &attrbuf, sizeof(attrbuf), 0);
+ Py_END_ALLOW_THREADS
+ if (ret == 0) {
+ Py_XDECREF(py_mount_point_bytes);
+ return PyLong_FromUnsignedLongLong(attrbuf.spaceused);
+ }
+ psutil_debug("getattrlist(ATTR_VOL_SPACEUSED) failed, fall-back to default value");
+#endif
+ Py_XDECREF(py_mount_point_bytes);
+ Py_INCREF(py_default_value);
+ return py_default_value;
+}
+
+
+/*
+ * Return a Python dict of tuples for disk I/O information
+ */
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ CFDictionaryRef parent_dict;
+ CFDictionaryRef props_dict;
+ CFDictionaryRef stats_dict;
+ io_registry_entry_t parent;
+ io_registry_entry_t disk;
+ io_iterator_t disk_list;
+ PyObject *py_disk_info = NULL;
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ // Get list of disks
+ if (IOServiceGetMatchingServices(kIOMasterPortDefault,
+ IOServiceMatching(kIOMediaClass),
+ &disk_list) != kIOReturnSuccess) {
+ PyErr_SetString(
+ PyExc_RuntimeError, "unable to get the list of disks.");
+ goto error;
+ }
+
+ // Iterate over disks
+ while ((disk = IOIteratorNext(disk_list)) != 0) {
+ py_disk_info = NULL;
+ parent_dict = NULL;
+ props_dict = NULL;
+ stats_dict = NULL;
+
+ if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent)
+ != kIOReturnSuccess) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the disk's parent.");
+ IOObjectRelease(disk);
+ goto error;
+ }
+
+ if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
+ if (IORegistryEntryCreateCFProperties(
+ disk,
+ (CFMutableDictionaryRef *) &parent_dict,
+ kCFAllocatorDefault,
+ kNilOptions
+ ) != kIOReturnSuccess)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the parent's properties.");
+ IOObjectRelease(disk);
+ IOObjectRelease(parent);
+ goto error;
+ }
+
+ if (IORegistryEntryCreateCFProperties(
+ parent,
+ (CFMutableDictionaryRef *) &props_dict,
+ kCFAllocatorDefault,
+ kNilOptions
+ ) != kIOReturnSuccess)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the disk properties.");
+ CFRelease(props_dict);
+ IOObjectRelease(disk);
+ IOObjectRelease(parent);
+ goto error;
+ }
+
+ const int kMaxDiskNameSize = 64;
+ CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue(
+ parent_dict, CFSTR(kIOBSDNameKey));
+ char disk_name[kMaxDiskNameSize];
+
+ CFStringGetCString(disk_name_ref,
+ disk_name,
+ kMaxDiskNameSize,
+ CFStringGetSystemEncoding());
+
+ stats_dict = (CFDictionaryRef)CFDictionaryGetValue(
+ props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
+
+ if (stats_dict == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unable to get disk stats.");
+ goto error;
+ }
+
+ CFNumberRef number;
+ int64_t reads = 0;
+ int64_t writes = 0;
+ int64_t read_bytes = 0;
+ int64_t write_bytes = 0;
+ int64_t read_time = 0;
+ int64_t write_time = 0;
+
+ // Get disk reads/writes
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsReadsKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &reads);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsWritesKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &writes);
+ }
+
+ // Get disk bytes read/written
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes);
+ }
+
+ // Get disk time spent reading/writing (nanoseconds)
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &read_time);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &write_time);
+ }
+
+ // Read/Write time on macOS comes back in nanoseconds and in psutil
+ // we've standardized on milliseconds so do the conversion.
+ py_disk_info = Py_BuildValue(
+ "(KKKKKK)",
+ reads,
+ writes,
+ read_bytes,
+ write_bytes,
+ read_time / 1000 / 1000,
+ write_time / 1000 / 1000);
+ if (!py_disk_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
+ goto error;
+ Py_CLEAR(py_disk_info);
+
+ CFRelease(parent_dict);
+ IOObjectRelease(parent);
+ CFRelease(props_dict);
+ IOObjectRelease(disk);
+ }
+ }
+
+ IOObjectRelease (disk_list);
+
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/disk.h b/contrib/python/psutil/py3/psutil/arch/osx/disk.h
new file mode 100644
index 0000000000..88ca9a28b1
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/disk.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
+PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
+PyObject *psutil_disk_usage_used(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/mem.c b/contrib/python/psutil/py3/psutil/arch/osx/mem.c
new file mode 100644
index 0000000000..53493065c2
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/mem.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// System memory related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <mach/host_info.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+
+#include "../../_psutil_posix.h"
+
+
+static int
+psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
+ kern_return_t ret;
+ mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
+ mach_port_t mport = mach_host_self();
+
+ ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
+ if (ret != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_VM_INFO) syscall failed: %s",
+ mach_error_string(ret));
+ return 0;
+ }
+ mach_port_deallocate(mach_task_self(), mport);
+ return 1;
+}
+
+
+/*
+ * Return system virtual memory stats.
+ * See:
+ * https://opensource.apple.com/source/system_cmds/system_cmds-790/
+ * vm_stat.tproj/vm_stat.c.auto.html
+ */
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ int mib[2];
+ uint64_t total;
+ size_t len = sizeof(total);
+ vm_statistics_data_t vm;
+ long pagesize = psutil_getpagesize();
+ // physical mem
+ mib[0] = CTL_HW;
+ mib[1] = HW_MEMSIZE;
+
+ // This is also available as sysctlbyname("hw.memsize").
+ if (sysctl(mib, 2, &total, &len, NULL, 0)) {
+ if (errno != 0)
+ PyErr_SetFromErrno(PyExc_OSError);
+ else
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
+ return NULL;
+ }
+
+ // vm
+ if (!psutil_sys_vminfo(&vm))
+ return NULL;
+
+ return Py_BuildValue(
+ "KKKKKK",
+ total,
+ (unsigned long long) vm.active_count * pagesize, // active
+ (unsigned long long) vm.inactive_count * pagesize, // inactive
+ (unsigned long long) vm.wire_count * pagesize, // wired
+ (unsigned long long) vm.free_count * pagesize, // free
+ (unsigned long long) vm.speculative_count * pagesize // speculative
+ );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ int mib[2];
+ size_t size;
+ struct xsw_usage totals;
+ vm_statistics_data_t vmstat;
+ long pagesize = psutil_getpagesize();
+
+ mib[0] = CTL_VM;
+ mib[1] = VM_SWAPUSAGE;
+ size = sizeof(totals);
+ if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
+ if (errno != 0)
+ PyErr_SetFromErrno(PyExc_OSError);
+ else
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
+ return NULL;
+ }
+ if (!psutil_sys_vminfo(&vmstat))
+ return NULL;
+
+ return Py_BuildValue(
+ "LLLKK",
+ totals.xsu_total,
+ totals.xsu_used,
+ totals.xsu_avail,
+ (unsigned long long)vmstat.pageins * pagesize,
+ (unsigned long long)vmstat.pageouts * pagesize);
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/mem.h b/contrib/python/psutil/py3/psutil/arch/osx/mem.h
new file mode 100644
index 0000000000..dc4cd74388
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/mem.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/net.c b/contrib/python/psutil/py3/psutil/arch/osx/net.c
new file mode 100644
index 0000000000..e9cc61e9b1
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/net.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Networks related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ char *buf = NULL, *lim, *next;
+ struct if_msghdr *ifm;
+ int mib[6];
+ mib[0] = CTL_NET; // networking subsystem
+ mib[1] = PF_ROUTE; // type of information
+ mib[2] = 0; // protocol (IPPROTO_xxx)
+ mib[3] = 0; // address family
+ mib[4] = NET_RT_IFLIST2; // operation
+ mib[5] = 0;
+ size_t len;
+ PyObject *py_ifc_info = NULL;
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ lim = buf + len;
+
+ for (next = buf; next < lim; ) {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+
+ if (ifm->ifm_type == RTM_IFINFO2) {
+ py_ifc_info = NULL;
+ struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
+ char ifc_name[32];
+
+ strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
+ ifc_name[sdl->sdl_nlen] = 0;
+
+ py_ifc_info = Py_BuildValue(
+ "(KKKKKKKi)",
+ if2m->ifm_data.ifi_obytes,
+ if2m->ifm_data.ifi_ibytes,
+ if2m->ifm_data.ifi_opackets,
+ if2m->ifm_data.ifi_ipackets,
+ if2m->ifm_data.ifi_ierrors,
+ if2m->ifm_data.ifi_oerrors,
+ if2m->ifm_data.ifi_iqdrops,
+ 0); // dropout not supported
+
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
+ goto error;
+ Py_CLEAR(py_ifc_info);
+ }
+ else {
+ continue;
+ }
+ }
+
+ free(buf);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_ifc_info);
+ Py_DECREF(py_retdict);
+ if (buf != NULL)
+ free(buf);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/net.h b/contrib/python/psutil/py3/psutil/arch/osx/net.h
new file mode 100644
index 0000000000..99079523c1
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/net.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_net_io_counters(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/proc.c b/contrib/python/psutil/py3/psutil/arch/osx/proc.c
new file mode 100644
index 0000000000..6f66c8613f
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/proc.c
@@ -0,0 +1,1267 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Process related functions. Original code was moved in here from
+// psutil/_psutil_osx.c and psutil/arc/osx/process_info.c in 2023.
+// For reference, here's the GIT blame history before the move:
+// https://github.com/giampaolo/psutil/blame/59504a5/psutil/_psutil_osx.c
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/arch/osx/process_info.c
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysctl.h>
+#include <libproc.h>
+#include <sys/proc_info.h>
+#include <sys/sysctl.h>
+#include <netinet/tcp_fsm.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <mach-o/loader.h>
+
+#include "../../_psutil_common.h"
+#include "../../_psutil_posix.h"
+
+
+#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+typedef struct kinfo_proc kinfo_proc;
+
+
+// ====================================================================
+// --- utils
+// ====================================================================
+
+/*
+ * Returns a list of all BSD processes on the system. This routine
+ * allocates the list and puts it in *procList and a count of the
+ * number of entries in *procCount. You are responsible for freeing
+ * this list (use "free" from System framework).
+ * On success, the function returns 0.
+ * On error, the function returns a BSD errno value.
+ */
+static int
+psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
+ int mib[3];
+ size_t size, size2;
+ void *ptr;
+ int err;
+ int lim = 8; // some limit
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_ALL;
+ *procCount = 0;
+
+ /*
+ * We start by calling sysctl with ptr == NULL and size == 0.
+ * That will succeed, and set size to the appropriate length.
+ * We then allocate a buffer of at least that size and call
+ * sysctl with that buffer. If that succeeds, we're done.
+ * If that call fails with ENOMEM, we throw the buffer away
+ * and try again.
+ * Note that the loop calls sysctl with NULL again. This is
+ * is necessary because the ENOMEM failure case sets size to
+ * the amount of data returned, not the amount of data that
+ * could have been returned.
+ */
+ while (lim-- > 0) {
+ size = 0;
+ if (sysctl((int *)mib, 3, NULL, &size, NULL, 0) == -1) {
+ PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
+ return 1;
+ }
+ size2 = size + (size >> 3); // add some
+ if (size2 > size) {
+ ptr = malloc(size2);
+ if (ptr == NULL)
+ ptr = malloc(size);
+ else
+ size = size2;
+ }
+ else {
+ ptr = malloc(size);
+ }
+ if (ptr == NULL) {
+ PyErr_NoMemory();
+ return 1;
+ }
+
+ if (sysctl((int *)mib, 3, ptr, &size, NULL, 0) == -1) {
+ err = errno;
+ free(ptr);
+ if (err != ENOMEM) {
+ PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
+ return 1;
+ }
+ }
+ else {
+ *procList = (kinfo_proc *)ptr;
+ *procCount = size / sizeof(kinfo_proc);
+ if (procCount <= 0) {
+ PyErr_Format(PyExc_RuntimeError, "no PIDs found");
+ return 1;
+ }
+ return 0; // success
+ }
+ }
+
+ PyErr_Format(PyExc_RuntimeError, "couldn't collect PIDs list");
+ return 1;
+}
+
+
+// Read the maximum argument size for processes
+static int
+psutil_sysctl_argmax() {
+ int argmax;
+ int mib[2];
+ size_t size = sizeof(argmax);
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARGMAX;
+
+ if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0)
+ return argmax;
+ PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_ARGMAX)");
+ return 0;
+}
+
+
+// Read process argument space.
+static int
+psutil_sysctl_procargs(pid_t pid, char *procargs, size_t *argmax) {
+ int mib[3];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROCARGS2;
+ mib[2] = pid;
+
+ if (sysctl(mib, 3, procargs, argmax, NULL, 0) < 0) {
+ if (psutil_pid_exists(pid) == 0) {
+ NoSuchProcess("psutil_pid_exists -> 0");
+ return 1;
+ }
+ // In case of zombie process we'll get EINVAL. We translate it
+ // to NSP and _psosx.py will translate it to ZP.
+ if (errno == EINVAL) {
+ psutil_debug("sysctl(KERN_PROCARGS2) -> EINVAL translated to NSP");
+ NoSuchProcess("sysctl(KERN_PROCARGS2) -> EINVAL");
+ return 1;
+ }
+ // There's nothing we can do other than raising AD.
+ if (errno == EIO) {
+ psutil_debug("sysctl(KERN_PROCARGS2) -> EIO translated to AD");
+ AccessDenied("sysctl(KERN_PROCARGS2) -> EIO");
+ return 1;
+ }
+ PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROCARGS2)");
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) {
+ int mib[4];
+ size_t len;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+
+ // fetch the info with sysctl()
+ len = sizeof(struct kinfo_proc);
+
+ // now read the data from sysctl
+ if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {
+ // raise an exception and throw errno as the error
+ PyErr_SetFromOSErrnoWithSyscall("sysctl");
+ return -1;
+ }
+
+ // sysctl succeeds but len is zero, happens when process has gone away
+ if (len == 0) {
+ NoSuchProcess("sysctl(kinfo_proc), len == 0");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * A wrapper around proc_pidinfo().
+ * https://opensource.apple.com/source/xnu/xnu-2050.7.9/bsd/kern/proc_info.c
+ * Returns 0 on failure.
+ */
+static int
+psutil_proc_pidinfo(pid_t pid, int flavor, uint64_t arg, void *pti, int size) {
+ errno = 0;
+ int ret;
+
+ ret = proc_pidinfo(pid, flavor, arg, pti, size);
+ if (ret <= 0) {
+ psutil_raise_for_pid(pid, "proc_pidinfo()");
+ return 0;
+ }
+ if ((unsigned long)ret < sizeof(pti)) {
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo() return size < sizeof(struct_pointer)");
+ return 0;
+ }
+ return ret;
+}
+
+
+/*
+ * A wrapper around task_for_pid() which sucks big time:
+ * - it's not documented
+ * - errno is set only sometimes
+ * - sometimes errno is ENOENT (?!?)
+ * - for PIDs != getpid() or PIDs which are not members of the procmod
+ * it requires root
+ * As such we can only guess what the heck went wrong and fail either
+ * with NoSuchProcess or giveup with AccessDenied.
+ * Here's some history:
+ * https://github.com/giampaolo/psutil/issues/1181
+ * https://github.com/giampaolo/psutil/issues/1209
+ * https://github.com/giampaolo/psutil/issues/1291#issuecomment-396062519
+ */
+static int
+psutil_task_for_pid(pid_t pid, mach_port_t *task)
+{
+ // See: https://github.com/giampaolo/psutil/issues/1181
+ kern_return_t err = KERN_SUCCESS;
+
+ err = task_for_pid(mach_task_self(), pid, task);
+ if (err != KERN_SUCCESS) {
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess("task_for_pid");
+ // Now done in Python.
+ // else if (psutil_is_zombie(pid) == 1)
+ // PyErr_SetString(ZombieProcessError,
+ // "task_for_pid -> psutil_is_zombie -> 1");
+ else {
+ psutil_debug(
+ "task_for_pid() failed (pid=%ld, err=%i, errno=%i, msg='%s'); "
+ "setting AccessDenied()",
+ (long)pid, err, errno, mach_error_string(err));
+ AccessDenied("task_for_pid");
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * A wrapper around proc_pidinfo(PROC_PIDLISTFDS), which dynamically sets
+ * the buffer size.
+ */
+static struct proc_fdinfo*
+psutil_proc_list_fds(pid_t pid, int *num_fds) {
+ int ret;
+ int fds_size = 0;
+ int max_size = 24 * 1024 * 1024; // 24M
+ struct proc_fdinfo *fds_pointer = NULL;
+
+ errno = 0;
+ ret = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ if (ret <= 0) {
+ psutil_raise_for_pid(pid, "proc_pidinfo(PROC_PIDLISTFDS) 1/2");
+ goto error;
+ }
+
+ while (1) {
+ if (ret > fds_size) {
+ while (ret > fds_size) {
+ fds_size += PROC_PIDLISTFD_SIZE * 32;
+ if (fds_size > max_size) {
+ PyErr_Format(PyExc_RuntimeError,
+ "prevent malloc() to allocate > 24M");
+ goto error;
+ }
+ }
+
+ if (fds_pointer != NULL) {
+ free(fds_pointer);
+ }
+ fds_pointer = malloc(fds_size);
+
+ if (fds_pointer == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+
+ errno = 0;
+ ret = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, fds_size);
+ if (ret <= 0) {
+ psutil_raise_for_pid(pid, "proc_pidinfo(PROC_PIDLISTFDS) 2/2");
+ goto error;
+ }
+
+ if (ret + (int)PROC_PIDLISTFD_SIZE >= fds_size) {
+ psutil_debug("PROC_PIDLISTFDS: make room for 1 extra fd");
+ ret = fds_size + (int)PROC_PIDLISTFD_SIZE;
+ continue;
+ }
+
+ break;
+ }
+
+ *num_fds = (ret / (int)PROC_PIDLISTFD_SIZE);
+ return fds_pointer;
+
+error:
+ if (fds_pointer != NULL)
+ free(fds_pointer);
+ return NULL;
+}
+
+
+// ====================================================================
+// --- Python APIs
+// ====================================================================
+
+
+/*
+ * Return a Python list of all the PIDs running on the system.
+ */
+PyObject *
+psutil_pids(PyObject *self, PyObject *args) {
+ kinfo_proc *proclist = NULL;
+ kinfo_proc *orig_address = NULL;
+ size_t num_processes;
+ size_t idx;
+ PyObject *py_pid = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (psutil_get_proc_list(&proclist, &num_processes) != 0)
+ goto error;
+
+ // save the address of proclist so we can free it later
+ orig_address = proclist;
+ for (idx = 0; idx < num_processes; idx++) {
+ py_pid = PyLong_FromPid(proclist->kp_proc.p_pid);
+ if (! py_pid)
+ goto error;
+ if (PyList_Append(py_retlist, py_pid))
+ goto error;
+ Py_CLEAR(py_pid);
+ proclist++;
+ }
+ free(orig_address);
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_pid);
+ Py_DECREF(py_retlist);
+ if (orig_address != NULL)
+ free(orig_address);
+ return NULL;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using sysctl() and filling up a kinfo_proc struct.
+ * It should be possible to do this for all processes without
+ * incurring into permission (EPERM) errors.
+ * This will also succeed for zombie processes returning correct
+ * information.
+ */
+PyObject *
+psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) {
+ pid_t pid;
+ struct kinfo_proc kp;
+ PyObject *py_name;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (psutil_get_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+
+ py_name = PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
+ if (! py_name) {
+ // Likely a decoding error. We don't want to fail the whole
+ // operation. The python module may retry with proc_name().
+ PyErr_Clear();
+ py_name = Py_None;
+ }
+
+ py_retlist = Py_BuildValue(
+ _Py_PARSE_PID "llllllidiO",
+ kp.kp_eproc.e_ppid, // (pid_t) ppid
+ (long)kp.kp_eproc.e_pcred.p_ruid, // (long) real uid
+ (long)kp.kp_eproc.e_ucred.cr_uid, // (long) effective uid
+ (long)kp.kp_eproc.e_pcred.p_svuid, // (long) saved uid
+ (long)kp.kp_eproc.e_pcred.p_rgid, // (long) real gid
+ (long)kp.kp_eproc.e_ucred.cr_groups[0], // (long) effective gid
+ (long)kp.kp_eproc.e_pcred.p_svgid, // (long) saved gid
+ kp.kp_eproc.e_tdev, // (int) tty nr
+ PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime), // (double) create time
+ (int)kp.kp_proc.p_stat, // (int) status
+ py_name // (pystr) name
+ );
+
+ if (py_retlist != NULL) {
+ // XXX shall we decref() also in case of Py_BuildValue() error?
+ Py_DECREF(py_name);
+ }
+ return py_retlist;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using proc_pidinfo(PROC_PIDTASKINFO) and filling a proc_taskinfo
+ * struct.
+ * Contrarily from proc_kinfo above this function will fail with
+ * EACCES for PIDs owned by another user and with ESRCH for zombie
+ * processes.
+ */
+PyObject *
+psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) {
+ pid_t pid;
+ struct proc_taskinfo pti;
+ uint64_t total_user;
+ uint64_t total_system;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) <= 0)
+ return NULL;
+
+ total_user = pti.pti_total_user * PSUTIL_MACH_TIMEBASE_INFO.numer;
+ total_user /= PSUTIL_MACH_TIMEBASE_INFO.denom;
+ total_system = pti.pti_total_system * PSUTIL_MACH_TIMEBASE_INFO.numer;
+ total_system /= PSUTIL_MACH_TIMEBASE_INFO.denom;
+
+ return Py_BuildValue(
+ "(ddKKkkkk)",
+ (float)total_user / 1000000000.0, // (float) cpu user time
+ (float)total_system / 1000000000.0, // (float) cpu sys time
+ // Note about memory: determining other mem stats on macOS is a mess:
+ // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
+ // I just give up.
+ // struct proc_regioninfo pri;
+ // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, sizeof(pri))
+ pti.pti_resident_size, // (uns long long) rss
+ pti.pti_virtual_size, // (uns long long) vms
+ pti.pti_faults, // (uns long) number of page faults (pages)
+ pti.pti_pageins, // (uns long) number of actual pageins (pages)
+ pti.pti_threadnum, // (uns long) num threads
+ // Unvoluntary value seems not to be available;
+ // pti.pti_csw probably refers to the sum of the two;
+ // getrusage() numbers seems to confirm this theory.
+ pti.pti_csw // (uns long) voluntary ctx switches
+ );
+}
+
+
+/*
+ * Return process name from kinfo_proc as a Python string.
+ */
+PyObject *
+psutil_proc_name(PyObject *self, PyObject *args) {
+ pid_t pid;
+ struct kinfo_proc kp;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (psutil_get_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
+}
+
+
+/*
+ * Return process current working directory.
+ * Raises NSP in case of zombie process.
+ */
+PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+ pid_t pid;
+ struct proc_vnodepathinfo pathinfo;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ if (psutil_proc_pidinfo(
+ pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)) <= 0)
+ {
+ return NULL;
+ }
+
+ return PyUnicode_DecodeFSDefault(pathinfo.pvi_cdir.vip_path);
+}
+
+
+/*
+ * Return path of the process executable.
+ */
+PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+ pid_t pid;
+ char buf[PATH_MAX];
+ int ret;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ errno = 0;
+ ret = proc_pidpath(pid, &buf, sizeof(buf));
+ if (ret == 0) {
+ if (pid == 0) {
+ AccessDenied("automatically set for PID 0");
+ return NULL;
+ }
+ else if (errno == ENOENT) {
+ // It may happen (file not found error) if the process is
+ // still alive but the executable which launched it got
+ // deleted, see:
+ // https://github.com/giampaolo/psutil/issues/1738
+ return Py_BuildValue("s", "");
+ }
+ else {
+ psutil_raise_for_pid(pid, "proc_pidpath()");
+ return NULL;
+ }
+ }
+ return PyUnicode_DecodeFSDefault(buf);
+}
+
+
+/*
+ * Indicates if the given virtual address on the given architecture is in the
+ * shared VM region.
+ */
+static bool
+psutil_in_shared_region(mach_vm_address_t addr, cpu_type_t type) {
+ mach_vm_address_t base;
+ mach_vm_address_t size;
+
+ switch (type) {
+ case CPU_TYPE_ARM:
+ base = SHARED_REGION_BASE_ARM;
+ size = SHARED_REGION_SIZE_ARM;
+ break;
+ case CPU_TYPE_I386:
+ base = SHARED_REGION_BASE_I386;
+ size = SHARED_REGION_SIZE_I386;
+ break;
+ case CPU_TYPE_X86_64:
+ base = SHARED_REGION_BASE_X86_64;
+ size = SHARED_REGION_SIZE_X86_64;
+ break;
+ default:
+ return false;
+ }
+
+ return base <= addr && addr < (base + size);
+}
+
+
+/*
+ * Returns the USS (unique set size) of the process. Reference:
+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
+ * nsMemoryReporterManager.cpp
+ */
+PyObject *
+psutil_proc_memory_uss(PyObject *self, PyObject *args) {
+ pid_t pid;
+ size_t len;
+ cpu_type_t cpu_type;
+ size_t private_pages = 0;
+ mach_vm_size_t size = 0;
+ mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
+ kern_return_t kr;
+ long pagesize = psutil_getpagesize();
+ mach_vm_address_t addr = MACH_VM_MIN_ADDRESS;
+ mach_port_t task = MACH_PORT_NULL;
+ vm_region_top_info_data_t info;
+ mach_port_t object_name;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ if (psutil_task_for_pid(pid, &task) != 0)
+ return NULL;
+
+ len = sizeof(cpu_type);
+ if (sysctlbyname("sysctl.proc_cputype", &cpu_type, &len, NULL, 0) != 0) {
+ return PyErr_SetFromOSErrnoWithSyscall(
+ "sysctlbyname('sysctl.proc_cputype')");
+ }
+
+ // Roughly based on libtop_update_vm_regions in
+ // http://www.opensource.apple.com/source/top/top-100.1.2/libtop.c
+ for (addr = 0; ; addr += size) {
+ kr = mach_vm_region(
+ task, &addr, &size, VM_REGION_TOP_INFO, (vm_region_info_t)&info,
+ &info_count, &object_name);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // Done iterating VM regions.
+ break;
+ }
+ else if (kr != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "mach_vm_region(VM_REGION_TOP_INFO) syscall failed");
+ return NULL;
+ }
+
+ if (psutil_in_shared_region(addr, cpu_type) &&
+ info.share_mode != SM_PRIVATE) {
+ continue;
+ }
+
+ switch (info.share_mode) {
+#ifdef SM_LARGE_PAGE
+ case SM_LARGE_PAGE:
+ // NB: Large pages are not shareable and always resident.
+#endif
+ case SM_PRIVATE:
+ private_pages += info.private_pages_resident;
+ private_pages += info.shared_pages_resident;
+ break;
+ case SM_COW:
+ private_pages += info.private_pages_resident;
+ if (info.ref_count == 1) {
+ // Treat copy-on-write pages as private if they only
+ // have one reference.
+ private_pages += info.shared_pages_resident;
+ }
+ break;
+ case SM_SHARED:
+ default:
+ break;
+ }
+ }
+
+ mach_port_deallocate(mach_task_self(), task);
+ return Py_BuildValue("K", private_pages * pagesize);
+}
+
+
+/*
+ * Return process threads
+ */
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int err, ret;
+ kern_return_t kr;
+ unsigned int info_count = TASK_BASIC_INFO_COUNT;
+ mach_port_t task = MACH_PORT_NULL;
+ struct task_basic_info tasks_info;
+ thread_act_port_array_t thread_list = NULL;
+ thread_info_data_t thinfo_basic;
+ thread_basic_info_t basic_info_th;
+ mach_msg_type_number_t thread_count, thread_info_count, j;
+
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ goto error;
+
+ if (psutil_task_for_pid(pid, &task) != 0)
+ goto error;
+
+ info_count = TASK_BASIC_INFO_COUNT;
+ err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info,
+ &info_count);
+ if (err != KERN_SUCCESS) {
+ // errcode 4 is "invalid argument" (access denied)
+ if (err == 4) {
+ AccessDenied("task_info(TASK_BASIC_INFO)");
+ }
+ else {
+ // otherwise throw a runtime error with appropriate error code
+ PyErr_Format(PyExc_RuntimeError,
+ "task_info(TASK_BASIC_INFO) syscall failed");
+ }
+ goto error;
+ }
+
+ err = task_threads(task, &thread_list, &thread_count);
+ if (err != KERN_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError, "task_threads() syscall failed");
+ goto error;
+ }
+
+ for (j = 0; j < thread_count; j++) {
+ thread_info_count = THREAD_INFO_MAX;
+ kr = thread_info(thread_list[j], THREAD_BASIC_INFO,
+ (thread_info_t)thinfo_basic, &thread_info_count);
+ if (kr != KERN_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError,
+ "thread_info(THREAD_BASIC_INFO) syscall failed");
+ goto error;
+ }
+
+ basic_info_th = (thread_basic_info_t)thinfo_basic;
+ py_tuple = Py_BuildValue(
+ "Iff",
+ j + 1,
+ basic_info_th->user_time.seconds + \
+ (float)basic_info_th->user_time.microseconds / 1000000.0,
+ basic_info_th->system_time.seconds + \
+ (float)basic_info_th->system_time.microseconds / 1000000.0
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ }
+
+ ret = vm_deallocate(task, (vm_address_t)thread_list,
+ thread_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+
+ mach_port_deallocate(mach_task_self(), task);
+
+ return py_retlist;
+
+error:
+ if (task != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), task);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (thread_list != NULL) {
+ ret = vm_deallocate(task, (vm_address_t)thread_list,
+ thread_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+ }
+ return NULL;
+}
+
+
+/*
+ * Return process open files as a Python tuple.
+ * References:
+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd
+ * - /usr/include/sys/proc_info.h
+ */
+PyObject *
+psutil_proc_open_files(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int num_fds;
+ int i;
+ unsigned long nb;
+ struct proc_fdinfo *fds_pointer = NULL;
+ struct proc_fdinfo *fdp_pointer;
+ struct vnode_fdinfowithpath vi;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_path = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ goto error;
+
+ // see: https://github.com/giampaolo/psutil/issues/2116
+ if (pid == 0)
+ return py_retlist;
+
+ fds_pointer = psutil_proc_list_fds(pid, &num_fds);
+ if (fds_pointer == NULL)
+ goto error;
+
+ for (i = 0; i < num_fds; i++) {
+ fdp_pointer = &fds_pointer[i];
+
+ if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
+ errno = 0;
+ nb = proc_pidfdinfo((pid_t)pid,
+ fdp_pointer->proc_fd,
+ PROC_PIDFDVNODEPATHINFO,
+ &vi,
+ sizeof(vi));
+
+ // --- errors checking
+ if ((nb <= 0) || nb < sizeof(vi)) {
+ if ((errno == ENOENT) || (errno == EBADF)) {
+ // no such file or directory or bad file descriptor;
+ // let's assume the file has been closed or removed
+ continue;
+ }
+ else {
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO)");
+ goto error;
+ }
+ }
+ // --- /errors checking
+
+ // --- construct python list
+ py_path = PyUnicode_DecodeFSDefault(vi.pvip.vip_path);
+ if (! py_path)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(Oi)",
+ py_path,
+ (int)fdp_pointer->proc_fd);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_path);
+ // --- /construct python list
+ }
+ }
+
+ free(fds_pointer);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_path);
+ Py_DECREF(py_retlist);
+ if (fds_pointer != NULL)
+ free(fds_pointer);
+ return NULL; // exception has already been set earlier
+}
+
+
+/*
+ * Return process TCP and UDP connections as a list of tuples.
+ * Raises NSP in case of zombie process.
+ * References:
+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
+ * - /usr/include/sys/proc_info.h
+ */
+PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int num_fds;
+ int i;
+ unsigned long nb;
+ struct proc_fdinfo *fds_pointer = NULL;
+ struct proc_fdinfo *fdp_pointer;
+ struct socket_fdinfo si;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_laddr = NULL;
+ PyObject *py_raddr = NULL;
+ PyObject *py_af_filter = NULL;
+ PyObject *py_type_filter = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "OO", &pid, &py_af_filter,
+ &py_type_filter)) {
+ goto error;
+ }
+
+ // see: https://github.com/giampaolo/psutil/issues/2116
+ if (pid == 0)
+ return py_retlist;
+
+ if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
+ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+ goto error;
+ }
+
+ fds_pointer = psutil_proc_list_fds(pid, &num_fds);
+ if (fds_pointer == NULL)
+ goto error;
+
+ for (i = 0; i < num_fds; i++) {
+ py_tuple = NULL;
+ py_laddr = NULL;
+ py_raddr = NULL;
+ fdp_pointer = &fds_pointer[i];
+
+ if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {
+ errno = 0;
+ nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd,
+ PROC_PIDFDSOCKETINFO, &si, sizeof(si));
+
+ // --- errors checking
+ if ((nb <= 0) || (nb < sizeof(si)) || (errno != 0)) {
+ if (errno == EBADF) {
+ // let's assume socket has been closed
+ psutil_debug("proc_pidfdinfo(PROC_PIDFDSOCKETINFO) -> "
+ "EBADF (ignored)");
+ continue;
+ }
+ else if (errno == EOPNOTSUPP) {
+ // may happen sometimes, see:
+ // https://github.com/giampaolo/psutil/issues/1512
+ psutil_debug("proc_pidfdinfo(PROC_PIDFDSOCKETINFO) -> "
+ "EOPNOTSUPP (ignored)");
+ continue;
+ }
+ else {
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)");
+ goto error;
+ }
+ }
+ // --- /errors checking
+
+ //
+ int fd, family, type, lport, rport, state;
+ // TODO: use INET6_ADDRSTRLEN instead of 200
+ char lip[200], rip[200];
+ int inseq;
+ PyObject *py_family;
+ PyObject *py_type;
+
+ fd = (int)fdp_pointer->proc_fd;
+ family = si.psi.soi_family;
+ type = si.psi.soi_type;
+
+ // apply filters
+ py_family = PyLong_FromLong((long)family);
+ inseq = PySequence_Contains(py_af_filter, py_family);
+ Py_DECREF(py_family);
+ if (inseq == 0)
+ continue;
+ py_type = PyLong_FromLong((long)type);
+ inseq = PySequence_Contains(py_type_filter, py_type);
+ Py_DECREF(py_type);
+ if (inseq == 0)
+ continue;
+
+ if ((family == AF_INET) || (family == AF_INET6)) {
+ if (family == AF_INET) {
+ inet_ntop(AF_INET,
+ &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
+ insi_laddr.ina_46.i46a_addr4,
+ lip,
+ sizeof(lip));
+ inet_ntop(AF_INET,
+ &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr. \
+ ina_46.i46a_addr4,
+ rip,
+ sizeof(rip));
+ }
+ else {
+ inet_ntop(AF_INET6,
+ &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
+ insi_laddr.ina_6,
+ lip, sizeof(lip));
+ inet_ntop(AF_INET6,
+ &si.psi.soi_proto.pri_tcp.tcpsi_ini. \
+ insi_faddr.ina_6,
+ rip, sizeof(rip));
+ }
+
+ // check for inet_ntop failures
+ if (errno != 0) {
+ PyErr_SetFromOSErrnoWithSyscall("inet_ntop()");
+ goto error;
+ }
+
+ lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport);
+ rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport);
+ if (type == SOCK_STREAM)
+ state = (int)si.psi.soi_proto.pri_tcp.tcpsi_state;
+ else
+ state = PSUTIL_CONN_NONE;
+
+ py_laddr = Py_BuildValue("(si)", lip, lport);
+ if (!py_laddr)
+ goto error;
+ if (rport != 0)
+ py_raddr = Py_BuildValue("(si)", rip, rport);
+ else
+ py_raddr = Py_BuildValue("()");
+ if (!py_raddr)
+ goto error;
+
+ // construct the python list
+ py_tuple = Py_BuildValue(
+ "(iiiNNi)", fd, family, type, py_laddr, py_raddr, state);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ }
+ else if (family == AF_UNIX) {
+ py_laddr = PyUnicode_DecodeFSDefault(
+ si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path);
+ if (!py_laddr)
+ goto error;
+ py_raddr = PyUnicode_DecodeFSDefault(
+ si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path);
+ if (!py_raddr)
+ goto error;
+ // construct the python list
+ py_tuple = Py_BuildValue(
+ "(iiiOOi)",
+ fd, family, type,
+ py_laddr,
+ py_raddr,
+ PSUTIL_CONN_NONE);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_laddr);
+ Py_CLEAR(py_raddr);
+ }
+ }
+ }
+
+ free(fds_pointer);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_laddr);
+ Py_XDECREF(py_raddr);
+ Py_DECREF(py_retlist);
+ if (fds_pointer != NULL)
+ free(fds_pointer);
+ return NULL;
+}
+
+
+/*
+ * Return number of file descriptors opened by process.
+ * Raises NSP in case of zombie process.
+ */
+PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int num_fds;
+ struct proc_fdinfo *fds_pointer;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ fds_pointer = psutil_proc_list_fds(pid, &num_fds);
+ if (fds_pointer == NULL)
+ return NULL;
+
+ free(fds_pointer);
+ return Py_BuildValue("i", num_fds);
+}
+
+
+// return process args as a python list
+PyObject *
+psutil_proc_cmdline(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int nargs;
+ size_t len;
+ char *procargs = NULL;
+ char *arg_ptr;
+ char *arg_end;
+ char *curr_arg;
+ size_t argmax;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_arg = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ goto error;
+
+ // special case for PID 0 (kernel_task) where cmdline cannot be fetched
+ if (pid == 0)
+ return py_retlist;
+
+ // read argmax and allocate memory for argument space.
+ argmax = psutil_sysctl_argmax();
+ if (! argmax)
+ goto error;
+
+ procargs = (char *)malloc(argmax);
+ if (NULL == procargs) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (psutil_sysctl_procargs(pid, procargs, &argmax) != 0)
+ goto error;
+
+ arg_end = &procargs[argmax];
+ // copy the number of arguments to nargs
+ memcpy(&nargs, procargs, sizeof(nargs));
+
+ arg_ptr = procargs + sizeof(nargs);
+ len = strlen(arg_ptr);
+ arg_ptr += len + 1;
+
+ if (arg_ptr == arg_end) {
+ free(procargs);
+ return py_retlist;
+ }
+
+ // skip ahead to the first argument
+ for (; arg_ptr < arg_end; arg_ptr++) {
+ if (*arg_ptr != '\0')
+ break;
+ }
+
+ // iterate through arguments
+ curr_arg = arg_ptr;
+ while (arg_ptr < arg_end && nargs > 0) {
+ if (*arg_ptr++ == '\0') {
+ py_arg = PyUnicode_DecodeFSDefault(curr_arg);
+ if (! py_arg)
+ goto error;
+ if (PyList_Append(py_retlist, py_arg))
+ goto error;
+ Py_DECREF(py_arg);
+ // iterate to next arg and decrement # of args
+ curr_arg = arg_ptr;
+ nargs--;
+ }
+ }
+
+ free(procargs);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_arg);
+ Py_XDECREF(py_retlist);
+ if (procargs != NULL)
+ free(procargs);
+ return NULL;
+}
+
+
+// Return process environment as a python string.
+// On Big Sur this function returns an empty string unless:
+// * kernel is DEVELOPMENT || DEBUG
+// * target process is same as current_proc()
+// * target process is not cs_restricted
+// * SIP is off
+// * caller has an entitlement
+// See: https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/kern_sysctl.c#L1315-L1321
+PyObject *
+psutil_proc_environ(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int nargs;
+ char *procargs = NULL;
+ char *procenv = NULL;
+ char *arg_ptr;
+ char *arg_end;
+ char *env_start;
+ size_t argmax;
+ PyObject *py_ret = NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ // special case for PID 0 (kernel_task) where cmdline cannot be fetched
+ if (pid == 0)
+ goto empty;
+
+ // read argmax and allocate memory for argument space.
+ argmax = psutil_sysctl_argmax();
+ if (! argmax)
+ goto error;
+
+ procargs = (char *)malloc(argmax);
+ if (NULL == procargs) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (psutil_sysctl_procargs(pid, procargs, &argmax) != 0)
+ goto error;
+
+ arg_end = &procargs[argmax];
+ // copy the number of arguments to nargs
+ memcpy(&nargs, procargs, sizeof(nargs));
+
+ // skip executable path
+ arg_ptr = procargs + sizeof(nargs);
+ arg_ptr = memchr(arg_ptr, '\0', arg_end - arg_ptr);
+
+ if (arg_ptr == NULL || arg_ptr == arg_end) {
+ psutil_debug(
+ "(arg_ptr == NULL || arg_ptr == arg_end); set environ to empty");
+ goto empty;
+ }
+
+ // skip ahead to the first argument
+ for (; arg_ptr < arg_end; arg_ptr++) {
+ if (*arg_ptr != '\0')
+ break;
+ }
+
+ // iterate through arguments
+ while (arg_ptr < arg_end && nargs > 0) {
+ if (*arg_ptr++ == '\0')
+ nargs--;
+ }
+
+ // build an environment variable block
+ env_start = arg_ptr;
+
+ procenv = calloc(1, arg_end - arg_ptr);
+ if (procenv == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ while (*arg_ptr != '\0' && arg_ptr < arg_end) {
+ char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
+ if (s == NULL)
+ break;
+ memcpy(procenv + (arg_ptr - env_start), arg_ptr, s - arg_ptr);
+ arg_ptr = s + 1;
+ }
+
+ py_ret = PyUnicode_DecodeFSDefaultAndSize(
+ procenv, arg_ptr - env_start + 1);
+ if (!py_ret) {
+ // XXX: don't want to free() this as per:
+ // https://github.com/giampaolo/psutil/issues/926
+ // It sucks but not sure what else to do.
+ procargs = NULL;
+ goto error;
+ }
+
+ free(procargs);
+ free(procenv);
+ return py_ret;
+
+empty:
+ if (procargs != NULL)
+ free(procargs);
+ return Py_BuildValue("s", "");
+
+error:
+ Py_XDECREF(py_ret);
+ if (procargs != NULL)
+ free(procargs);
+ if (procenv != NULL)
+ free(procargs);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/proc.h b/contrib/python/psutil/py3/psutil/arch/osx/proc.h
new file mode 100644
index 0000000000..63f16ccdd2
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/proc.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_pids(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cmdline(PyObject *self, PyObject *args);
+PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cwd(PyObject *self, PyObject *args);
+PyObject *psutil_proc_environ(PyObject *self, PyObject *args);
+PyObject *psutil_proc_exe(PyObject *self, PyObject *args);
+PyObject *psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args);
+PyObject *psutil_proc_memory_uss(PyObject *self, PyObject *args);
+PyObject *psutil_proc_name(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
+PyObject *psutil_proc_open_files(PyObject *self, PyObject *args);
+PyObject *psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args);
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/process_info.c b/contrib/python/psutil/py3/psutil/arch/osx/process_info.c
deleted file mode 100644
index fb9f24ffac..0000000000
--- a/contrib/python/psutil/py3/psutil/arch/osx/process_info.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Helper functions related to fetching process information.
- * Used by _psutil_osx module methods.
- */
-
-
-#include <Python.h>
-#include <errno.h>
-#include <sys/sysctl.h>
-#include <libproc.h>
-
-#include "../../_psutil_common.h"
-#include "../../_psutil_posix.h"
-#include "process_info.h"
-
-
-/*
- * Returns a list of all BSD processes on the system. This routine
- * allocates the list and puts it in *procList and a count of the
- * number of entries in *procCount. You are responsible for freeing
- * this list (use "free" from System framework).
- * On success, the function returns 0.
- * On error, the function returns a BSD errno value.
- */
-int
-psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
- int mib[3];
- size_t size, size2;
- void *ptr;
- int err;
- int lim = 8; // some limit
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC;
- mib[2] = KERN_PROC_ALL;
- *procCount = 0;
-
- /*
- * We start by calling sysctl with ptr == NULL and size == 0.
- * That will succeed, and set size to the appropriate length.
- * We then allocate a buffer of at least that size and call
- * sysctl with that buffer. If that succeeds, we're done.
- * If that call fails with ENOMEM, we throw the buffer away
- * and try again.
- * Note that the loop calls sysctl with NULL again. This is
- * is necessary because the ENOMEM failure case sets size to
- * the amount of data returned, not the amount of data that
- * could have been returned.
- */
- while (lim-- > 0) {
- size = 0;
- if (sysctl((int *)mib, 3, NULL, &size, NULL, 0) == -1) {
- PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
- return 1;
- }
- size2 = size + (size >> 3); // add some
- if (size2 > size) {
- ptr = malloc(size2);
- if (ptr == NULL)
- ptr = malloc(size);
- else
- size = size2;
- }
- else {
- ptr = malloc(size);
- }
- if (ptr == NULL) {
- PyErr_NoMemory();
- return 1;
- }
-
- if (sysctl((int *)mib, 3, ptr, &size, NULL, 0) == -1) {
- err = errno;
- free(ptr);
- if (err != ENOMEM) {
- PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)");
- return 1;
- }
- }
- else {
- *procList = (kinfo_proc *)ptr;
- *procCount = size / sizeof(kinfo_proc);
- if (procCount <= 0) {
- PyErr_Format(PyExc_RuntimeError, "no PIDs found");
- return 1;
- }
- return 0; // success
- }
- }
-
- PyErr_Format(PyExc_RuntimeError, "couldn't collect PIDs list");
- return 1;
-}
-
-
-// Read the maximum argument size for processes
-static int
-psutil_sysctl_argmax() {
- int argmax;
- int mib[2];
- size_t size = sizeof(argmax);
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_ARGMAX;
-
- if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0)
- return argmax;
- PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_ARGMAX)");
- return 0;
-}
-
-
-// Read process argument space.
-static int
-psutil_sysctl_procargs(pid_t pid, char *procargs, size_t argmax) {
- int mib[3];
-
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROCARGS2;
- mib[2] = pid;
-
- if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
- if (psutil_pid_exists(pid) == 0) {
- NoSuchProcess("psutil_pid_exists -> 0");
- return 1;
- }
- // In case of zombie process we'll get EINVAL. We translate it
- // to NSP and _psosx.py will translate it to ZP.
- if (errno == EINVAL) {
- psutil_debug("sysctl(KERN_PROCARGS2) -> EINVAL translated to NSP");
- NoSuchProcess("sysctl(KERN_PROCARGS2) -> EINVAL");
- return 1;
- }
- // There's nothing we can do other than raising AD.
- if (errno == EIO) {
- psutil_debug("sysctl(KERN_PROCARGS2) -> EIO translated to AD");
- AccessDenied("sysctl(KERN_PROCARGS2) -> EIO");
- return 1;
- }
- PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROCARGS2)");
- return 1;
- }
- return 0;
-}
-
-
-// Return 1 if pid refers to a zombie process else 0.
-int
-psutil_is_zombie(pid_t pid) {
- struct kinfo_proc kp;
-
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return 0;
- return (kp.kp_proc.p_stat == SZOMB) ? 1 : 0;
-}
-
-
-// return process args as a python list
-PyObject *
-psutil_get_cmdline(pid_t pid) {
- int nargs;
- size_t len;
- char *procargs = NULL;
- char *arg_ptr;
- char *arg_end;
- char *curr_arg;
- size_t argmax;
-
- PyObject *py_arg = NULL;
- PyObject *py_retlist = NULL;
-
- // special case for PID 0 (kernel_task) where cmdline cannot be fetched
- if (pid == 0)
- return Py_BuildValue("[]");
-
- // read argmax and allocate memory for argument space.
- argmax = psutil_sysctl_argmax();
- if (! argmax)
- goto error;
-
- procargs = (char *)malloc(argmax);
- if (NULL == procargs) {
- PyErr_NoMemory();
- goto error;
- }
-
- if (psutil_sysctl_procargs(pid, procargs, argmax) != 0)
- goto error;
-
- arg_end = &procargs[argmax];
- // copy the number of arguments to nargs
- memcpy(&nargs, procargs, sizeof(nargs));
-
- arg_ptr = procargs + sizeof(nargs);
- len = strlen(arg_ptr);
- arg_ptr += len + 1;
-
- if (arg_ptr == arg_end) {
- free(procargs);
- return Py_BuildValue("[]");
- }
-
- // skip ahead to the first argument
- for (; arg_ptr < arg_end; arg_ptr++) {
- if (*arg_ptr != '\0')
- break;
- }
-
- // iterate through arguments
- curr_arg = arg_ptr;
- py_retlist = Py_BuildValue("[]");
- if (!py_retlist)
- goto error;
- while (arg_ptr < arg_end && nargs > 0) {
- if (*arg_ptr++ == '\0') {
- py_arg = PyUnicode_DecodeFSDefault(curr_arg);
- if (! py_arg)
- goto error;
- if (PyList_Append(py_retlist, py_arg))
- goto error;
- Py_DECREF(py_arg);
- // iterate to next arg and decrement # of args
- curr_arg = arg_ptr;
- nargs--;
- }
- }
-
- free(procargs);
- return py_retlist;
-
-error:
- Py_XDECREF(py_arg);
- Py_XDECREF(py_retlist);
- if (procargs != NULL)
- free(procargs);
- return NULL;
-}
-
-
-// return process environment as a python string
-PyObject *
-psutil_get_environ(pid_t pid) {
- int nargs;
- char *procargs = NULL;
- char *procenv = NULL;
- char *arg_ptr;
- char *arg_end;
- char *env_start;
- size_t argmax;
- PyObject *py_ret = NULL;
-
- // special case for PID 0 (kernel_task) where cmdline cannot be fetched
- if (pid == 0)
- goto empty;
-
- // read argmax and allocate memory for argument space.
- argmax = psutil_sysctl_argmax();
- if (! argmax)
- goto error;
-
- procargs = (char *)malloc(argmax);
- if (NULL == procargs) {
- PyErr_NoMemory();
- goto error;
- }
-
- if (psutil_sysctl_procargs(pid, procargs, argmax) != 0)
- goto error;
-
- arg_end = &procargs[argmax];
- // copy the number of arguments to nargs
- memcpy(&nargs, procargs, sizeof(nargs));
-
- // skip executable path
- arg_ptr = procargs + sizeof(nargs);
- arg_ptr = memchr(arg_ptr, '\0', arg_end - arg_ptr);
-
- if (arg_ptr == NULL || arg_ptr == arg_end)
- goto empty;
-
- // skip ahead to the first argument
- for (; arg_ptr < arg_end; arg_ptr++) {
- if (*arg_ptr != '\0')
- break;
- }
-
- // iterate through arguments
- while (arg_ptr < arg_end && nargs > 0) {
- if (*arg_ptr++ == '\0')
- nargs--;
- }
-
- // build an environment variable block
- env_start = arg_ptr;
-
- procenv = calloc(1, arg_end - arg_ptr);
- if (procenv == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- while (*arg_ptr != '\0' && arg_ptr < arg_end) {
- char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
- if (s == NULL)
- break;
- memcpy(procenv + (arg_ptr - env_start), arg_ptr, s - arg_ptr);
- arg_ptr = s + 1;
- }
-
- py_ret = PyUnicode_DecodeFSDefaultAndSize(
- procenv, arg_ptr - env_start + 1);
- if (!py_ret) {
- // XXX: don't want to free() this as per:
- // https://github.com/giampaolo/psutil/issues/926
- // It sucks but not sure what else to do.
- procargs = NULL;
- goto error;
- }
-
- free(procargs);
- free(procenv);
- return py_ret;
-
-empty:
- if (procargs != NULL)
- free(procargs);
- return Py_BuildValue("s", "");
-
-error:
- Py_XDECREF(py_ret);
- if (procargs != NULL)
- free(procargs);
- if (procenv != NULL)
- free(procargs);
- return NULL;
-}
-
-
-int
-psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) {
- int mib[4];
- size_t len;
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC;
- mib[2] = KERN_PROC_PID;
- mib[3] = pid;
-
- // fetch the info with sysctl()
- len = sizeof(struct kinfo_proc);
-
- // now read the data from sysctl
- if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {
- // raise an exception and throw errno as the error
- PyErr_SetFromOSErrnoWithSyscall("sysctl");
- return -1;
- }
-
- // sysctl succeeds but len is zero, happens when process has gone away
- if (len == 0) {
- NoSuchProcess("sysctl (len == 0)");
- return -1;
- }
- return 0;
-}
-
-
-/*
- * A wrapper around proc_pidinfo().
- * Returns 0 on failure (and Python exception gets already set).
- */
-int
-psutil_proc_pidinfo(pid_t pid, int flavor, uint64_t arg, void *pti, int size) {
- errno = 0;
- int ret = proc_pidinfo(pid, flavor, arg, pti, size);
- if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
- psutil_raise_for_pid(pid, "proc_pidinfo()");
- return 0;
- }
- return ret;
-}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/process_info.h b/contrib/python/psutil/py3/psutil/arch/osx/process_info.h
deleted file mode 100644
index ffa6230f7c..0000000000
--- a/contrib/python/psutil/py3/psutil/arch/osx/process_info.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <Python.h>
-
-typedef struct kinfo_proc kinfo_proc;
-
-int psutil_is_zombie(pid_t pid);
-int psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp);
-int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
-int psutil_proc_pidinfo(
- pid_t pid, int flavor, uint64_t arg, void *pti, int size);
-PyObject* psutil_get_cmdline(pid_t pid);
-PyObject* psutil_get_environ(pid_t pid);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/sensors.c b/contrib/python/psutil/py3/psutil/arch/osx/sensors.c
new file mode 100644
index 0000000000..a2faa157c4
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/sensors.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Sensors related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+// Original battery code:
+// https://github.com/giampaolo/psutil/commit/e0df5da
+
+
+#include <Python.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+ PyObject *py_tuple = NULL;
+ CFTypeRef power_info = NULL;
+ CFArrayRef power_sources_list = NULL;
+ CFDictionaryRef power_sources_information = NULL;
+ CFNumberRef capacity_ref = NULL;
+ CFNumberRef time_to_empty_ref = NULL;
+ CFStringRef ps_state_ref = NULL;
+ uint32_t capacity; /* units are percent */
+ int time_to_empty; /* units are minutes */
+ int is_power_plugged;
+
+ power_info = IOPSCopyPowerSourcesInfo();
+
+ if (!power_info) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesInfo() syscall failed");
+ goto error;
+ }
+
+ power_sources_list = IOPSCopyPowerSourcesList(power_info);
+ if (!power_sources_list) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesList() syscall failed");
+ goto error;
+ }
+
+ /* Should only get one source. But in practice, check for > 0 sources */
+ if (!CFArrayGetCount(power_sources_list)) {
+ PyErr_SetString(PyExc_NotImplementedError, "no battery");
+ goto error;
+ }
+
+ power_sources_information = IOPSGetPowerSourceDescription(
+ power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
+
+ capacity_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
+ if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "No battery capacity infomration in power sources info");
+ goto error;
+ }
+
+ ps_state_ref = (CFStringRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
+ is_power_plugged = CFStringCompare(
+ ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
+ == kCFCompareEqualTo;
+
+ time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
+ if (!CFNumberGetValue(time_to_empty_ref,
+ kCFNumberIntType, &time_to_empty)) {
+ /* This value is recommended for non-Apple power sources, so it's not
+ * an error if it doesn't exist. We'll return -1 for "unknown" */
+ /* A value of -1 indicates "Still Calculating the Time" also for
+ * apple power source */
+ time_to_empty = -1;
+ }
+
+ py_tuple = Py_BuildValue("Iii",
+ capacity, time_to_empty, is_power_plugged);
+ if (!py_tuple) {
+ goto error;
+ }
+
+ CFRelease(power_info);
+ CFRelease(power_sources_list);
+ /* Caller should NOT release power_sources_information */
+
+ return py_tuple;
+
+error:
+ if (power_info)
+ CFRelease(power_info);
+ if (power_sources_list)
+ CFRelease(power_sources_list);
+ Py_XDECREF(py_tuple);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/sensors.h b/contrib/python/psutil/py3/psutil/arch/osx/sensors.h
new file mode 100644
index 0000000000..edace25d3d
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/sensors.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_sensors_battery(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/sys.c b/contrib/python/psutil/py3/psutil/arch/osx/sys.c
new file mode 100644
index 0000000000..4fe6642597
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/sys.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// System related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <sys/sysctl.h>
+#include <utmpx.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ // fetch sysctl "kern.boottime"
+ static int request[2] = { CTL_KERN, KERN_BOOTTIME };
+ struct timeval result;
+ size_t result_len = sizeof result;
+ time_t boot_time = 0;
+
+ if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ boot_time = result.tv_sec;
+ return Py_BuildValue("f", (float)boot_time);
+}
+
+
+PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ struct utmpx *utx;
+ PyObject *py_username = NULL;
+ PyObject *py_tty = NULL;
+ PyObject *py_hostname = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ while ((utx = getutxent()) != NULL) {
+ if (utx->ut_type != USER_PROCESS)
+ continue;
+ py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
+ if (! py_username)
+ goto error;
+ py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
+ if (! py_tty)
+ goto error;
+ py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
+ if (! py_hostname)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOOdi)",
+ py_username, // username
+ py_tty, // tty
+ py_hostname, // hostname
+ (double)utx->ut_tv.tv_sec, // start time
+ utx->ut_pid // process id
+ );
+ if (!py_tuple) {
+ endutxent();
+ goto error;
+ }
+ if (PyList_Append(py_retlist, py_tuple)) {
+ endutxent();
+ goto error;
+ }
+ Py_CLEAR(py_username);
+ Py_CLEAR(py_tty);
+ Py_CLEAR(py_hostname);
+ Py_CLEAR(py_tuple);
+ }
+
+ endutxent();
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tty);
+ Py_XDECREF(py_hostname);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/sys.h b/contrib/python/psutil/py3/psutil/arch/osx/sys.h
new file mode 100644
index 0000000000..344ca21d42
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/sys.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_boot_time(PyObject *self, PyObject *args);
+PyObject *psutil_users(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/cpu.c b/contrib/python/psutil/py3/psutil/arch/windows/cpu.c
index 18f32e5983..9d89e5bb6c 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/cpu.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/cpu.c
@@ -100,7 +100,7 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) {
goto error;
}
- // gets cpu time informations
+ // gets cpu time information
status = NtQuerySystemInformation(
SystemProcessorPerformanceInformation,
sppi,
@@ -172,16 +172,15 @@ psutil_cpu_count_logical(PyObject *self, PyObject *args) {
if (ncpus != 0)
return Py_BuildValue("I", ncpus);
else
- Py_RETURN_NONE; // mimick os.cpu_count()
+ Py_RETURN_NONE; // mimic os.cpu_count()
}
/*
- * Return the number of physical CPU cores (hyper-thread CPUs count
- * is excluded).
+ * Return the number of CPU cores (non hyper-threading).
*/
PyObject *
-psutil_cpu_count_phys(PyObject *self, PyObject *args) {
+psutil_cpu_count_cores(PyObject *self, PyObject *args) {
DWORD rc;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
@@ -196,7 +195,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) {
// than 64 CPUs. See:
// https://bugs.python.org/issue33166
if (GetLogicalProcessorInformationEx == NULL) {
- psutil_debug("Win < 7; cpu_count_phys() forced to None");
+ psutil_debug("Win < 7; cpu_count_cores() forced to None");
Py_RETURN_NONE;
}
@@ -216,7 +215,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) {
}
}
else {
- psutil_debug("GetLogicalProcessorInformationEx() returned ",
+ psutil_debug("GetLogicalProcessorInformationEx() returned %u",
GetLastError());
goto return_none;
}
@@ -249,7 +248,7 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) {
}
else {
psutil_debug("GetLogicalProcessorInformationEx() count was 0");
- Py_RETURN_NONE; // mimick os.cpu_count()
+ Py_RETURN_NONE; // mimic os.cpu_count()
}
return_none:
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/cpu.h b/contrib/python/psutil/py3/psutil/arch/windows/cpu.h
index d88c221210..1ef3ff1f04 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/cpu.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/cpu.h
@@ -7,7 +7,7 @@
#include <Python.h>
PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
-PyObject *psutil_cpu_count_phys(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);
PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/disk.c b/contrib/python/psutil/py3/psutil/arch/windows/disk.c
index 2288f22c56..e72e3a7554 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/disk.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/disk.c
@@ -45,6 +45,8 @@ PyObject *
psutil_disk_usage(PyObject *self, PyObject *args) {
BOOL retval;
ULARGE_INTEGER _, total, free;
+
+#if PY_MAJOR_VERSION <= 2
char *path;
if (PyArg_ParseTuple(args, "u", &path)) {
@@ -56,7 +58,6 @@ psutil_disk_usage(PyObject *self, PyObject *args) {
// on Python 2 we also want to accept plain strings other
// than Unicode
-#if PY_MAJOR_VERSION <= 2
PyErr_Clear(); // drop the argument parsing error
if (PyArg_ParseTuple(args, "s", &path)) {
Py_BEGIN_ALLOW_THREADS
@@ -64,15 +65,35 @@ psutil_disk_usage(PyObject *self, PyObject *args) {
Py_END_ALLOW_THREADS
goto return_;
}
-#endif
return NULL;
return_:
if (retval == 0)
return PyErr_SetFromWindowsErrWithFilename(0, path);
- else
- return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart);
+#else
+ PyObject *py_path;
+ wchar_t *path;
+
+ if (!PyArg_ParseTuple(args, "U", &py_path)) {
+ return NULL;
+ }
+
+ path = PyUnicode_AsWideCharString(py_path, NULL);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ retval = GetDiskFreeSpaceExW(path, &_, &total, &free);
+ Py_END_ALLOW_THREADS
+
+ PyMem_Free(path);
+
+ if (retval == 0)
+ return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, py_path);
+#endif
+ return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart);
}
@@ -202,7 +223,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
int type;
int ret;
unsigned int old_mode = 0;
- char opts[20];
+ char opts[50];
HANDLE mp_h;
BOOL mp_flag= TRUE;
LPTSTR fs_type[MAX_PATH + 1] = { 0 };
@@ -291,6 +312,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
mp_h = FindFirstVolumeMountPoint(
drive_letter, mp_buf, MAX_PATH);
if (mp_h != INVALID_HANDLE_VALUE) {
+ mp_flag = TRUE;
while (mp_flag) {
// Append full mount path with drive letter
strcpy_s(mp_path, _countof(mp_path), drive_letter);
@@ -365,7 +387,7 @@ error:
If no match is found return an empty string.
*/
PyObject *
-psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) {
+psutil_QueryDosDevice(PyObject *self, PyObject *args) {
LPCTSTR lpDevicePath;
TCHAR d = TEXT('A');
TCHAR szBuff[5];
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/disk.h b/contrib/python/psutil/py3/psutil/arch/windows/disk.h
index 298fb6ba0e..28bed22b54 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/disk.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/disk.h
@@ -9,4 +9,4 @@
PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
PyObject *psutil_disk_usage(PyObject *self, PyObject *args);
-PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args);
+PyObject *psutil_QueryDosDevice(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/mem.c b/contrib/python/psutil/py3/psutil/arch/windows/mem.c
new file mode 100644
index 0000000000..24dc15ad0e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/mem.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <windows.h>
+#include <Psapi.h>
+#include <pdh.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_getpagesize(PyObject *self, PyObject *args) {
+ // XXX: we may want to use GetNativeSystemInfo to differentiate
+ // page size for WoW64 processes (but am not sure).
+ return Py_BuildValue("I", PSUTIL_SYSTEM_INFO.dwPageSize);
+}
+
+
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ unsigned long long totalPhys, availPhys, totalSys, availSys, pageSize;
+ PERFORMANCE_INFORMATION perfInfo;
+
+ if (! GetPerformanceInfo(&perfInfo, sizeof(PERFORMANCE_INFORMATION))) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+ // values are size_t, widen (if needed) to long long
+ pageSize = perfInfo.PageSize;
+ totalPhys = perfInfo.PhysicalTotal * pageSize;
+ availPhys = perfInfo.PhysicalAvailable * pageSize;
+ totalSys = perfInfo.CommitLimit * pageSize;
+ availSys = totalSys - perfInfo.CommitTotal * pageSize;
+ return Py_BuildValue(
+ "(LLLL)",
+ totalPhys,
+ availPhys,
+ totalSys,
+ availSys);
+}
+
+
+// Return a float representing the percent usage of all paging files on
+// the system.
+PyObject *
+psutil_swap_percent(PyObject *self, PyObject *args) {
+ WCHAR *szCounterPath = L"\\Paging File(_Total)\\% Usage";
+ PDH_STATUS s;
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ PDH_FMT_COUNTERVALUE counterValue;
+ double percentUsage;
+
+ if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError, "PdhOpenQueryW failed");
+ return NULL;
+ }
+
+ s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
+ if (s != ERROR_SUCCESS) {
+ PdhCloseQuery(hQuery);
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "PdhAddEnglishCounterW failed. Performance counters may be disabled."
+ );
+ return NULL;
+ }
+
+ s = PdhCollectQueryData(hQuery);
+ if (s != ERROR_SUCCESS) {
+ // If swap disabled this will fail.
+ psutil_debug("PdhCollectQueryData failed; assume swap percent is 0");
+ percentUsage = 0;
+ }
+ else {
+ s = PdhGetFormattedCounterValue(
+ (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &counterValue);
+ if (s != ERROR_SUCCESS) {
+ PdhCloseQuery(hQuery);
+ PyErr_Format(
+ PyExc_RuntimeError, "PdhGetFormattedCounterValue failed");
+ return NULL;
+ }
+ percentUsage = counterValue.doubleValue;
+ }
+
+ PdhRemoveCounter(hCounter);
+ PdhCloseQuery(hQuery);
+ return Py_BuildValue("d", percentUsage);
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/mem.h b/contrib/python/psutil/py3/psutil/arch/windows/mem.h
new file mode 100644
index 0000000000..48d3dadee6
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/mem.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_getpagesize(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
+PyObject *psutil_swap_percent(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h b/contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h
index a3cc932470..6323152a59 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h
@@ -403,7 +403,7 @@ typedef struct _WTSINFOW {
#define PWTSINFO PWTSINFOW
-// cpu_count_phys()
+// cpu_count_cores()
#if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP)
typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/proc.c b/contrib/python/psutil/py3/psutil/arch/windows/proc.c
new file mode 100644
index 0000000000..af3df267ac
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc.c
@@ -0,0 +1,1251 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Process related functions. Original code was moved in here from
+ * psutil/_psutil_windows.c in 2023. For reference, here's the GIT blame
+ * history before the move:
+ * https://github.com/giampaolo/psutil/blame/59504a5/psutil/_psutil_windows.c
+*/
+
+// Fixes clash between winsock2.h and windows.h
+#define WIN32_LEAN_AND_MEAN
+
+#include <Python.h>
+#include <windows.h>
+#include <Psapi.h> // memory_info(), memory_maps()
+#include <signal.h>
+#include <tlhelp32.h> // threads(), PROCESSENTRY32
+
+// Link with Iphlpapi.lib
+#pragma comment(lib, "IPHLPAPI.lib")
+
+#include "../../_psutil_common.h"
+#include "proc.h"
+#include "proc_info.h"
+#include "proc_handles.h"
+#include "proc_utils.h"
+
+
+// Raised by Process.wait().
+PyObject *TimeoutExpired;
+PyObject *TimeoutAbandoned;
+
+
+/*
+ * Return 1 if PID exists in the current process list, else 0.
+ */
+PyObject *
+psutil_pid_exists(PyObject *self, PyObject *args) {
+ DWORD pid;
+ int status;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ status = psutil_pid_is_running(pid);
+ if (-1 == status)
+ return NULL; // exception raised in psutil_pid_is_running()
+ return PyBool_FromLong(status);
+}
+
+
+/*
+ * Return a Python list of all the PIDs running on the system.
+ */
+PyObject *
+psutil_pids(PyObject *self, PyObject *args) {
+ DWORD *proclist = NULL;
+ DWORD numberOfReturnedPIDs;
+ DWORD i;
+ PyObject *py_pid = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ proclist = psutil_get_pids(&numberOfReturnedPIDs);
+ if (proclist == NULL)
+ goto error;
+
+ for (i = 0; i < numberOfReturnedPIDs; i++) {
+ py_pid = PyLong_FromPid(proclist[i]);
+ if (!py_pid)
+ goto error;
+ if (PyList_Append(py_retlist, py_pid))
+ goto error;
+ Py_CLEAR(py_pid);
+ }
+
+ // free C array allocated for PIDs
+ free(proclist);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_pid);
+ Py_DECREF(py_retlist);
+ if (proclist != NULL)
+ free(proclist);
+ return NULL;
+}
+
+
+/*
+ * Kill a process given its PID.
+ */
+PyObject *
+psutil_proc_kill(PyObject *self, PyObject *args) {
+ HANDLE hProcess;
+ DWORD pid;
+ DWORD access = PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (pid == 0)
+ return AccessDenied("automatically set for PID 0");
+
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL) {
+ return NULL;
+ }
+
+ if (! TerminateProcess(hProcess, SIGTERM)) {
+ // ERROR_ACCESS_DENIED may happen if the process already died. See:
+ // https://github.com/giampaolo/psutil/issues/1099
+ // http://bugs.python.org/issue14252
+ if (GetLastError() != ERROR_ACCESS_DENIED) {
+ PyErr_SetFromOSErrnoWithSyscall("TerminateProcess");
+ return NULL;
+ }
+ }
+
+ CloseHandle(hProcess);
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Wait for process to terminate and return its exit code.
+ */
+PyObject *
+psutil_proc_wait(PyObject *self, PyObject *args) {
+ HANDLE hProcess;
+ DWORD ExitCode;
+ DWORD retVal;
+ DWORD pid;
+ long timeout;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "l", &pid, &timeout))
+ return NULL;
+ if (pid == 0)
+ return AccessDenied("automatically set for PID 0");
+
+ hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+ FALSE, pid);
+ if (hProcess == NULL) {
+ if (GetLastError() == ERROR_INVALID_PARAMETER) {
+ // no such process; we do not want to raise NSP but
+ // return None instead.
+ Py_RETURN_NONE;
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcess");
+ return NULL;
+ }
+ }
+
+ // wait until the process has terminated
+ Py_BEGIN_ALLOW_THREADS
+ retVal = WaitForSingleObject(hProcess, timeout);
+ Py_END_ALLOW_THREADS
+
+ // handle return code
+ if (retVal == WAIT_FAILED) {
+ PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ if (retVal == WAIT_TIMEOUT) {
+ PyErr_SetString(TimeoutExpired,
+ "WaitForSingleObject() returned WAIT_TIMEOUT");
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ if (retVal == WAIT_ABANDONED) {
+ psutil_debug("WaitForSingleObject() -> WAIT_ABANDONED");
+ PyErr_SetString(TimeoutAbandoned,
+ "WaitForSingleObject() returned WAIT_ABANDONED");
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ // WaitForSingleObject() returned WAIT_OBJECT_0. It means the
+ // process is gone so we can get its process exit code. The PID
+ // may still stick around though but we'll handle that from Python.
+ if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess");
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong((long) ExitCode);
+#else
+ return PyInt_FromLong((long) ExitCode);
+#endif
+}
+
+
+/*
+ * Return a Python tuple (user_time, kernel_time)
+ */
+PyObject *
+psutil_proc_times(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ FILETIME ftCreate, ftExit, ftKernel, ftUser;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+
+ if (hProcess == NULL)
+ return NULL;
+ if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
+ // usually means the process has died so we throw a NoSuchProcess
+ // here
+ NoSuchProcess("GetProcessTimes -> ERROR_ACCESS_DENIED");
+ }
+ else {
+ PyErr_SetFromWindowsErr(0);
+ }
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+
+ /*
+ * User and kernel times are represented as a FILETIME structure
+ * which contains a 64-bit value representing the number of
+ * 100-nanosecond intervals since January 1, 1601 (UTC):
+ * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
+ * To convert it into a float representing the seconds that the
+ * process has executed in user/kernel mode I borrowed the code
+ * below from Python's Modules/posixmodule.c
+ */
+ return Py_BuildValue(
+ "(ddd)",
+ (double)(ftUser.dwHighDateTime * HI_T + \
+ ftUser.dwLowDateTime * LO_T),
+ (double)(ftKernel.dwHighDateTime * HI_T + \
+ ftKernel.dwLowDateTime * LO_T),
+ psutil_FiletimeToUnixTime(ftCreate)
+ );
+}
+
+
+/*
+ * Return process executable path. Works for all processes regardless of
+ * privilege. NtQuerySystemInformation has some sort of internal cache,
+ * since it succeeds even when a process is gone (but not if a PID never
+ * existed).
+ */
+PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+ DWORD pid;
+ NTSTATUS status;
+ PVOID buffer = NULL;
+ ULONG bufferSize = 0x104 * 2; // WIN_MAX_PATH * sizeof(wchar_t)
+ SYSTEM_PROCESS_ID_INFORMATION processIdInfo;
+ PyObject *py_exe;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ if (pid == 0)
+ return AccessDenied("automatically set for PID 0");
+
+ // ...because NtQuerySystemInformation can succeed for terminated
+ // processes.
+ if (psutil_pid_is_running(pid) == 0)
+ return NoSuchProcess("psutil_pid_is_running -> 0");
+
+ buffer = MALLOC_ZERO(bufferSize);
+ if (! buffer) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ processIdInfo.ProcessId = (HANDLE)(ULONG_PTR)pid;
+ processIdInfo.ImageName.Length = 0;
+ processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize;
+ processIdInfo.ImageName.Buffer = buffer;
+
+ status = NtQuerySystemInformation(
+ SystemProcessIdInformation,
+ &processIdInfo,
+ sizeof(SYSTEM_PROCESS_ID_INFORMATION),
+ NULL);
+
+ if ((status == STATUS_INFO_LENGTH_MISMATCH) &&
+ (processIdInfo.ImageName.MaximumLength <= bufferSize))
+ {
+ // Required length was NOT stored in MaximumLength (WOW64 issue).
+ ULONG maxBufferSize = 0x7FFF * 2; // NTFS_MAX_PATH * sizeof(wchar_t)
+ do {
+ // Iteratively double the size of the buffer up to maxBufferSize
+ bufferSize *= 2;
+ FREE(buffer);
+ buffer = MALLOC_ZERO(bufferSize);
+ if (! buffer) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ processIdInfo.ImageName.MaximumLength = (USHORT)bufferSize;
+ processIdInfo.ImageName.Buffer = buffer;
+
+ status = NtQuerySystemInformation(
+ SystemProcessIdInformation,
+ &processIdInfo,
+ sizeof(SYSTEM_PROCESS_ID_INFORMATION),
+ NULL);
+ } while ((status == STATUS_INFO_LENGTH_MISMATCH) &&
+ (bufferSize <= maxBufferSize));
+ }
+ else if (status == STATUS_INFO_LENGTH_MISMATCH) {
+ // Required length is stored in MaximumLength.
+ FREE(buffer);
+ buffer = MALLOC_ZERO(processIdInfo.ImageName.MaximumLength);
+ if (! buffer) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ processIdInfo.ImageName.Buffer = buffer;
+
+ status = NtQuerySystemInformation(
+ SystemProcessIdInformation,
+ &processIdInfo,
+ sizeof(SYSTEM_PROCESS_ID_INFORMATION),
+ NULL);
+ }
+
+ if (! NT_SUCCESS(status)) {
+ FREE(buffer);
+ if (psutil_pid_is_running(pid) == 0)
+ NoSuchProcess("psutil_pid_is_running -> 0");
+ else
+ psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
+ return NULL;
+ }
+
+ if (processIdInfo.ImageName.Buffer == NULL) {
+ // Happens for PID 4.
+ py_exe = Py_BuildValue("s", "");
+ }
+ else {
+ py_exe = PyUnicode_FromWideChar(processIdInfo.ImageName.Buffer,
+ processIdInfo.ImageName.Length / 2);
+ }
+ FREE(buffer);
+ return py_exe;
+}
+
+
+/*
+ * Return process memory information as a Python tuple.
+ */
+PyObject *
+psutil_proc_memory_info(PyObject *self, PyObject *args) {
+ HANDLE hProcess;
+ DWORD pid;
+ PROCESS_MEMORY_COUNTERS_EX cnt;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (NULL == hProcess)
+ return NULL;
+
+ if (! GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&cnt,
+ sizeof(cnt))) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ CloseHandle(hProcess);
+
+ // PROCESS_MEMORY_COUNTERS values are defined as SIZE_T which on 64bits
+ // is an (unsigned long long) and on 32bits is an (unsigned int).
+ // "_WIN64" is defined if we're running a 64bit Python interpreter not
+ // exclusively if the *system* is 64bit.
+#if defined(_WIN64)
+ return Py_BuildValue(
+ "(kKKKKKKKKK)",
+ cnt.PageFaultCount, // unsigned long
+ (unsigned long long)cnt.PeakWorkingSetSize,
+ (unsigned long long)cnt.WorkingSetSize,
+ (unsigned long long)cnt.QuotaPeakPagedPoolUsage,
+ (unsigned long long)cnt.QuotaPagedPoolUsage,
+ (unsigned long long)cnt.QuotaPeakNonPagedPoolUsage,
+ (unsigned long long)cnt.QuotaNonPagedPoolUsage,
+ (unsigned long long)cnt.PagefileUsage,
+ (unsigned long long)cnt.PeakPagefileUsage,
+ (unsigned long long)cnt.PrivateUsage);
+#else
+ return Py_BuildValue(
+ "(kIIIIIIIII)",
+ cnt.PageFaultCount, // unsigned long
+ (unsigned int)cnt.PeakWorkingSetSize,
+ (unsigned int)cnt.WorkingSetSize,
+ (unsigned int)cnt.QuotaPeakPagedPoolUsage,
+ (unsigned int)cnt.QuotaPagedPoolUsage,
+ (unsigned int)cnt.QuotaPeakNonPagedPoolUsage,
+ (unsigned int)cnt.QuotaNonPagedPoolUsage,
+ (unsigned int)cnt.PagefileUsage,
+ (unsigned int)cnt.PeakPagefileUsage,
+ (unsigned int)cnt.PrivateUsage);
+#endif
+}
+
+
+static int
+psutil_GetProcWsetInformation(
+ DWORD pid,
+ HANDLE hProcess,
+ PMEMORY_WORKING_SET_INFORMATION *wSetInfo)
+{
+ NTSTATUS status;
+ PVOID buffer;
+ SIZE_T bufferSize;
+
+ bufferSize = 0x8000;
+ buffer = MALLOC_ZERO(bufferSize);
+ if (! buffer) {
+ PyErr_NoMemory();
+ return 1;
+ }
+
+ while ((status = NtQueryVirtualMemory(
+ hProcess,
+ NULL,
+ MemoryWorkingSetInformation,
+ buffer,
+ bufferSize,
+ NULL)) == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ FREE(buffer);
+ bufferSize *= 2;
+ // Fail if we're resizing the buffer to something very large.
+ if (bufferSize > 256 * 1024 * 1024) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NtQueryVirtualMemory bufsize is too large");
+ return 1;
+ }
+ buffer = MALLOC_ZERO(bufferSize);
+ if (! buffer) {
+ PyErr_NoMemory();
+ return 1;
+ }
+ }
+
+ if (!NT_SUCCESS(status)) {
+ if (status == STATUS_ACCESS_DENIED) {
+ AccessDenied("NtQueryVirtualMemory -> STATUS_ACCESS_DENIED");
+ }
+ else if (psutil_pid_is_running(pid) == 0) {
+ NoSuchProcess("psutil_pid_is_running -> 0");
+ }
+ else {
+ PyErr_Clear();
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryVirtualMemory(MemoryWorkingSetInformation)");
+ }
+ HeapFree(GetProcessHeap(), 0, buffer);
+ return 1;
+ }
+
+ *wSetInfo = (PMEMORY_WORKING_SET_INFORMATION)buffer;
+ return 0;
+}
+
+
+/*
+ * Returns the USS of the process.
+ * Reference:
+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
+ * nsMemoryReporterManager.cpp
+ */
+PyObject *
+psutil_proc_memory_uss(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ PSUTIL_PROCESS_WS_COUNTERS wsCounters;
+ PMEMORY_WORKING_SET_INFORMATION wsInfo;
+ ULONG_PTR i;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_INFORMATION);
+ if (hProcess == NULL)
+ return NULL;
+
+ if (psutil_GetProcWsetInformation(pid, hProcess, &wsInfo) != 0) {
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ memset(&wsCounters, 0, sizeof(PSUTIL_PROCESS_WS_COUNTERS));
+
+ for (i = 0; i < wsInfo->NumberOfEntries; i++) {
+ // This is what ProcessHacker does.
+ /*
+ wsCounters.NumberOfPages++;
+ if (wsInfo->WorkingSetInfo[i].ShareCount > 1)
+ wsCounters.NumberOfSharedPages++;
+ if (wsInfo->WorkingSetInfo[i].ShareCount == 0)
+ wsCounters.NumberOfPrivatePages++;
+ if (wsInfo->WorkingSetInfo[i].Shared)
+ wsCounters.NumberOfShareablePages++;
+ */
+
+ // This is what we do: count shared pages that only one process
+ // is using as private (USS).
+ if (!wsInfo->WorkingSetInfo[i].Shared ||
+ wsInfo->WorkingSetInfo[i].ShareCount <= 1) {
+ wsCounters.NumberOfPrivatePages++;
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, wsInfo);
+ CloseHandle(hProcess);
+
+ return Py_BuildValue("I", wsCounters.NumberOfPrivatePages);
+}
+
+
+/*
+ * Resume or suspends a process
+ */
+PyObject *
+psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) {
+ DWORD pid;
+ NTSTATUS status;
+ HANDLE hProcess;
+ DWORD access = PROCESS_SUSPEND_RESUME | PROCESS_QUERY_LIMITED_INFORMATION;
+ PyObject* suspend;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &suspend))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL)
+ return NULL;
+
+ if (PyObject_IsTrue(suspend))
+ status = NtSuspendProcess(hProcess);
+ else
+ status = NtResumeProcess(hProcess);
+
+ if (! NT_SUCCESS(status)) {
+ CloseHandle(hProcess);
+ return psutil_SetFromNTStatusErr(status, "NtSuspend|ResumeProcess");
+ }
+
+ CloseHandle(hProcess);
+ Py_RETURN_NONE;
+}
+
+
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ HANDLE hThread = NULL;
+ THREADENTRY32 te32 = {0};
+ DWORD pid;
+ int pid_return;
+ int rc;
+ FILETIME ftDummy, ftKernel, ftUser;
+ HANDLE hThreadSnap = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ goto error;
+ if (pid == 0) {
+ // raise AD instead of returning 0 as procexp is able to
+ // retrieve useful information somehow
+ AccessDenied("forced for PID 0");
+ goto error;
+ }
+
+ pid_return = psutil_pid_is_running(pid);
+ if (pid_return == 0) {
+ NoSuchProcess("psutil_pid_is_running -> 0");
+ goto error;
+ }
+ if (pid_return == -1)
+ goto error;
+
+ hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+ if (hThreadSnap == INVALID_HANDLE_VALUE) {
+ PyErr_SetFromOSErrnoWithSyscall("CreateToolhelp32Snapshot");
+ goto error;
+ }
+
+ // Fill in the size of the structure before using it
+ te32.dwSize = sizeof(THREADENTRY32);
+
+ if (! Thread32First(hThreadSnap, &te32)) {
+ PyErr_SetFromOSErrnoWithSyscall("Thread32First");
+ goto error;
+ }
+
+ // Walk the thread snapshot to find all threads of the process.
+ // If the thread belongs to the process, increase the counter.
+ do {
+ if (te32.th32OwnerProcessID == pid) {
+ py_tuple = NULL;
+ hThread = NULL;
+ hThread = OpenThread(THREAD_QUERY_INFORMATION,
+ FALSE, te32.th32ThreadID);
+ if (hThread == NULL) {
+ // thread has disappeared on us
+ continue;
+ }
+
+ rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel,
+ &ftUser);
+ if (rc == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("GetThreadTimes");
+ goto error;
+ }
+
+ /*
+ * User and kernel times are represented as a FILETIME structure
+ * which contains a 64-bit value representing the number of
+ * 100-nanosecond intervals since January 1, 1601 (UTC):
+ * http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
+ * To convert it into a float representing the seconds that the
+ * process has executed in user/kernel mode I borrowed the code
+ * below from Python's Modules/posixmodule.c
+ */
+ py_tuple = Py_BuildValue(
+ "kdd",
+ te32.th32ThreadID,
+ (double)(ftUser.dwHighDateTime * HI_T + \
+ ftUser.dwLowDateTime * LO_T),
+ (double)(ftKernel.dwHighDateTime * HI_T + \
+ ftKernel.dwLowDateTime * LO_T));
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+
+ CloseHandle(hThread);
+ }
+ } while (Thread32Next(hThreadSnap, &te32));
+
+ CloseHandle(hThreadSnap);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (hThread != NULL)
+ CloseHandle(hThread);
+ if (hThreadSnap != NULL)
+ CloseHandle(hThreadSnap);
+ return NULL;
+}
+
+
+PyObject *
+psutil_proc_open_files(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE processHandle;
+ DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ processHandle = psutil_handle_from_pid(pid, access);
+ if (processHandle == NULL)
+ return NULL;
+
+ py_retlist = psutil_get_open_files(pid, processHandle);
+ CloseHandle(processHandle);
+ return py_retlist;
+}
+
+
+static PTOKEN_USER
+_psutil_user_token_from_pid(DWORD pid) {
+ HANDLE hProcess = NULL;
+ HANDLE hToken = NULL;
+ PTOKEN_USER userToken = NULL;
+ ULONG bufferSize = 0x100;
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (hProcess == NULL)
+ return NULL;
+
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken");
+ goto error;
+ }
+
+ // Get the user SID.
+ while (1) {
+ userToken = malloc(bufferSize);
+ if (userToken == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ if (!GetTokenInformation(hToken, TokenUser, userToken, bufferSize,
+ &bufferSize))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ free(userToken);
+ continue;
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall("GetTokenInformation");
+ goto error;
+ }
+ }
+ break;
+ }
+
+ CloseHandle(hProcess);
+ CloseHandle(hToken);
+ return userToken;
+
+error:
+ if (hProcess != NULL)
+ CloseHandle(hProcess);
+ if (hToken != NULL)
+ CloseHandle(hToken);
+ return NULL;
+}
+
+
+/*
+ * Return process username as a "DOMAIN//USERNAME" string.
+ */
+PyObject *
+psutil_proc_username(PyObject *self, PyObject *args) {
+ DWORD pid;
+ PTOKEN_USER userToken = NULL;
+ WCHAR *userName = NULL;
+ WCHAR *domainName = NULL;
+ ULONG nameSize = 0x100;
+ ULONG domainNameSize = 0x100;
+ SID_NAME_USE nameUse;
+ PyObject *py_username = NULL;
+ PyObject *py_domain = NULL;
+ PyObject *py_tuple = NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ userToken = _psutil_user_token_from_pid(pid);
+ if (userToken == NULL)
+ return NULL;
+
+ // resolve the SID to a name
+ while (1) {
+ userName = malloc(nameSize * sizeof(WCHAR));
+ if (userName == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ domainName = malloc(domainNameSize * sizeof(WCHAR));
+ if (domainName == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ if (!LookupAccountSidW(NULL, userToken->User.Sid, userName, &nameSize,
+ domainName, &domainNameSize, &nameUse))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ free(userName);
+ free(domainName);
+ continue;
+ }
+ else if (GetLastError() == ERROR_NONE_MAPPED) {
+ // From MS doc:
+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/
+ // nf-winbase-lookupaccountsida
+ // If the function cannot find an account name for the SID,
+ // GetLastError returns ERROR_NONE_MAPPED. This can occur if
+ // a network time-out prevents the function from finding the
+ // name. It also occurs for SIDs that have no corresponding
+ // account name, such as a logon SID that identifies a logon
+ // session.
+ AccessDenied("LookupAccountSidW -> ERROR_NONE_MAPPED");
+ goto error;
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall("LookupAccountSidW");
+ goto error;
+ }
+ }
+ break;
+ }
+
+ py_domain = PyUnicode_FromWideChar(domainName, wcslen(domainName));
+ if (! py_domain)
+ goto error;
+ py_username = PyUnicode_FromWideChar(userName, wcslen(userName));
+ if (! py_username)
+ goto error;
+ py_tuple = Py_BuildValue("OO", py_domain, py_username);
+ if (! py_tuple)
+ goto error;
+ Py_DECREF(py_domain);
+ Py_DECREF(py_username);
+
+ free(userName);
+ free(domainName);
+ free(userToken);
+ return py_tuple;
+
+error:
+ if (userName != NULL)
+ free(userName);
+ if (domainName != NULL)
+ free(domainName);
+ if (userToken != NULL)
+ free(userToken);
+ Py_XDECREF(py_domain);
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tuple);
+ return NULL;
+}
+
+
+/*
+ * Get process priority as a Python integer.
+ */
+PyObject *
+psutil_proc_priority_get(PyObject *self, PyObject *args) {
+ DWORD pid;
+ DWORD priority;
+ HANDLE hProcess;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (hProcess == NULL)
+ return NULL;
+
+ priority = GetPriorityClass(hProcess);
+ if (priority == 0) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ CloseHandle(hProcess);
+ return Py_BuildValue("i", priority);
+}
+
+
+/*
+ * Set process priority.
+ */
+PyObject *
+psutil_proc_priority_set(PyObject *self, PyObject *args) {
+ DWORD pid;
+ int priority;
+ int retval;
+ HANDLE hProcess;
+ DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &priority))
+ return NULL;
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL)
+ return NULL;
+
+ retval = SetPriorityClass(hProcess, priority);
+ if (retval == 0) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Get process IO priority as a Python integer.
+ */
+PyObject *
+psutil_proc_io_priority_get(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ DWORD IoPriority;
+ NTSTATUS status;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (hProcess == NULL)
+ return NULL;
+
+ status = NtQueryInformationProcess(
+ hProcess,
+ ProcessIoPriority,
+ &IoPriority,
+ sizeof(DWORD),
+ NULL
+ );
+
+ CloseHandle(hProcess);
+ if (! NT_SUCCESS(status))
+ return psutil_SetFromNTStatusErr(status, "NtQueryInformationProcess");
+ return Py_BuildValue("i", IoPriority);
+}
+
+
+/*
+ * Set process IO priority.
+ */
+PyObject *
+psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
+ DWORD pid;
+ DWORD prio;
+ HANDLE hProcess;
+ NTSTATUS status;
+ DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &prio))
+ return NULL;
+
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL)
+ return NULL;
+
+ status = NtSetInformationProcess(
+ hProcess,
+ ProcessIoPriority,
+ (PVOID)&prio,
+ sizeof(DWORD)
+ );
+
+ CloseHandle(hProcess);
+ if (! NT_SUCCESS(status))
+ return psutil_SetFromNTStatusErr(status, "NtSetInformationProcess");
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Return a Python tuple referencing process I/O counters.
+ */
+PyObject *
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ IO_COUNTERS IoCounters;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (NULL == hProcess)
+ return NULL;
+
+ if (! GetProcessIoCounters(hProcess, &IoCounters)) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+ return Py_BuildValue("(KKKKKK)",
+ IoCounters.ReadOperationCount,
+ IoCounters.WriteOperationCount,
+ IoCounters.ReadTransferCount,
+ IoCounters.WriteTransferCount,
+ IoCounters.OtherOperationCount,
+ IoCounters.OtherTransferCount);
+}
+
+
+/*
+ * Return process CPU affinity as a bitmask
+ */
+PyObject *
+psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ DWORD_PTR proc_mask;
+ DWORD_PTR system_mask;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (hProcess == NULL) {
+ return NULL;
+ }
+ if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+#ifdef _WIN64
+ return Py_BuildValue("K", (unsigned long long)proc_mask);
+#else
+ return Py_BuildValue("k", (unsigned long)proc_mask);
+#endif
+}
+
+
+/*
+ * Set process CPU affinity
+ */
+PyObject *
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
+ DWORD_PTR mask;
+
+#ifdef _WIN64
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "K", &pid, &mask))
+#else
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "k", &pid, &mask))
+#endif
+ {
+ return NULL;
+ }
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL)
+ return NULL;
+
+ if (SetProcessAffinityMask(hProcess, mask) == 0) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+
+ CloseHandle(hProcess);
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Return True if all process threads are in waiting/suspended state.
+ */
+PyObject *
+psutil_proc_is_suspended(PyObject *self, PyObject *args) {
+ DWORD pid;
+ ULONG i;
+ PSYSTEM_PROCESS_INFORMATION process;
+ PVOID buffer;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (! psutil_get_proc_info(pid, &process, &buffer))
+ return NULL;
+ for (i = 0; i < process->NumberOfThreads; i++) {
+ if (process->Threads[i].ThreadState != Waiting ||
+ process->Threads[i].WaitReason != Suspended)
+ {
+ free(buffer);
+ Py_RETURN_FALSE;
+ }
+ }
+ free(buffer);
+ Py_RETURN_TRUE;
+}
+
+
+/*
+ * Return the number of handles opened by process.
+ */
+PyObject *
+psutil_proc_num_handles(PyObject *self, PyObject *args) {
+ DWORD pid;
+ HANDLE hProcess;
+ DWORD handleCount;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (NULL == hProcess)
+ return NULL;
+ if (! GetProcessHandleCount(hProcess, &handleCount)) {
+ PyErr_SetFromWindowsErr(0);
+ CloseHandle(hProcess);
+ return NULL;
+ }
+ CloseHandle(hProcess);
+ return Py_BuildValue("k", handleCount);
+}
+
+
+static char *get_region_protection_string(ULONG protection) {
+ switch (protection & 0xff) {
+ case PAGE_NOACCESS:
+ return "";
+ case PAGE_READONLY:
+ return "r";
+ case PAGE_READWRITE:
+ return "rw";
+ case PAGE_WRITECOPY:
+ return "wc";
+ case PAGE_EXECUTE:
+ return "x";
+ case PAGE_EXECUTE_READ:
+ return "xr";
+ case PAGE_EXECUTE_READWRITE:
+ return "xrw";
+ case PAGE_EXECUTE_WRITECOPY:
+ return "xwc";
+ default:
+ return "?";
+ }
+}
+
+
+/*
+ * Return a list of process's memory mappings.
+ */
+PyObject *
+psutil_proc_memory_maps(PyObject *self, PyObject *args) {
+ MEMORY_BASIC_INFORMATION basicInfo;
+ DWORD pid;
+ HANDLE hProcess = NULL;
+ PVOID baseAddress;
+ WCHAR mappedFileName[MAX_PATH];
+ LPVOID maxAddr;
+ // required by GetMappedFileNameW
+ DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_str = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ goto error;
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (NULL == hProcess)
+ goto error;
+
+ maxAddr = PSUTIL_SYSTEM_INFO.lpMaximumApplicationAddress;
+ baseAddress = NULL;
+
+ while (VirtualQueryEx(hProcess, baseAddress, &basicInfo,
+ sizeof(MEMORY_BASIC_INFORMATION)))
+ {
+ py_tuple = NULL;
+ if (baseAddress > maxAddr)
+ break;
+ if (GetMappedFileNameW(hProcess, baseAddress, mappedFileName,
+ sizeof(mappedFileName)))
+ {
+ py_str = PyUnicode_FromWideChar(mappedFileName,
+ wcslen(mappedFileName));
+ if (py_str == NULL)
+ goto error;
+#ifdef _WIN64
+ py_tuple = Py_BuildValue(
+ "(KsOI)",
+ (unsigned long long)baseAddress,
+#else
+ py_tuple = Py_BuildValue(
+ "(ksOI)",
+ (unsigned long)baseAddress,
+#endif
+ get_region_protection_string(basicInfo.Protect),
+ py_str,
+ basicInfo.RegionSize);
+
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_str);
+ }
+ baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize;
+ }
+
+ CloseHandle(hProcess);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_str);
+ Py_DECREF(py_retlist);
+ if (hProcess != NULL)
+ CloseHandle(hProcess);
+ return NULL;
+}
+
+
+/*
+ * Return a {pid:ppid, ...} dict for all running processes.
+ */
+PyObject *
+psutil_ppid_map(PyObject *self, PyObject *args) {
+ PyObject *py_pid = NULL;
+ PyObject *py_ppid = NULL;
+ PyObject *py_retdict = PyDict_New();
+ HANDLE handle = NULL;
+ PROCESSENTRY32 pe = {0};
+ pe.dwSize = sizeof(PROCESSENTRY32);
+
+ if (py_retdict == NULL)
+ return NULL;
+ handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (handle == INVALID_HANDLE_VALUE) {
+ PyErr_SetFromWindowsErr(0);
+ Py_DECREF(py_retdict);
+ return NULL;
+ }
+
+ if (Process32First(handle, &pe)) {
+ do {
+ py_pid = PyLong_FromPid(pe.th32ProcessID);
+ if (py_pid == NULL)
+ goto error;
+ py_ppid = PyLong_FromPid(pe.th32ParentProcessID);
+ if (py_ppid == NULL)
+ goto error;
+ if (PyDict_SetItem(py_retdict, py_pid, py_ppid))
+ goto error;
+ Py_CLEAR(py_pid);
+ Py_CLEAR(py_ppid);
+ } while (Process32Next(handle, &pe));
+ }
+
+ CloseHandle(handle);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_pid);
+ Py_XDECREF(py_ppid);
+ Py_DECREF(py_retdict);
+ CloseHandle(handle);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/proc.h b/contrib/python/psutil/py3/psutil/arch/windows/proc.h
new file mode 100644
index 0000000000..048d27dd7c
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+extern PyObject *TimeoutExpired;
+extern PyObject *TimeoutAbandoned;
+
+PyObject *psutil_pid_exists(PyObject *self, PyObject *args);
+PyObject *psutil_pids(PyObject *self, PyObject *args);
+PyObject *psutil_ppid_map(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args);
+PyObject *psutil_proc_exe(PyObject *self, PyObject *args);
+PyObject *psutil_proc_io_counters(PyObject *self, PyObject *args);
+PyObject *psutil_proc_io_priority_get(PyObject *self, PyObject *args);
+PyObject *psutil_proc_io_priority_set(PyObject *self, PyObject *args);
+PyObject *psutil_proc_is_suspended(PyObject *self, PyObject *args);
+PyObject *psutil_proc_kill(PyObject *self, PyObject *args);
+PyObject *psutil_proc_memory_info(PyObject *self, PyObject *args);
+PyObject *psutil_proc_memory_maps(PyObject *self, PyObject *args);
+PyObject *psutil_proc_memory_uss(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_handles(PyObject *self, PyObject *args);
+PyObject *psutil_proc_open_files(PyObject *self, PyObject *args);
+PyObject *psutil_proc_priority_get(PyObject *self, PyObject *args);
+PyObject *psutil_proc_priority_set(PyObject *self, PyObject *args);
+PyObject *psutil_proc_suspend_or_resume(PyObject *self, PyObject *args);
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
+PyObject *psutil_proc_times(PyObject *self, PyObject *args);
+PyObject *psutil_proc_username(PyObject *self, PyObject *args);
+PyObject *psutil_proc_wait(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.c b/contrib/python/psutil/py3/psutil/arch/windows/proc_handles.c
index 72e3f4d41e..30e7cd2d84 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_handles.c
@@ -23,7 +23,7 @@
#include <Python.h>
#include "../../_psutil_common.h"
-#include "process_utils.h"
+#include "proc_utils.h"
#define THREAD_TIMEOUT 100 // ms
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.h b/contrib/python/psutil/py3/psutil/arch/windows/proc_handles.h
index d1be3152d5..d1be3152d5 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_handles.h
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_info.c b/contrib/python/psutil/py3/psutil/arch/windows/proc_info.c
index d44c4eb75e..5d16b8133d 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_info.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_info.c
@@ -11,8 +11,8 @@
#include <windows.h>
#include "../../_psutil_common.h"
-#include "process_info.h"
-#include "process_utils.h"
+#include "proc_info.h"
+#include "proc_utils.h"
#ifndef _WIN64
@@ -24,6 +24,13 @@ typedef NTSTATUS (NTAPI *__NtQueryInformationProcess)(
PDWORD ReturnLength);
#endif
+#define PSUTIL_FIRST_PROCESS(Processes) ( \
+ (PSYSTEM_PROCESS_INFORMATION)(Processes))
+#define PSUTIL_NEXT_PROCESS(Process) ( \
+ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \
+ (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \
+ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : NULL)
+
/*
* Given a pointer into a process's memory, figure out how much
@@ -535,15 +542,38 @@ error:
* with given pid or NULL on error.
*/
PyObject *
-psutil_get_cmdline(DWORD pid, int use_peb) {
- PyObject *ret = NULL;
+psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict) {
WCHAR *data = NULL;
+ LPWSTR *szArglist = NULL;
SIZE_T size;
+ int nArgs;
+ int i;
+ int func_ret;
+ DWORD pid;
+ int pid_return;
+ int use_peb;
+ // TODO: shouldn't this be decref-ed in case of error on
+ // PyArg_ParseTuple?
+ PyObject *py_usepeb = Py_True;
PyObject *py_retlist = NULL;
PyObject *py_unicode = NULL;
- LPWSTR *szArglist = NULL;
- int nArgs, i;
- int func_ret;
+ static char *keywords[] = {"pid", "use_peb", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwdict, _Py_PARSE_PID "|O",
+ keywords, &pid, &py_usepeb))
+ {
+ return NULL;
+ }
+ if ((pid == 0) || (pid == 4))
+ return Py_BuildValue("[]");
+
+ pid_return = psutil_pid_is_running(pid);
+ if (pid_return == 0)
+ return NoSuchProcess("psutil_pid_is_running -> 0");
+ if (pid_return == -1)
+ return NULL;
+
+ use_peb = (py_usepeb == Py_True) ? 1 : 0;
/*
Reading the PEB to get the cmdline seem to be the best method if
@@ -559,47 +589,60 @@ psutil_get_cmdline(DWORD pid, int use_peb) {
else
func_ret = psutil_cmdline_query_proc(pid, &data, &size);
if (func_ret != 0)
- goto out;
+ goto error;
// attempt to parse the command line using Win32 API
szArglist = CommandLineToArgvW(data, &nArgs);
if (szArglist == NULL) {
PyErr_SetFromOSErrnoWithSyscall("CommandLineToArgvW");
- goto out;
+ goto error;
}
// arglist parsed as array of UNICODE_STRING, so convert each to
// Python string object and add to arg list
py_retlist = PyList_New(nArgs);
if (py_retlist == NULL)
- goto out;
+ goto error;
for (i = 0; i < nArgs; i++) {
py_unicode = PyUnicode_FromWideChar(szArglist[i],
wcslen(szArglist[i]));
if (py_unicode == NULL)
- goto out;
- PyList_SET_ITEM(py_retlist, i, py_unicode);
+ goto error;
+ PyList_SetItem(py_retlist, i, py_unicode);
py_unicode = NULL;
}
- ret = py_retlist;
- py_retlist = NULL;
-out:
+ LocalFree(szArglist);
+ free(data);
+ return py_retlist;
+
+error:
if (szArglist != NULL)
LocalFree(szArglist);
if (data != NULL)
free(data);
Py_XDECREF(py_unicode);
Py_XDECREF(py_retlist);
- return ret;
+ return NULL;
}
PyObject *
-psutil_get_cwd(DWORD pid) {
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+ DWORD pid;
PyObject *ret = NULL;
WCHAR *data = NULL;
SIZE_T size;
+ int pid_return;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+ pid_return = psutil_pid_is_running(pid);
+ if (pid_return == 0)
+ return NoSuchProcess("psutil_pid_is_running -> 0");
+ if (pid_return == -1)
+ return NULL;
if (psutil_get_process_data(pid, KIND_CWD, &data, &size) != 0)
goto out;
@@ -620,10 +663,23 @@ out:
* process with given pid or NULL on error.
*/
PyObject *
-psutil_get_environ(DWORD pid) {
- PyObject *ret = NULL;
+psutil_proc_environ(PyObject *self, PyObject *args) {
+ DWORD pid;
WCHAR *data = NULL;
SIZE_T size;
+ int pid_return;
+ PyObject *ret = NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if ((pid == 0) || (pid == 4))
+ return Py_BuildValue("s", "");
+
+ pid_return = psutil_pid_is_running(pid);
+ if (pid_return == 0)
+ return NoSuchProcess("psutil_pid_is_running -> 0");
+ if (pid_return == -1)
+ return NULL;
if (psutil_get_process_data(pid, KIND_ENVIRON, &data, &size) != 0)
goto out;
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_info.h b/contrib/python/psutil/py3/psutil/arch/windows/proc_info.h
index 5e89ddebdf..b7795451dc 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_info.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_info.h
@@ -5,6 +5,9 @@
*/
#include <Python.h>
+#include <windows.h>
+
+#include "ntextapi.h"
#define PSUTIL_FIRST_PROCESS(Processes) ( \
(PSYSTEM_PROCESS_INFORMATION)(Processes))
@@ -15,7 +18,7 @@
int psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
PVOID *retBuffer);
-PyObject* psutil_get_cmdline(DWORD pid, int use_peb);
-PyObject* psutil_get_cwd(DWORD pid);
-PyObject* psutil_get_environ(DWORD pid);
+PyObject* psutil_proc_cmdline(PyObject *self, PyObject *args, PyObject *kwdict);
+PyObject* psutil_proc_cwd(PyObject *self, PyObject *args);
+PyObject* psutil_proc_environ(PyObject *self, PyObject *args);
PyObject* psutil_proc_info(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_utils.c b/contrib/python/psutil/py3/psutil/arch/windows/proc_utils.c
index acbda301a4..dac1129c0f 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_utils.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_utils.c
@@ -11,7 +11,7 @@
#include <Psapi.h> // EnumProcesses
#include "../../_psutil_common.h"
-#include "process_utils.h"
+#include "proc_utils.h"
DWORD *
@@ -160,7 +160,7 @@ psutil_handle_from_pid(DWORD pid, DWORD access) {
}
-// Check for PID existance. Return 1 if pid exists, 0 if not, -1 on error.
+// Check for PID existence. Return 1 if pid exists, 0 if not, -1 on error.
int
psutil_pid_is_running(DWORD pid) {
HANDLE hProcess;
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_utils.h b/contrib/python/psutil/py3/psutil/arch/windows/proc_utils.h
index dca7c991a5..dca7c991a5 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/process_utils.h
+++ b/contrib/python/psutil/py3/psutil/arch/windows/proc_utils.h
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/sensors.c b/contrib/python/psutil/py3/psutil/arch/windows/sensors.c
new file mode 100644
index 0000000000..fbe2c2fe9f
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/sensors.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <windows.h>
+
+
+// Added in https://github.com/giampaolo/psutil/commit/109f873 in 2017.
+// Moved in here in 2023.
+PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+ SYSTEM_POWER_STATUS sps;
+
+ if (GetSystemPowerStatus(&sps) == 0) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+ return Py_BuildValue(
+ "iiiI",
+ sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown
+ // status flag:
+ // 1, 2, 4 = high, low, critical
+ // 8 = charging
+ // 128 = no battery
+ sps.BatteryFlag,
+ sps.BatteryLifePercent, // percent
+ sps.BatteryLifeTime // remaining secs
+ );
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/sensors.h b/contrib/python/psutil/py3/psutil/arch/windows/sensors.h
new file mode 100644
index 0000000000..edace25d3d
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/sensors.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_sensors_battery(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/services.c b/contrib/python/psutil/py3/psutil/arch/windows/services.c
index a91cb8f797..fa3e646e51 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/services.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/services.c
@@ -433,6 +433,7 @@ psutil_winservice_start(PyObject *self, PyObject *args) {
goto error;
}
+ CloseServiceHandle(hService);
Py_RETURN_NONE;
error:
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/socks.c b/contrib/python/psutil/py3/psutil/arch/windows/socks.c
index 5e4c2df802..0dc77f2d9d 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/socks.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/socks.c
@@ -12,7 +12,7 @@
#include <ws2tcpip.h>
#include "../../_psutil_common.h"
-#include "process_utils.h"
+#include "proc_utils.h"
#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff)
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/sys.c b/contrib/python/psutil/py3/psutil/arch/windows/sys.c
new file mode 100644
index 0000000000..3e12e71b72
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/sys.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+System related functions. Original code moved in here from
+psutil/_psutil_windows.c in 2023. For reference, here's the GIT blame
+history before the move:
+
+* boot_time(): https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_windows.c#L51-L60
+* users(): https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_windows.c#L1103-L1244
+*/
+
+#include <Python.h>
+#include <windows.h>
+
+#include "ntextapi.h"
+#include "../../_psutil_common.h"
+
+
+// Return a Python float representing the system uptime expressed in
+// seconds since the epoch.
+PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ ULONGLONG upTime;
+ FILETIME fileTime;
+
+ GetSystemTimeAsFileTime(&fileTime);
+ // Number of milliseconds that have elapsed since the system was started.
+ upTime = GetTickCount64() / 1000ull;
+ return Py_BuildValue("d", psutil_FiletimeToUnixTime(fileTime) - upTime);
+}
+
+
+PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
+ LPWSTR buffer_user = NULL;
+ LPWSTR buffer_addr = NULL;
+ LPWSTR buffer_info = NULL;
+ PWTS_SESSION_INFOW sessions = NULL;
+ DWORD count;
+ DWORD i;
+ DWORD sessionId;
+ DWORD bytes;
+ PWTS_CLIENT_ADDRESS address;
+ char address_str[50];
+ PWTSINFOW wts_info;
+ PyObject *py_tuple = NULL;
+ PyObject *py_address = NULL;
+ PyObject *py_username = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (WTSEnumerateSessionsW == NULL ||
+ WTSQuerySessionInformationW == NULL ||
+ WTSFreeMemory == NULL) {
+ // If we don't run in an environment that is a Remote Desktop Services environment
+ // the Wtsapi32 proc might not be present.
+ // https://docs.microsoft.com/en-us/windows/win32/termserv/run-time-linking-to-wtsapi32-dll
+ return py_retlist;
+ }
+
+ if (WTSEnumerateSessionsW(hServer, 0, 1, &sessions, &count) == 0) {
+ if (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) {
+ // On Windows Nano server, the Wtsapi32 API can be present, but return WinError 120.
+ return py_retlist;
+ }
+ PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessionsW");
+ goto error;
+ }
+
+ for (i = 0; i < count; i++) {
+ py_address = NULL;
+ py_tuple = NULL;
+ sessionId = sessions[i].SessionId;
+ if (buffer_user != NULL)
+ WTSFreeMemory(buffer_user);
+ if (buffer_addr != NULL)
+ WTSFreeMemory(buffer_addr);
+ if (buffer_info != NULL)
+ WTSFreeMemory(buffer_info);
+
+ buffer_user = NULL;
+ buffer_addr = NULL;
+ buffer_info = NULL;
+
+ // username
+ bytes = 0;
+ if (WTSQuerySessionInformationW(hServer, sessionId, WTSUserName,
+ &buffer_user, &bytes) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
+ goto error;
+ }
+ if (bytes <= 2)
+ continue;
+
+ // address
+ bytes = 0;
+ if (WTSQuerySessionInformationW(hServer, sessionId, WTSClientAddress,
+ &buffer_addr, &bytes) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
+ goto error;
+ }
+
+ address = (PWTS_CLIENT_ADDRESS)buffer_addr;
+ if (address->AddressFamily == 2) { // AF_INET == 2
+ sprintf_s(address_str,
+ _countof(address_str),
+ "%u.%u.%u.%u",
+ // The IP address is offset by two bytes from the start of the Address member of the WTS_CLIENT_ADDRESS structure.
+ address->Address[2],
+ address->Address[3],
+ address->Address[4],
+ address->Address[5]);
+ py_address = Py_BuildValue("s", address_str);
+ if (!py_address)
+ goto error;
+ }
+ else {
+ Py_INCREF(Py_None);
+ py_address = Py_None;
+ }
+
+ // login time
+ bytes = 0;
+ if (WTSQuerySessionInformationW(hServer, sessionId, WTSSessionInfo,
+ &buffer_info, &bytes) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW");
+ goto error;
+ }
+ wts_info = (PWTSINFOW)buffer_info;
+
+ py_username = PyUnicode_FromWideChar(buffer_user, wcslen(buffer_user));
+ if (py_username == NULL)
+ goto error;
+
+ py_tuple = Py_BuildValue(
+ "OOd",
+ py_username,
+ py_address,
+ psutil_LargeIntegerToUnixTime(wts_info->ConnectTime)
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_username);
+ Py_CLEAR(py_address);
+ Py_CLEAR(py_tuple);
+ }
+
+ WTSFreeMemory(sessions);
+ WTSFreeMemory(buffer_user);
+ WTSFreeMemory(buffer_addr);
+ WTSFreeMemory(buffer_info);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_address);
+ Py_DECREF(py_retlist);
+
+ if (sessions != NULL)
+ WTSFreeMemory(sessions);
+ if (buffer_user != NULL)
+ WTSFreeMemory(buffer_user);
+ if (buffer_addr != NULL)
+ WTSFreeMemory(buffer_addr);
+ if (buffer_info != NULL)
+ WTSFreeMemory(buffer_info);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/sys.h b/contrib/python/psutil/py3/psutil/arch/windows/sys.h
new file mode 100644
index 0000000000..344ca21d42
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/sys.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_boot_time(PyObject *self, PyObject *args);
+PyObject *psutil_users(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/wmi.c b/contrib/python/psutil/py3/psutil/arch/windows/wmi.c
index f9a847d3bf..fc7a66529e 100644
--- a/contrib/python/psutil/py3/psutil/arch/windows/wmi.c
+++ b/contrib/python/psutil/py3/psutil/arch/windows/wmi.c
@@ -64,22 +64,31 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) {
HANDLE event;
HANDLE waitHandle;
- if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS)
- goto error;
+ if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError, "PdhOpenQueryW failed");
+ return NULL;
+ }
s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
- if (s != ERROR_SUCCESS)
- goto error;
+ if (s != ERROR_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "PdhAddEnglishCounterW failed. Performance counters may be disabled."
+ );
+ return NULL;
+ }
event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent");
if (event == NULL) {
- PyErr_SetFromWindowsErr(0);
+ PyErr_SetFromOSErrnoWithSyscall("CreateEventW");
return NULL;
}
s = PdhCollectQueryDataEx(hQuery, SAMPLING_INTERVAL, event);
- if (s != ERROR_SUCCESS)
- goto error;
+ if (s != ERROR_SUCCESS) {
+ PyErr_Format(PyExc_RuntimeError, "PdhCollectQueryDataEx failed");
+ return NULL;
+ }
ret = RegisterWaitForSingleObject(
&waitHandle,
@@ -91,15 +100,11 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) {
WT_EXECUTEDEFAULT);
if (ret == 0) {
- PyErr_SetFromWindowsErr(0);
+ PyErr_SetFromOSErrnoWithSyscall("RegisterWaitForSingleObject");
return NULL;
}
Py_RETURN_NONE;
-
-error:
- PyErr_SetFromWindowsErr(0);
- return NULL;
}
diff --git a/contrib/python/psutil/py3/ya.make b/contrib/python/psutil/py3/ya.make
index 16c79983b7..5ae4714a6a 100644
--- a/contrib/python/psutil/py3/ya.make
+++ b/contrib/python/psutil/py3/ya.make
@@ -1,6 +1,6 @@
PY3_LIBRARY()
-VERSION(5.8.0)
+VERSION(5.9.8)
LICENSE(BSD-3-Clause)
@@ -24,7 +24,7 @@ NO_CHECK_IMPORTS(
NO_UTIL()
CFLAGS(
- -DPSUTIL_VERSION=580
+ -DPSUTIL_VERSION=598
)
SRCS(
@@ -40,6 +40,11 @@ IF (OS_LINUX)
SRCS(
psutil/_psutil_linux.c
psutil/_psutil_posix.c
+ psutil/arch/linux/disk.c
+ psutil/arch/linux/mem.c
+ psutil/arch/linux/net.c
+ psutil/arch/linux/proc.c
+ psutil/arch/linux/users.c
)
PY_REGISTER(
@@ -62,7 +67,13 @@ IF (OS_DARWIN)
SRCS(
psutil/_psutil_osx.c
psutil/_psutil_posix.c
- psutil/arch/osx/process_info.c
+ psutil/arch/osx/cpu.c
+ psutil/arch/osx/disk.c
+ psutil/arch/osx/mem.c
+ psutil/arch/osx/net.c
+ psutil/arch/osx/proc.c
+ psutil/arch/osx/sensors.c
+ psutil/arch/osx/sys.c
)
PY_REGISTER(
@@ -88,13 +99,17 @@ IF (OS_WINDOWS)
psutil/_psutil_windows.c
psutil/arch/windows/cpu.c
psutil/arch/windows/disk.c
+ psutil/arch/windows/mem.c
psutil/arch/windows/net.c
- psutil/arch/windows/process_handles.c
- psutil/arch/windows/process_info.c
- psutil/arch/windows/process_utils.c
+ psutil/arch/windows/proc.c
+ psutil/arch/windows/proc_handles.c
+ psutil/arch/windows/proc_info.c
+ psutil/arch/windows/proc_utils.c
psutil/arch/windows/security.c
+ psutil/arch/windows/sensors.c
psutil/arch/windows/services.c
psutil/arch/windows/socks.c
+ psutil/arch/windows/sys.c
psutil/arch/windows/wmi.c
)